add missing files from same resvision of browser tree

BUILD.gn files are edit with little-effort, so many of the added files are
not compiled in.

This helps with future uprev.

Bug: chromium:1048060
Test: Presubmit

Change-Id: I32b77ce2e40425a0a45c6672457ac736ca248ccd
(cherry picked from commit 991a472978a91cd467a6895bf47e1e6f8285b00a)
diff --git a/BUILD.IGNORE b/BUILD.IGNORE
new file mode 100644
index 0000000..d84edf6
--- /dev/null
+++ b/BUILD.IGNORE
@@ -0,0 +1,10 @@
+android
+components/policy
+fuzzer
+perftest
+power_monitor
+test_runner
+tests
+trace_event
+ui/gfx
+unittest
diff --git a/BUILD.gn b/BUILD.gn
index b956970..6687248 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -1,6 +1,9 @@
 # Copyright 2018 The Chromium OS Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
+#
+# BUILD.gn doesn't compile all files in the directory to reduce build size.
+# Missing files can be added if needed.
 
 import("//common-mk/mojom_bindings_generator.gni")
 import("//common-mk/pkg_config.gni")
diff --git a/base/android/android_hardware_buffer_abi.h b/base/android/android_hardware_buffer_abi.h
new file mode 100644
index 0000000..7012532
--- /dev/null
+++ b/base/android/android_hardware_buffer_abi.h
@@ -0,0 +1,90 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_ANDROID_ANDROID_HARDWARE_BUFFER_ABI_H_
+#define BASE_ANDROID_ANDROID_HARDWARE_BUFFER_ABI_H_
+
+// Minimal binary interface definitions for AHardwareBuffer based on
+// include/android/hardware_buffer.h from the Android NDK for platform level
+// 26+. This is only intended for use from the AndroidHardwareBufferCompat
+// wrapper for building without NDK platform level support, it is not a
+// general-use header and is not complete.
+//
+// TODO(crbug.com/771171): Delete this file when third_party/android_ndk/
+// is updated to a version that contains the android/hardware_buffer.h file.
+//
+// Please refer to the API documentation for details:
+// https://developer.android.com/ndk/reference/hardware__buffer_8h.html
+
+#include <stdint.h>
+
+// Use "C" linkage to match the original header file. This isn't strictly
+// required since the file is not declaring global functions, but the types
+// should remain in the global namespace for compatibility, and it's a reminder
+// that forward declarations elsewhere should use "extern "C" to avoid
+// namespace issues.
+extern "C" {
+
+typedef struct AHardwareBuffer AHardwareBuffer;
+typedef struct ARect ARect;
+
+enum {
+  AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM = 1,
+  AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM = 2,
+  AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM = 3,
+  AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM = 4,
+  AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT = 0x16,
+  AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM = 0x2b,
+  AHARDWAREBUFFER_FORMAT_BLOB = 0x21,
+};
+
+enum {
+  AHARDWAREBUFFER_USAGE_CPU_READ_NEVER = 0UL,
+  AHARDWAREBUFFER_USAGE_CPU_READ_RARELY = 2UL,
+  AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN = 3UL,
+  AHARDWAREBUFFER_USAGE_CPU_READ_MASK = 0xFUL,
+  AHARDWAREBUFFER_USAGE_CPU_WRITE_NEVER = 0UL << 4,
+  AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY = 2UL << 4,
+  AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN = 3UL << 4,
+  AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK = 0xFUL << 4,
+  AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE = 1UL << 8,
+  AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT = 1UL << 9,
+  AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT = 1UL << 14,
+  AHARDWAREBUFFER_USAGE_VIDEO_ENCODE = 1UL << 16,
+  AHARDWAREBUFFER_USAGE_SENSOR_DIRECT_DATA = 1UL << 23,
+  AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER = 1UL << 24,
+};
+
+typedef struct AHardwareBuffer_Desc {
+  uint32_t width;
+  uint32_t height;
+  uint32_t layers;
+  uint32_t format;
+  uint64_t usage;
+  uint32_t stride;
+  uint32_t rfu0;
+  uint64_t rfu1;
+} AHardwareBuffer_Desc;
+
+using PFAHardwareBuffer_allocate = void (*)(const AHardwareBuffer_Desc* desc,
+                                            AHardwareBuffer** outBuffer);
+using PFAHardwareBuffer_acquire = void (*)(AHardwareBuffer* buffer);
+using PFAHardwareBuffer_describe = void (*)(const AHardwareBuffer* buffer,
+                                            AHardwareBuffer_Desc* outDesc);
+using PFAHardwareBuffer_lock = int (*)(AHardwareBuffer* buffer,
+                                       uint64_t usage,
+                                       int32_t fence,
+                                       const ARect* rect,
+                                       void** outVirtualAddress);
+using PFAHardwareBuffer_recvHandleFromUnixSocket =
+    int (*)(int socketFd, AHardwareBuffer** outBuffer);
+using PFAHardwareBuffer_release = void (*)(AHardwareBuffer* buffer);
+using PFAHardwareBuffer_sendHandleToUnixSocket =
+    int (*)(const AHardwareBuffer* buffer, int socketFd);
+using PFAHardwareBuffer_unlock = int (*)(AHardwareBuffer* buffer,
+                                         int32_t* fence);
+
+}  // extern "C"
+
+#endif  // BASE_ANDROID_ANDROID_HARDWARE_BUFFER_ABI_H_
diff --git a/base/android/android_hardware_buffer_compat.cc b/base/android/android_hardware_buffer_compat.cc
new file mode 100644
index 0000000..70f0589
--- /dev/null
+++ b/base/android/android_hardware_buffer_compat.cc
@@ -0,0 +1,129 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/android/android_hardware_buffer_compat.h"
+
+#include "base/android/build_info.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+
+#include <dlfcn.h>
+
+namespace base {
+
+namespace {
+
+static base::LazyInstance<AndroidHardwareBufferCompat>::Leaky g_compat =
+    LAZY_INSTANCE_INITIALIZER;
+
+}  // namespace
+
+AndroidHardwareBufferCompat::AndroidHardwareBufferCompat() {
+  DCHECK(IsSupportAvailable());
+
+  // TODO(klausw): If the Chromium build requires __ANDROID_API__ >= 26 at some
+  // point in the future, we could directly use the global functions instead of
+  // dynamic loading. However, since this would be incompatible with pre-Oreo
+  // devices, this is unlikely to happen in the foreseeable future, so just
+  // unconditionally use dynamic loading.
+
+  // cf. base/android/linker/modern_linker_jni.cc
+  void* main_dl_handle = dlopen(nullptr, RTLD_NOW);
+
+  *reinterpret_cast<void**>(&allocate_) =
+      dlsym(main_dl_handle, "AHardwareBuffer_allocate");
+  DCHECK(allocate_);
+
+  *reinterpret_cast<void**>(&acquire_) =
+      dlsym(main_dl_handle, "AHardwareBuffer_acquire");
+  DCHECK(acquire_);
+
+  *reinterpret_cast<void**>(&describe_) =
+      dlsym(main_dl_handle, "AHardwareBuffer_describe");
+  DCHECK(describe_);
+
+  *reinterpret_cast<void**>(&lock_) =
+      dlsym(main_dl_handle, "AHardwareBuffer_lock");
+  DCHECK(lock_);
+
+  *reinterpret_cast<void**>(&recv_handle_) =
+      dlsym(main_dl_handle, "AHardwareBuffer_recvHandleFromUnixSocket");
+  DCHECK(recv_handle_);
+
+  *reinterpret_cast<void**>(&release_) =
+      dlsym(main_dl_handle, "AHardwareBuffer_release");
+  DCHECK(release_);
+
+  *reinterpret_cast<void**>(&send_handle_) =
+      dlsym(main_dl_handle, "AHardwareBuffer_sendHandleToUnixSocket");
+  DCHECK(send_handle_);
+
+  *reinterpret_cast<void**>(&unlock_) =
+      dlsym(main_dl_handle, "AHardwareBuffer_unlock");
+  DCHECK(unlock_);
+}
+
+// static
+bool AndroidHardwareBufferCompat::IsSupportAvailable() {
+  return base::android::BuildInfo::GetInstance()->sdk_int() >=
+         base::android::SDK_VERSION_OREO;
+}
+
+// static
+AndroidHardwareBufferCompat AndroidHardwareBufferCompat::GetInstance() {
+  return g_compat.Get();
+}
+
+void AndroidHardwareBufferCompat::Allocate(const AHardwareBuffer_Desc* desc,
+                                           AHardwareBuffer** out_buffer) {
+  DCHECK(IsSupportAvailable());
+  allocate_(desc, out_buffer);
+}
+
+void AndroidHardwareBufferCompat::Acquire(AHardwareBuffer* buffer) {
+  DCHECK(IsSupportAvailable());
+  acquire_(buffer);
+}
+
+void AndroidHardwareBufferCompat::Describe(const AHardwareBuffer* buffer,
+                                           AHardwareBuffer_Desc* out_desc) {
+  DCHECK(IsSupportAvailable());
+  describe_(buffer, out_desc);
+}
+
+int AndroidHardwareBufferCompat::Lock(AHardwareBuffer* buffer,
+                                      uint64_t usage,
+                                      int32_t fence,
+                                      const ARect* rect,
+                                      void** out_virtual_address) {
+  DCHECK(IsSupportAvailable());
+  return lock_(buffer, usage, fence, rect, out_virtual_address);
+}
+
+int AndroidHardwareBufferCompat::RecvHandleFromUnixSocket(
+    int socket_fd,
+    AHardwareBuffer** out_buffer) {
+  DCHECK(IsSupportAvailable());
+  return recv_handle_(socket_fd, out_buffer);
+}
+
+void AndroidHardwareBufferCompat::Release(AHardwareBuffer* buffer) {
+  DCHECK(IsSupportAvailable());
+  release_(buffer);
+}
+
+int AndroidHardwareBufferCompat::SendHandleToUnixSocket(
+    const AHardwareBuffer* buffer,
+    int socket_fd) {
+  DCHECK(IsSupportAvailable());
+  return send_handle_(buffer, socket_fd);
+}
+
+int AndroidHardwareBufferCompat::Unlock(AHardwareBuffer* buffer,
+                                        int32_t* fence) {
+  DCHECK(IsSupportAvailable());
+  return unlock_(buffer, fence);
+}
+
+}  // namespace base
diff --git a/base/android/android_hardware_buffer_compat.h b/base/android/android_hardware_buffer_compat.h
new file mode 100644
index 0000000..14be3d5
--- /dev/null
+++ b/base/android/android_hardware_buffer_compat.h
@@ -0,0 +1,51 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_ANDROID_ANDROID_HARDWARE_BUFFER_COMPAT_H_
+#define BASE_ANDROID_ANDROID_HARDWARE_BUFFER_COMPAT_H_
+
+#include "base/android/android_hardware_buffer_abi.h"
+#include "base/base_export.h"
+#include "base/lazy_instance.h"
+
+namespace base {
+
+// This class provides runtime support for working with AHardwareBuffer objects
+// on Android O systems without requiring building for the Android O NDK level.
+// Don't call GetInstance() unless IsSupportAvailable() returns true.
+class BASE_EXPORT AndroidHardwareBufferCompat {
+ public:
+  static bool IsSupportAvailable();
+  static AndroidHardwareBufferCompat GetInstance();
+
+  void Allocate(const AHardwareBuffer_Desc* desc, AHardwareBuffer** outBuffer);
+  void Acquire(AHardwareBuffer* buffer);
+  void Describe(const AHardwareBuffer* buffer, AHardwareBuffer_Desc* outDesc);
+  int Lock(AHardwareBuffer* buffer,
+           uint64_t usage,
+           int32_t fence,
+           const ARect* rect,
+           void** out_virtual_address);
+  int RecvHandleFromUnixSocket(int socketFd, AHardwareBuffer** outBuffer);
+  void Release(AHardwareBuffer* buffer);
+  int SendHandleToUnixSocket(const AHardwareBuffer* buffer, int socketFd);
+  int Unlock(AHardwareBuffer* buffer, int32_t* fence);
+
+ private:
+  friend struct base::LazyInstanceTraitsBase<AndroidHardwareBufferCompat>;
+  AndroidHardwareBufferCompat();
+
+  PFAHardwareBuffer_allocate allocate_;
+  PFAHardwareBuffer_acquire acquire_;
+  PFAHardwareBuffer_describe describe_;
+  PFAHardwareBuffer_lock lock_;
+  PFAHardwareBuffer_recvHandleFromUnixSocket recv_handle_;
+  PFAHardwareBuffer_release release_;
+  PFAHardwareBuffer_sendHandleToUnixSocket send_handle_;
+  PFAHardwareBuffer_unlock unlock_;
+};
+
+}  // namespace base
+
+#endif  // BASE_ANDROID_ANDROID_HARDWARE_BUFFER_COMPAT_H_
diff --git a/base/android/android_image_reader_abi.h b/base/android/android_image_reader_abi.h
new file mode 100644
index 0000000..c81087f
--- /dev/null
+++ b/base/android/android_image_reader_abi.h
@@ -0,0 +1,97 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_ANDROID_ANDROID_IMAGE_READER_ABI_H_
+#define BASE_ANDROID_ANDROID_IMAGE_READER_ABI_H_
+
+// Minimal binary interface definitions for AImage,AImageReader
+// and ANativeWindow based on include/media/NdkImage.h,
+// include/media/NdkImageReader.h and include/android/native_window_jni.h
+// from the Android NDK for platform level 26+. This is only
+// intended for use from the AndroidImageReader wrapper for building
+// without NDK platform level support, it is not a general-use header
+// and is not complete. Only the functions/data types which
+// are currently needed by media/gpu/android/image_reader_gl_owner.h are
+// included in this ABI
+//
+// Please refer to the API documentation for details:
+// https://developer.android.com/ndk/reference/group/media (AIMage and
+// AImageReader)
+// https://developer.android.com/ndk/reference/group/native-activity
+// (ANativeWindow)
+
+#include <android/native_window.h>
+#include <media/NdkMediaError.h>
+
+#include <jni.h>
+#include <stdint.h>
+
+// Use "C" linkage to match the original header file. This isn't strictly
+// required since the file is not declaring global functions, but the types
+// should remain in the global namespace for compatibility, and it's a reminder
+// that forward declarations elsewhere should use "extern "C" to avoid
+// namespace issues.
+extern "C" {
+
+// For AImage
+typedef struct AHardwareBuffer AHardwareBuffer;
+
+typedef struct AImage AImage;
+
+enum AIMAGE_FORMATS {
+  AIMAGE_FORMAT_YUV_420_888 = 0x23,
+  IMAGE_FORMAT_PRIVATE = 0x22
+};
+
+using pAImage_delete = void (*)(AImage* image);
+
+using pAImage_deleteAsync = void (*)(AImage* image, int releaseFenceFd);
+
+using pAImage_getHardwareBuffer = media_status_t (*)(const AImage* image,
+                                                     AHardwareBuffer** buffer);
+
+using pAImage_getWidth = media_status_t (*)(const AImage* image,
+                                            int32_t* width);
+
+using pAImage_getHeight = media_status_t (*)(const AImage* image,
+                                             int32_t* height);
+
+// For AImageReader
+
+typedef struct AImageReader AImageReader;
+
+typedef void (*AImageReader_ImageCallback)(void* context, AImageReader* reader);
+
+typedef struct AImageReader_ImageListener {
+  void* context;
+  AImageReader_ImageCallback onImageAvailable;
+} AImageReader_ImageListener;
+
+using pAImageReader_new = media_status_t (*)(int32_t width,
+                                             int32_t height,
+                                             int32_t format,
+                                             int32_t maxImages,
+                                             AImageReader** reader);
+
+using pAImageReader_setImageListener =
+    media_status_t (*)(AImageReader* reader,
+                       AImageReader_ImageListener* listener);
+
+using pAImageReader_delete = void (*)(AImageReader* reader);
+
+using pAImageReader_getWindow = media_status_t (*)(AImageReader* reader,
+                                                   ANativeWindow** window);
+
+using pAImageReader_acquireLatestImageAsync =
+    media_status_t (*)(AImageReader* reader,
+                       AImage** image,
+                       int* acquireFenceFd);
+
+// For ANativeWindow
+using pANativeWindow_toSurface = jobject (*)(JNIEnv* env,
+                                             ANativeWindow* window);
+
+}  // extern "C"
+
+#endif  // BASE_ANDROID_ANDROID_IMAGE_READER_ABI_H_
diff --git a/base/android/android_image_reader_compat.cc b/base/android/android_image_reader_compat.cc
new file mode 100644
index 0000000..0b08c17
--- /dev/null
+++ b/base/android/android_image_reader_compat.cc
@@ -0,0 +1,142 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/android/android_image_reader_compat.h"
+
+#include <dlfcn.h>
+
+#include "base/android/build_info.h"
+#include "base/feature_list.h"
+#include "base/logging.h"
+
+#define LOAD_FUNCTION(lib, func)                            \
+  do {                                                      \
+    func##_ = reinterpret_cast<p##func>(dlsym(lib, #func)); \
+    if (!func##_) {                                         \
+      DLOG(ERROR) << "Unable to load function " << #func;   \
+      return false;                                         \
+    }                                                       \
+  } while (0)
+
+namespace base {
+namespace android {
+
+AndroidImageReader& AndroidImageReader::GetInstance() {
+  // C++11 static local variable initialization is
+  // thread-safe.
+  static base::NoDestructor<AndroidImageReader> instance;
+  return *instance;
+}
+
+bool AndroidImageReader::IsSupported() {
+  return is_supported_;
+}
+
+AndroidImageReader::AndroidImageReader() {
+  is_supported_ = LoadFunctions();
+}
+
+bool AndroidImageReader::LoadFunctions() {
+  // If the Chromium build requires __ANDROID_API__ >= 26 at some
+  // point in the future, we could directly use the global functions instead of
+  // dynamic loading. However, since this would be incompatible with pre-Oreo
+  // devices, this is unlikely to happen in the foreseeable future, so we use
+  // dynamic loading.
+
+  // Functions are not present for android version older than OREO
+  if (base::android::BuildInfo::GetInstance()->sdk_int() <
+      base::android::SDK_VERSION_OREO) {
+    return false;
+  }
+
+  void* libmediandk = dlopen("libmediandk.so", RTLD_NOW);
+  if (libmediandk == nullptr) {
+    LOG(ERROR) << "Couldnt open libmediandk.so";
+    return false;
+  }
+
+  LOAD_FUNCTION(libmediandk, AImage_delete);
+  LOAD_FUNCTION(libmediandk, AImage_deleteAsync);
+  LOAD_FUNCTION(libmediandk, AImage_getHardwareBuffer);
+  LOAD_FUNCTION(libmediandk, AImage_getWidth);
+  LOAD_FUNCTION(libmediandk, AImage_getHeight);
+  LOAD_FUNCTION(libmediandk, AImageReader_new);
+  LOAD_FUNCTION(libmediandk, AImageReader_setImageListener);
+  LOAD_FUNCTION(libmediandk, AImageReader_delete);
+  LOAD_FUNCTION(libmediandk, AImageReader_getWindow);
+  LOAD_FUNCTION(libmediandk, AImageReader_acquireLatestImageAsync);
+
+  void* libandroid = dlopen("libandroid.so", RTLD_NOW);
+  if (libandroid == nullptr) {
+    LOG(ERROR) << "Couldnt open libandroid.so";
+    return false;
+  }
+
+  LOAD_FUNCTION(libandroid, ANativeWindow_toSurface);
+
+  return true;
+}
+
+void AndroidImageReader::AImage_delete(AImage* image) {
+  AImage_delete_(image);
+}
+
+void AndroidImageReader::AImage_deleteAsync(AImage* image, int releaseFenceFd) {
+  AImage_deleteAsync_(image, releaseFenceFd);
+}
+
+media_status_t AndroidImageReader::AImage_getHardwareBuffer(
+    const AImage* image,
+    AHardwareBuffer** buffer) {
+  return AImage_getHardwareBuffer_(image, buffer);
+}
+
+media_status_t AndroidImageReader::AImage_getWidth(const AImage* image,
+                                                   int32_t* width) {
+  return AImage_getWidth_(image, width);
+}
+
+media_status_t AndroidImageReader::AImage_getHeight(const AImage* image,
+                                                    int32_t* height) {
+  return AImage_getHeight_(image, height);
+}
+
+media_status_t AndroidImageReader::AImageReader_new(int32_t width,
+                                                    int32_t height,
+                                                    int32_t format,
+                                                    int32_t maxImages,
+                                                    AImageReader** reader) {
+  return AImageReader_new_(width, height, format, maxImages, reader);
+}
+
+media_status_t AndroidImageReader::AImageReader_setImageListener(
+    AImageReader* reader,
+    AImageReader_ImageListener* listener) {
+  return AImageReader_setImageListener_(reader, listener);
+}
+
+void AndroidImageReader::AImageReader_delete(AImageReader* reader) {
+  AImageReader_delete_(reader);
+}
+
+media_status_t AndroidImageReader::AImageReader_getWindow(
+    AImageReader* reader,
+    ANativeWindow** window) {
+  return AImageReader_getWindow_(reader, window);
+}
+
+media_status_t AndroidImageReader::AImageReader_acquireLatestImageAsync(
+    AImageReader* reader,
+    AImage** image,
+    int* acquireFenceFd) {
+  return AImageReader_acquireLatestImageAsync_(reader, image, acquireFenceFd);
+}
+
+jobject AndroidImageReader::ANativeWindow_toSurface(JNIEnv* env,
+                                                    ANativeWindow* window) {
+  return ANativeWindow_toSurface_(env, window);
+}
+
+}  // namespace android
+}  // namespace base
diff --git a/base/android/android_image_reader_compat.h b/base/android/android_image_reader_compat.h
new file mode 100644
index 0000000..5d5d881
--- /dev/null
+++ b/base/android/android_image_reader_compat.h
@@ -0,0 +1,79 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_ANDROID_ANDROID_IMAGE_READER_COMPAT_H_
+#define BASE_ANDROID_ANDROID_IMAGE_READER_COMPAT_H_
+
+#include "base/android/android_image_reader_abi.h"
+#include "base/base_export.h"
+#include "base/macros.h"
+#include "base/no_destructor.h"
+
+namespace base {
+namespace android {
+
+// This class provides runtime support for working with AImage, AImageReader and
+// ANativeWindow objects on Android O systems without requiring building for the
+// Android O NDK level. Don't call GetInstance() unless IsSupported() returns
+// true.
+class BASE_EXPORT AndroidImageReader {
+ public:
+  // Thread safe GetInstance.
+  static AndroidImageReader& GetInstance();
+
+  // Check if the image reader usage is supported. This function returns TRUE
+  // if android version is >=OREO, the media flag is enabled and all the
+  // required functions are loaded.
+  bool IsSupported();
+
+  // Naming convention of all the below functions are chosen to exactly match
+  // the function names in the NDK.
+  void AImage_delete(AImage* image);
+  void AImage_deleteAsync(AImage* image, int releaseFenceFd);
+  media_status_t AImage_getHardwareBuffer(const AImage* image,
+                                          AHardwareBuffer** buffer);
+  media_status_t AImage_getWidth(const AImage* image, int32_t* width);
+  media_status_t AImage_getHeight(const AImage* image, int32_t* height);
+  media_status_t AImageReader_new(int32_t width,
+                                  int32_t height,
+                                  int32_t format,
+                                  int32_t maxImages,
+                                  AImageReader** reader);
+  media_status_t AImageReader_setImageListener(
+      AImageReader* reader,
+      AImageReader_ImageListener* listener);
+  void AImageReader_delete(AImageReader* reader);
+  media_status_t AImageReader_getWindow(AImageReader* reader,
+                                        ANativeWindow** window);
+  media_status_t AImageReader_acquireLatestImageAsync(AImageReader* reader,
+                                                      AImage** image,
+                                                      int* acquireFenceFd);
+  jobject ANativeWindow_toSurface(JNIEnv* env, ANativeWindow* window);
+
+ private:
+  friend class base::NoDestructor<AndroidImageReader>;
+
+  AndroidImageReader();
+  bool LoadFunctions();
+
+  bool is_supported_;
+  pAImage_delete AImage_delete_;
+  pAImage_deleteAsync AImage_deleteAsync_;
+  pAImage_getHardwareBuffer AImage_getHardwareBuffer_;
+  pAImage_getWidth AImage_getWidth_;
+  pAImage_getHeight AImage_getHeight_;
+  pAImageReader_new AImageReader_new_;
+  pAImageReader_setImageListener AImageReader_setImageListener_;
+  pAImageReader_delete AImageReader_delete_;
+  pAImageReader_getWindow AImageReader_getWindow_;
+  pAImageReader_acquireLatestImageAsync AImageReader_acquireLatestImageAsync_;
+  pANativeWindow_toSurface ANativeWindow_toSurface_;
+
+  DISALLOW_COPY_AND_ASSIGN(AndroidImageReader);
+};
+
+}  // namespace android
+}  // namespace base
+
+#endif  // BASE_ANDROID_ANDROID_IMAGE_READER_COMPAT_H_
diff --git a/base/android/android_image_reader_compat_unittest.cc b/base/android/android_image_reader_compat_unittest.cc
new file mode 100644
index 0000000..756ec9f
--- /dev/null
+++ b/base/android/android_image_reader_compat_unittest.cc
@@ -0,0 +1,43 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/android/android_image_reader_compat.h"
+
+#include <stdint.h>
+#include <memory>
+
+#include "base/android/build_info.h"
+#include "base/test/scoped_feature_list.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace android {
+
+class AndroidImageReaderTest : public testing::Test {
+ public:
+  AndroidImageReaderTest() = default;
+  ~AndroidImageReaderTest() override = default;
+};
+
+// Getting instance of AndroidImageReader will invoke AndroidImageReader
+// constructor which will dlopen the mediandk and androidndk .so files and do
+// all the required symbol lookups.
+TEST_F(AndroidImageReaderTest, GetImageReaderInstance) {
+  // It is expected that image reader support will be available from android
+  // version OREO.
+  EXPECT_EQ(AndroidImageReader::GetInstance().IsSupported(),
+            base::android::BuildInfo::GetInstance()->sdk_int() >=
+                base::android::SDK_VERSION_OREO);
+}
+
+// There should be only 1 instance of AndroidImageReader im memory. Hence 2
+// instances should have same memory address.
+TEST_F(AndroidImageReaderTest, CompareImageReaderInstance) {
+  AndroidImageReader& a1 = AndroidImageReader::GetInstance();
+  AndroidImageReader& a2 = AndroidImageReader::GetInstance();
+  ASSERT_EQ(&a1, &a2);
+}
+
+}  // namespace android
+}  // namespace base
diff --git a/base/android/animation_frame_time_histogram.cc b/base/android/animation_frame_time_histogram.cc
new file mode 100644
index 0000000..23dffd8
--- /dev/null
+++ b/base/android/animation_frame_time_histogram.cc
@@ -0,0 +1,26 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/android/jni_string.h"
+#include "base/metrics/histogram_macros.h"
+#include "jni/AnimationFrameTimeHistogram_jni.h"
+
+using base::android::JavaParamRef;
+
+// static
+void JNI_AnimationFrameTimeHistogram_SaveHistogram(
+    JNIEnv* env,
+    const JavaParamRef<jobject>& jcaller,
+    const JavaParamRef<jstring>& j_histogram_name,
+    const JavaParamRef<jlongArray>& j_frame_times_ms,
+    jint j_count) {
+  jlong *frame_times_ms = env->GetLongArrayElements(j_frame_times_ms, NULL);
+  std::string histogram_name = base::android::ConvertJavaStringToUTF8(
+      env, j_histogram_name);
+
+  for (int i = 0; i < j_count; ++i) {
+    UMA_HISTOGRAM_TIMES(histogram_name.c_str(),
+                        base::TimeDelta::FromMilliseconds(frame_times_ms[i]));
+  }
+}
diff --git a/base/android/apk_assets.cc b/base/android/apk_assets.cc
new file mode 100644
index 0000000..de468b4
--- /dev/null
+++ b/base/android/apk_assets.cc
@@ -0,0 +1,47 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <jni.h>
+
+#include "base/android/apk_assets.h"
+
+#include "base/android/jni_array.h"
+#include "base/android/jni_string.h"
+#include "base/android/scoped_java_ref.h"
+#include "base/file_descriptor_store.h"
+#include "jni/ApkAssets_jni.h"
+
+namespace base {
+namespace android {
+
+int OpenApkAsset(const std::string& file_path,
+                 base::MemoryMappedFile::Region* region) {
+  // The AssetManager API of the NDK does not expose a method for accessing raw
+  // resources :(
+  JNIEnv* env = base::android::AttachCurrentThread();
+  ScopedJavaLocalRef<jlongArray> jarr = Java_ApkAssets_open(
+      env, base::android::ConvertUTF8ToJavaString(env, file_path));
+  std::vector<jlong> results;
+  base::android::JavaLongArrayToLongVector(env, jarr.obj(), &results);
+  CHECK_EQ(3U, results.size());
+  int fd = static_cast<int>(results[0]);
+  region->offset = results[1];
+  region->size = results[2];
+  return fd;
+}
+
+bool RegisterApkAssetWithFileDescriptorStore(const std::string& key,
+                                             const base::FilePath& file_path) {
+  base::MemoryMappedFile::Region region =
+      base::MemoryMappedFile::Region::kWholeFile;
+  int asset_fd = OpenApkAsset(file_path.value(), &region);
+  if (asset_fd == -1)
+    return false;
+  base::FileDescriptorStore::GetInstance().Set(key, base::ScopedFD(asset_fd),
+                                               region);
+  return true;
+}
+
+}  // namespace android
+}  // namespace base
diff --git a/base/android/apk_assets.h b/base/android/apk_assets.h
new file mode 100644
index 0000000..cdac000
--- /dev/null
+++ b/base/android/apk_assets.h
@@ -0,0 +1,39 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_ANDROID_APK_ASSETS_H_
+#define BASE_ANDROID_APK_ASSETS_H_
+
+#include <string>
+
+#include "base/android/jni_android.h"
+#include "base/files/file_path.h"
+#include "base/files/memory_mapped_file.h"
+
+namespace base {
+namespace android {
+
+// Opens an asset (e.g. a .pak file) from the apk.
+// Can be used from renderer process.
+// Fails if the asset is not stored uncompressed within the .apk.
+// Returns: The File Descriptor of the asset, or -1 upon failure.
+// Input arguments:
+// - |file_path|: Path to file within .apk. e.g.: assets/foo.pak
+// Output arguments:
+// - |region|: size & offset (in bytes) within the .apk of the asset.
+BASE_EXPORT int OpenApkAsset(
+    const std::string& file_path,
+    base::MemoryMappedFile::Region* region);
+
+// Registers an uncompressed asset from within the apk in the
+// FileDescriptorStore.
+// Returns: true in case of success, false otherwise.
+BASE_EXPORT bool RegisterApkAssetWithFileDescriptorStore(
+    const std::string& key,
+    const base::FilePath& file_path);
+
+}  // namespace android
+}  // namespace base
+
+#endif  // BASE_ANDROID_APK_ASSETS_H_
diff --git a/base/android/application_status_listener.cc b/base/android/application_status_listener.cc
new file mode 100644
index 0000000..c8c2cc6
--- /dev/null
+++ b/base/android/application_status_listener.cc
@@ -0,0 +1,78 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/android/application_status_listener.h"
+
+#include <jni.h>
+
+#include "base/lazy_instance.h"
+#include "base/observer_list_threadsafe.h"
+#include "jni/ApplicationStatus_jni.h"
+
+namespace base {
+namespace android {
+
+namespace {
+
+struct LeakyLazyObserverListTraits :
+    base::internal::LeakyLazyInstanceTraits<
+        ObserverListThreadSafe<ApplicationStatusListener> > {
+  static ObserverListThreadSafe<ApplicationStatusListener>*
+      New(void* instance) {
+    ObserverListThreadSafe<ApplicationStatusListener>* ret =
+        base::internal::LeakyLazyInstanceTraits<ObserverListThreadSafe<
+            ApplicationStatusListener>>::New(instance);
+    // Leaky.
+    ret->AddRef();
+    return ret;
+  }
+};
+
+LazyInstance<ObserverListThreadSafe<ApplicationStatusListener>,
+             LeakyLazyObserverListTraits> g_observers =
+    LAZY_INSTANCE_INITIALIZER;
+
+}  // namespace
+
+ApplicationStatusListener::ApplicationStatusListener(
+    const ApplicationStatusListener::ApplicationStateChangeCallback& callback)
+    : callback_(callback) {
+  DCHECK(!callback_.is_null());
+  g_observers.Get().AddObserver(this);
+
+  Java_ApplicationStatus_registerThreadSafeNativeApplicationStateListener(
+      AttachCurrentThread());
+}
+
+ApplicationStatusListener::~ApplicationStatusListener() {
+  g_observers.Get().RemoveObserver(this);
+}
+
+void ApplicationStatusListener::Notify(ApplicationState state) {
+  callback_.Run(state);
+}
+
+// static
+void ApplicationStatusListener::NotifyApplicationStateChange(
+    ApplicationState state) {
+  g_observers.Get().Notify(FROM_HERE, &ApplicationStatusListener::Notify,
+                           state);
+}
+
+// static
+ApplicationState ApplicationStatusListener::GetState() {
+  return static_cast<ApplicationState>(
+      Java_ApplicationStatus_getStateForApplication(AttachCurrentThread()));
+}
+
+static void JNI_ApplicationStatus_OnApplicationStateChange(
+    JNIEnv* env,
+    const JavaParamRef<jclass>& clazz,
+    jint new_state) {
+  ApplicationState application_state = static_cast<ApplicationState>(new_state);
+  ApplicationStatusListener::NotifyApplicationStateChange(application_state);
+}
+
+}  // namespace android
+}  // namespace base
diff --git a/base/android/application_status_listener.h b/base/android/application_status_listener.h
new file mode 100644
index 0000000..fcc26a2
--- /dev/null
+++ b/base/android/application_status_listener.h
@@ -0,0 +1,88 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_ANDROID_APPLICATION_STATUS_LISTENER_H_
+#define BASE_ANDROID_APPLICATION_STATUS_LISTENER_H_
+
+#include <jni.h>
+
+#include "base/android/jni_android.h"
+#include "base/base_export.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/singleton.h"
+#include "base/observer_list_threadsafe.h"
+
+namespace base {
+namespace android {
+
+// Define application state values like APPLICATION_STATE_VISIBLE in a
+// way that ensures they're always the same than their Java counterpart.
+//
+// Note that these states represent the most visible Activity state.
+// If there are activities with states paused and stopped, only
+// HAS_PAUSED_ACTIVITIES should be returned.
+//
+// A Java counterpart will be generated for this enum.
+// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.base
+enum ApplicationState {
+  APPLICATION_STATE_UNKNOWN = 0,
+  APPLICATION_STATE_HAS_RUNNING_ACTIVITIES = 1,
+  APPLICATION_STATE_HAS_PAUSED_ACTIVITIES = 2,
+  APPLICATION_STATE_HAS_STOPPED_ACTIVITIES = 3,
+  APPLICATION_STATE_HAS_DESTROYED_ACTIVITIES = 4
+};
+
+// A native helper class to listen to state changes of the Android
+// Application. This mirrors org.chromium.base.ApplicationStatus.
+// any thread.
+//
+// To start listening, create a new instance, passing a callback to a
+// function that takes an ApplicationState parameter. To stop listening,
+// simply delete the listener object. The implementation guarantees
+// that the callback will always be called on the thread that created
+// the listener.
+//
+// Example:
+//
+//    void OnApplicationStateChange(ApplicationState state) {
+//       ...
+//    }
+//
+//    // Start listening.
+//    ApplicationStatusListener* my_listener =
+//        new ApplicationStatusListener(
+//            base::Bind(&OnApplicationStateChange));
+//
+//    ...
+//
+//    // Stop listening.
+//    delete my_listener
+//
+class BASE_EXPORT ApplicationStatusListener {
+ public:
+  typedef base::Callback<void(ApplicationState)> ApplicationStateChangeCallback;
+
+  explicit ApplicationStatusListener(
+      const ApplicationStateChangeCallback& callback);
+  ~ApplicationStatusListener();
+
+  // Internal use only: must be public to be called from JNI and unit tests.
+  static void NotifyApplicationStateChange(ApplicationState state);
+
+  // Expose jni call for ApplicationStatus.getStateForApplication.
+  static ApplicationState GetState();
+
+ private:
+  void Notify(ApplicationState state);
+
+  ApplicationStateChangeCallback callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(ApplicationStatusListener);
+};
+
+}  // namespace android
+}  // namespace base
+
+#endif  // BASE_ANDROID_APPLICATION_STATUS_LISTENER_H_
diff --git a/base/android/application_status_listener_unittest.cc b/base/android/application_status_listener_unittest.cc
new file mode 100644
index 0000000..803dedb
--- /dev/null
+++ b/base/android/application_status_listener_unittest.cc
@@ -0,0 +1,131 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/android/application_status_listener.h"
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/callback_forward.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace android {
+
+namespace {
+
+using base::android::ScopedJavaLocalRef;
+
+// An invalid ApplicationState value.
+const ApplicationState kInvalidApplicationState =
+    static_cast<ApplicationState>(100);
+
+// Used to generate a callback that stores the new state at a given location.
+void StoreStateTo(ApplicationState* target, ApplicationState state) {
+  *target = state;
+}
+
+void RunTasksUntilIdle() {
+  RunLoop run_loop;
+  run_loop.RunUntilIdle();
+}
+
+// Shared state for the multi-threaded test.
+// This uses a thread to register for events and listen to them, while state
+// changes are forced on the main thread.
+class MultiThreadedTest {
+ public:
+  MultiThreadedTest()
+      : state_(kInvalidApplicationState),
+        event_(WaitableEvent::ResetPolicy::AUTOMATIC,
+               WaitableEvent::InitialState::NOT_SIGNALED),
+        thread_("ApplicationStatusTest thread"),
+        main_() {}
+
+  void Run() {
+    // Start the thread and tell it to register for events.
+    thread_.Start();
+    thread_.task_runner()->PostTask(
+        FROM_HERE, base::Bind(&MultiThreadedTest::RegisterThreadForEvents,
+                              base::Unretained(this)));
+
+    // Wait for its completion.
+    event_.Wait();
+
+    // Change state, then wait for the thread to modify state.
+    ApplicationStatusListener::NotifyApplicationStateChange(
+        APPLICATION_STATE_HAS_RUNNING_ACTIVITIES);
+    event_.Wait();
+    EXPECT_EQ(APPLICATION_STATE_HAS_RUNNING_ACTIVITIES, state_);
+
+    // Again
+    ApplicationStatusListener::NotifyApplicationStateChange(
+        APPLICATION_STATE_HAS_DESTROYED_ACTIVITIES);
+    event_.Wait();
+    EXPECT_EQ(APPLICATION_STATE_HAS_DESTROYED_ACTIVITIES, state_);
+  }
+
+ private:
+  void ExpectOnThread() {
+    EXPECT_EQ(thread_.message_loop(), base::MessageLoop::current());
+  }
+
+  void RegisterThreadForEvents() {
+    ExpectOnThread();
+    listener_.reset(new ApplicationStatusListener(base::Bind(
+        &MultiThreadedTest::StoreStateAndSignal, base::Unretained(this))));
+    EXPECT_TRUE(listener_.get());
+    event_.Signal();
+  }
+
+  void StoreStateAndSignal(ApplicationState state) {
+    ExpectOnThread();
+    state_ = state;
+    event_.Signal();
+  }
+
+  ApplicationState state_;
+  base::WaitableEvent event_;
+  base::Thread thread_;
+  base::MessageLoop main_;
+  std::unique_ptr<ApplicationStatusListener> listener_;
+};
+
+}  // namespace
+
+TEST(ApplicationStatusListenerTest, SingleThread) {
+  MessageLoop message_loop;
+
+  ApplicationState result = kInvalidApplicationState;
+
+  // Create a new listener that stores the new state into |result| on every
+  // state change.
+  ApplicationStatusListener listener(
+      base::Bind(&StoreStateTo, base::Unretained(&result)));
+
+  EXPECT_EQ(kInvalidApplicationState, result);
+
+  ApplicationStatusListener::NotifyApplicationStateChange(
+      APPLICATION_STATE_HAS_RUNNING_ACTIVITIES);
+  RunTasksUntilIdle();
+  EXPECT_EQ(APPLICATION_STATE_HAS_RUNNING_ACTIVITIES, result);
+
+  ApplicationStatusListener::NotifyApplicationStateChange(
+      APPLICATION_STATE_HAS_DESTROYED_ACTIVITIES);
+  RunTasksUntilIdle();
+  EXPECT_EQ(APPLICATION_STATE_HAS_DESTROYED_ACTIVITIES, result);
+}
+
+TEST(ApplicationStatusListenerTest, TwoThreads) {
+  MultiThreadedTest test;
+  test.Run();
+}
+
+}  // namespace android
+}  // namespace base
diff --git a/base/android/base_jni_onload.cc b/base/android/base_jni_onload.cc
new file mode 100644
index 0000000..170dd84
--- /dev/null
+++ b/base/android/base_jni_onload.cc
@@ -0,0 +1,24 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/android/base_jni_onload.h"
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_utils.h"
+#include "base/android/library_loader/library_loader_hooks.h"
+#include "base/bind.h"
+
+namespace base {
+namespace android {
+
+bool OnJNIOnLoadInit() {
+  InitAtExitManager();
+  JNIEnv* env = base::android::AttachCurrentThread();
+  base::android::InitReplacementClassLoader(env,
+                                            base::android::GetClassLoader(env));
+  return true;
+}
+
+}  // namespace android
+}  // namespace base
diff --git a/base/android/callback_android.cc b/base/android/callback_android.cc
new file mode 100644
index 0000000..7143664
--- /dev/null
+++ b/base/android/callback_android.cc
@@ -0,0 +1,44 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/android/callback_android.h"
+
+#include "base/android/jni_array.h"
+#include "base/android/jni_string.h"
+#include "base/android/scoped_java_ref.h"
+#include "jni/Callback_jni.h"
+
+namespace base {
+namespace android {
+
+void RunObjectCallbackAndroid(const JavaRef<jobject>& callback,
+                              const JavaRef<jobject>& arg) {
+  Java_Helper_onObjectResultFromNative(AttachCurrentThread(), callback, arg);
+}
+
+void RunBooleanCallbackAndroid(const JavaRef<jobject>& callback, bool arg) {
+  Java_Helper_onBooleanResultFromNative(AttachCurrentThread(), callback,
+                                        static_cast<jboolean>(arg));
+}
+
+void RunIntCallbackAndroid(const JavaRef<jobject>& callback, int arg) {
+  Java_Helper_onIntResultFromNative(AttachCurrentThread(), callback, arg);
+}
+
+void RunStringCallbackAndroid(const JavaRef<jobject>& callback,
+                              const std::string& arg) {
+  JNIEnv* env = AttachCurrentThread();
+  ScopedJavaLocalRef<jstring> java_string = ConvertUTF8ToJavaString(env, arg);
+  Java_Helper_onObjectResultFromNative(env, callback, java_string);
+}
+
+void RunByteArrayCallbackAndroid(const JavaRef<jobject>& callback,
+                                 const std::vector<uint8_t>& arg) {
+  JNIEnv* env = AttachCurrentThread();
+  ScopedJavaLocalRef<jbyteArray> j_bytes = ToJavaByteArray(env, arg);
+  Java_Helper_onObjectResultFromNative(env, callback, j_bytes);
+}
+
+}  // namespace android
+}  // namespace base
diff --git a/base/android/callback_android.h b/base/android/callback_android.h
new file mode 100644
index 0000000..8a14c1f
--- /dev/null
+++ b/base/android/callback_android.h
@@ -0,0 +1,38 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_ANDROID_CALLBACK_ANDROID_H_
+#define BASE_ANDROID_CALLBACK_ANDROID_H_
+
+#include <jni.h>
+#include <string>
+#include <vector>
+
+#include "base/android/scoped_java_ref.h"
+#include "base/base_export.h"
+
+// Provides helper utility methods that run the given callback with the
+// specified argument.
+namespace base {
+namespace android {
+
+void BASE_EXPORT RunObjectCallbackAndroid(const JavaRef<jobject>& callback,
+                                          const JavaRef<jobject>& arg);
+
+void BASE_EXPORT RunBooleanCallbackAndroid(const JavaRef<jobject>& callback,
+                                           bool arg);
+
+void BASE_EXPORT RunIntCallbackAndroid(const JavaRef<jobject>& callback,
+                                       int arg);
+
+void BASE_EXPORT RunStringCallbackAndroid(const JavaRef<jobject>& callback,
+                                          const std::string& arg);
+
+void BASE_EXPORT RunByteArrayCallbackAndroid(const JavaRef<jobject>& callback,
+                                             const std::vector<uint8_t>& arg);
+
+}  // namespace android
+}  // namespace base
+
+#endif  // BASE_ANDROID_CALLBACK_ANDROID_H_
diff --git a/base/android/child_process_binding_types.h b/base/android/child_process_binding_types.h
new file mode 100644
index 0000000..a3900d5
--- /dev/null
+++ b/base/android/child_process_binding_types.h
@@ -0,0 +1,25 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_ANDROID_CHILD_PROCESS_BINDING_TYPES_H_
+#define BASE_ANDROID_CHILD_PROCESS_BINDING_TYPES_H_
+
+namespace base {
+namespace android {
+
+// Defines the state of bindgings with child process. See ChildProcessConnection
+// to see what the bindings are. Note these values are used as array indices.
+// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.base
+enum class ChildBindingState {
+  UNBOUND,
+  WAIVED,
+  MODERATE,
+  STRONG,
+  MAX_VALUE = STRONG
+};
+
+}  // namespace android
+}  // namespace base
+
+#endif  // BASE_ANDROID_CHILD_PROCESS_BINDING_TYPES_H_
diff --git a/base/android/child_process_service.cc b/base/android/child_process_service.cc
new file mode 100644
index 0000000..013a70b
--- /dev/null
+++ b/base/android/child_process_service.cc
@@ -0,0 +1,79 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/android/jni_array.h"
+#include "base/android/jni_string.h"
+#include "base/android/library_loader/library_loader_hooks.h"
+#include "base/file_descriptor_store.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/optional.h"
+#include "base/posix/global_descriptors.h"
+#include "jni/ChildProcessService_jni.h"
+
+using base::android::JavaIntArrayToIntVector;
+using base::android::JavaParamRef;
+
+namespace base {
+namespace android {
+
+void JNI_ChildProcessService_RegisterFileDescriptors(
+    JNIEnv* env,
+    const JavaParamRef<jclass>& clazz,
+    const JavaParamRef<jobjectArray>& j_keys,
+    const JavaParamRef<jintArray>& j_ids,
+    const JavaParamRef<jintArray>& j_fds,
+    const JavaParamRef<jlongArray>& j_offsets,
+    const JavaParamRef<jlongArray>& j_sizes) {
+  std::vector<base::Optional<std::string>> keys;
+  jsize keys_size = env->GetArrayLength(j_keys);
+  keys.reserve(keys_size);
+  for (jsize i = 0; i < keys_size; i++) {
+    base::android::ScopedJavaLocalRef<jstring> str(
+        env, static_cast<jstring>(env->GetObjectArrayElement(j_keys, i)));
+    base::Optional<std::string> key;
+    if (!str.is_null()) {
+      key = base::android::ConvertJavaStringToUTF8(env, str);
+    }
+    keys.push_back(std::move(key));
+  }
+
+  std::vector<int> ids;
+  base::android::JavaIntArrayToIntVector(env, j_ids, &ids);
+  std::vector<int> fds;
+  base::android::JavaIntArrayToIntVector(env, j_fds, &fds);
+  std::vector<int64_t> offsets;
+  base::android::JavaLongArrayToInt64Vector(env, j_offsets, &offsets);
+  std::vector<int64_t> sizes;
+  base::android::JavaLongArrayToInt64Vector(env, j_sizes, &sizes);
+
+  DCHECK_EQ(keys.size(), ids.size());
+  DCHECK_EQ(ids.size(), fds.size());
+  DCHECK_EQ(fds.size(), offsets.size());
+  DCHECK_EQ(offsets.size(), sizes.size());
+
+  for (size_t i = 0; i < ids.size(); i++) {
+    base::MemoryMappedFile::Region region = {offsets.at(i), sizes.at(i)};
+    const base::Optional<std::string>& key = keys.at(i);
+    int id = ids.at(i);
+    int fd = fds.at(i);
+    if (key) {
+      base::FileDescriptorStore::GetInstance().Set(*key, base::ScopedFD(fd),
+                                                   region);
+    } else {
+      base::GlobalDescriptors::GetInstance()->Set(id, fd, region);
+    }
+  }
+}
+
+void JNI_ChildProcessService_ExitChildProcess(
+    JNIEnv* env,
+    const JavaParamRef<jclass>& clazz) {
+  VLOG(0) << "ChildProcessService: Exiting child process.";
+  base::android::LibraryLoaderExitHook();
+  _exit(0);
+}
+
+}  // namespace android
+}  // namespace base
diff --git a/base/android/command_line_android.cc b/base/android/command_line_android.cc
new file mode 100644
index 0000000..c9b545f
--- /dev/null
+++ b/base/android/command_line_android.cc
@@ -0,0 +1,89 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/android/jni_array.h"
+#include "base/android/jni_string.h"
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "jni/CommandLine_jni.h"
+
+using base::android::ConvertUTF8ToJavaString;
+using base::android::ConvertJavaStringToUTF8;
+using base::android::JavaParamRef;
+using base::android::ScopedJavaLocalRef;
+using base::CommandLine;
+
+namespace {
+
+void JNI_CommandLine_AppendJavaStringArrayToCommandLine(
+    JNIEnv* env,
+    const JavaParamRef<jobjectArray>& array,
+    bool includes_program) {
+  std::vector<std::string> vec;
+  if (array)
+    base::android::AppendJavaStringArrayToStringVector(env, array, &vec);
+  if (!includes_program)
+    vec.insert(vec.begin(), std::string());
+  CommandLine extra_command_line(vec);
+  CommandLine::ForCurrentProcess()->AppendArguments(extra_command_line,
+                                                    includes_program);
+}
+
+}  // namespace
+
+static jboolean JNI_CommandLine_HasSwitch(
+    JNIEnv* env,
+    const JavaParamRef<jclass>& clazz,
+    const JavaParamRef<jstring>& jswitch) {
+  std::string switch_string(ConvertJavaStringToUTF8(env, jswitch));
+  return CommandLine::ForCurrentProcess()->HasSwitch(switch_string);
+}
+
+static ScopedJavaLocalRef<jstring> JNI_CommandLine_GetSwitchValue(
+    JNIEnv* env,
+    const JavaParamRef<jclass>& clazz,
+    const JavaParamRef<jstring>& jswitch) {
+  std::string switch_string(ConvertJavaStringToUTF8(env, jswitch));
+  std::string value(CommandLine::ForCurrentProcess()->GetSwitchValueNative(
+      switch_string));
+  if (value.empty())
+    return ScopedJavaLocalRef<jstring>();
+  return ConvertUTF8ToJavaString(env, value);
+}
+
+static void JNI_CommandLine_AppendSwitch(JNIEnv* env,
+                                         const JavaParamRef<jclass>& clazz,
+                                         const JavaParamRef<jstring>& jswitch) {
+  std::string switch_string(ConvertJavaStringToUTF8(env, jswitch));
+  CommandLine::ForCurrentProcess()->AppendSwitch(switch_string);
+}
+
+static void JNI_CommandLine_AppendSwitchWithValue(
+    JNIEnv* env,
+    const JavaParamRef<jclass>& clazz,
+    const JavaParamRef<jstring>& jswitch,
+    const JavaParamRef<jstring>& jvalue) {
+  std::string switch_string(ConvertJavaStringToUTF8(env, jswitch));
+  std::string value_string (ConvertJavaStringToUTF8(env, jvalue));
+  CommandLine::ForCurrentProcess()->AppendSwitchASCII(switch_string,
+                                                      value_string);
+}
+
+static void JNI_CommandLine_AppendSwitchesAndArguments(
+    JNIEnv* env,
+    const JavaParamRef<jclass>& clazz,
+    const JavaParamRef<jobjectArray>& array) {
+  JNI_CommandLine_AppendJavaStringArrayToCommandLine(env, array, false);
+}
+
+static void JNI_CommandLine_Init(
+    JNIEnv* env,
+    const JavaParamRef<jclass>& jclazz,
+    const JavaParamRef<jobjectArray>& init_command_line) {
+  // TODO(port): Make an overload of Init() that takes StringVector rather than
+  // have to round-trip via AppendArguments.
+  CommandLine::Init(0, nullptr);
+  JNI_CommandLine_AppendJavaStringArrayToCommandLine(env, init_command_line,
+                                                     true);
+}
diff --git a/base/android/content_uri_utils.cc b/base/android/content_uri_utils.cc
new file mode 100644
index 0000000..a7955dc
--- /dev/null
+++ b/base/android/content_uri_utils.cc
@@ -0,0 +1,45 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/android/content_uri_utils.h"
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "jni/ContentUriUtils_jni.h"
+
+using base::android::ConvertUTF8ToJavaString;
+using base::android::ScopedJavaLocalRef;
+
+namespace base {
+
+bool ContentUriExists(const FilePath& content_uri) {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  ScopedJavaLocalRef<jstring> j_uri =
+      ConvertUTF8ToJavaString(env, content_uri.value());
+  return Java_ContentUriUtils_contentUriExists(env, j_uri);
+}
+
+File OpenContentUriForRead(const FilePath& content_uri) {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  ScopedJavaLocalRef<jstring> j_uri =
+      ConvertUTF8ToJavaString(env, content_uri.value());
+  jint fd = Java_ContentUriUtils_openContentUriForRead(env, j_uri);
+  if (fd < 0)
+    return File();
+  return File(fd);
+}
+
+std::string GetContentUriMimeType(const FilePath& content_uri) {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  ScopedJavaLocalRef<jstring> j_uri =
+      ConvertUTF8ToJavaString(env, content_uri.value());
+  ScopedJavaLocalRef<jstring> j_mime =
+      Java_ContentUriUtils_getMimeType(env, j_uri);
+  if (j_mime.is_null())
+    return std::string();
+
+  return base::android::ConvertJavaStringToUTF8(env, j_mime.obj());
+}
+
+}  // namespace base
diff --git a/base/android/content_uri_utils.h b/base/android/content_uri_utils.h
new file mode 100644
index 0000000..6d817c0
--- /dev/null
+++ b/base/android/content_uri_utils.h
@@ -0,0 +1,29 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_ANDROID_CONTENT_URI_UTILS_H_
+#define BASE_ANDROID_CONTENT_URI_UTILS_H_
+
+#include <jni.h>
+
+#include "base/base_export.h"
+#include "base/files/file.h"
+#include "base/files/file_path.h"
+
+namespace base {
+
+// Opens a content URI for read and returns the file descriptor to the caller.
+// Returns -1 if the URI is invalid.
+BASE_EXPORT File OpenContentUriForRead(const FilePath& content_uri);
+
+// Check whether a content URI exists.
+BASE_EXPORT bool ContentUriExists(const FilePath& content_uri);
+
+// Gets MIME type from a content URI. Returns an empty string if the URI is
+// invalid.
+BASE_EXPORT std::string GetContentUriMimeType(const FilePath& content_uri);
+
+}  // namespace base
+
+#endif  // BASE_ANDROID_CONTENT_URI_UTILS_H_
diff --git a/base/android/content_uri_utils_unittest.cc b/base/android/content_uri_utils_unittest.cc
new file mode 100644
index 0000000..0e5cf0c
--- /dev/null
+++ b/base/android/content_uri_utils_unittest.cc
@@ -0,0 +1,40 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/android/content_uri_utils.h"
+#include "base/files/file_util.h"
+#include "base/path_service.h"
+#include "base/test/test_file_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace android {
+
+// Disable on Android ASAN bot due to consistent failures: crbug.com/807080.
+#if !defined(ADDRESS_SANITIZER)
+TEST(ContentUriUtilsTest, ContentUriMimeTest) {
+  // Get the test image path.
+  FilePath data_dir;
+  ASSERT_TRUE(PathService::Get(DIR_TEST_DATA, &data_dir));
+  data_dir = data_dir.AppendASCII("file_util");
+  ASSERT_TRUE(PathExists(data_dir));
+  FilePath image_file = data_dir.Append(FILE_PATH_LITERAL("red.png"));
+
+  // Insert the image into MediaStore. MediaStore will do some conversions, and
+  // return the content URI.
+  FilePath path = base::InsertImageIntoMediaStore(image_file);
+  EXPECT_TRUE(path.IsContentUri());
+  EXPECT_TRUE(PathExists(path));
+
+  std::string mime = GetContentUriMimeType(path);
+  EXPECT_EQ(mime, std::string("image/png"));
+
+  FilePath invalid_path("content://foo.bar");
+  mime = GetContentUriMimeType(invalid_path);
+  EXPECT_TRUE(mime.empty());
+}
+#endif
+
+}  // namespace android
+}  // namespace base
diff --git a/base/android/cpu_features.cc b/base/android/cpu_features.cc
new file mode 100644
index 0000000..7c1dbe3
--- /dev/null
+++ b/base/android/cpu_features.cc
@@ -0,0 +1,22 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <cpu-features.h>
+
+#include "base/android/jni_android.h"
+#include "jni/CpuFeatures_jni.h"
+
+namespace base {
+namespace android {
+
+jint JNI_CpuFeatures_GetCoreCount(JNIEnv*, const JavaParamRef<jclass>&) {
+  return android_getCpuCount();
+}
+
+jlong JNI_CpuFeatures_GetCpuFeatures(JNIEnv*, const JavaParamRef<jclass>&) {
+  return android_getCpuFeatures();
+}
+
+}  // namespace android
+}  // namespace base
diff --git a/base/android/early_trace_event_binding.cc b/base/android/early_trace_event_binding.cc
new file mode 100644
index 0000000..bf6b910
--- /dev/null
+++ b/base/android/early_trace_event_binding.cc
@@ -0,0 +1,67 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdint.h>
+
+#include "base/android/jni_string.h"
+#include "base/time/time.h"
+#include "base/trace_event/trace_event.h"
+#include "jni/EarlyTraceEvent_jni.h"
+
+namespace base {
+namespace android {
+
+const char kEarlyJavaCategory[] = "EarlyJava";
+
+static void JNI_EarlyTraceEvent_RecordEarlyEvent(
+    JNIEnv* env,
+    const JavaParamRef<jclass>& clazz,
+    const JavaParamRef<jstring>& jname,
+    jlong begin_time_ns,
+    jlong end_time_ns,
+    jint thread_id,
+    jlong thread_duration_ms) {
+  std::string name = ConvertJavaStringToUTF8(env, jname);
+  int64_t begin_us = begin_time_ns / 1000;
+  int64_t end_us = end_time_ns / 1000;
+  int64_t thread_duration_us = thread_duration_ms * 1000;
+
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMPS(
+      kEarlyJavaCategory, name.c_str(), trace_event_internal::kNoId, thread_id,
+      TimeTicks::FromInternalValue(begin_us),
+      TimeTicks::FromInternalValue(end_us),
+      ThreadTicks::Now() + TimeDelta::FromMicroseconds(thread_duration_us),
+      TRACE_EVENT_FLAG_COPY);
+}
+
+static void JNI_EarlyTraceEvent_RecordEarlyStartAsyncEvent(
+    JNIEnv* env,
+    const JavaParamRef<jclass>& clazz,
+    const JavaParamRef<jstring>& jname,
+    jlong id,
+    jlong timestamp_ns) {
+  std::string name = ConvertJavaStringToUTF8(env, jname);
+  int64_t timestamp_us = timestamp_ns / 1000;
+
+  TRACE_EVENT_COPY_ASYNC_BEGIN_WITH_TIMESTAMP0(
+      kEarlyJavaCategory, name.c_str(), id,
+      base::TimeTicks() + base::TimeDelta::FromMicroseconds(timestamp_us));
+}
+
+static void JNI_EarlyTraceEvent_RecordEarlyFinishAsyncEvent(
+    JNIEnv* env,
+    const JavaParamRef<jclass>& clazz,
+    const JavaParamRef<jstring>& jname,
+    jlong id,
+    jlong timestamp_ns) {
+  std::string name = ConvertJavaStringToUTF8(env, jname);
+  int64_t timestamp_us = timestamp_ns / 1000;
+
+  TRACE_EVENT_COPY_ASYNC_END_WITH_TIMESTAMP0(
+      kEarlyJavaCategory, name.c_str(), id,
+      base::TimeTicks() + base::TimeDelta::FromMicroseconds(timestamp_us));
+}
+
+}  // namespace android
+}  // namespace base
diff --git a/base/android/event_log.cc b/base/android/event_log.cc
new file mode 100644
index 0000000..3eb5926
--- /dev/null
+++ b/base/android/event_log.cc
@@ -0,0 +1,16 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/android/event_log.h"
+#include "jni/EventLog_jni.h"
+
+namespace base {
+namespace android {
+
+void EventLogWriteInt(int tag, int value) {
+  Java_EventLog_writeEvent(AttachCurrentThread(), tag, value);
+}
+
+}  // namespace android
+}  // namespace base
diff --git a/base/android/event_log.h b/base/android/event_log.h
new file mode 100644
index 0000000..ebd5919
--- /dev/null
+++ b/base/android/event_log.h
@@ -0,0 +1,20 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_ANDROID_EVENT_LOG_H_
+#define BASE_ANDROID_EVENT_LOG_H_
+
+#include <jni.h>
+
+#include "base/base_export.h"
+
+namespace base {
+namespace android {
+
+void BASE_EXPORT EventLogWriteInt(int tag, int value);
+
+}  // namespace android
+}  // namespace base
+
+#endif  // BASE_ANDROID_EVENT_LOG_H_
diff --git a/base/android/field_trial_list.cc b/base/android/field_trial_list.cc
new file mode 100644
index 0000000..1dec5b5
--- /dev/null
+++ b/base/android/field_trial_list.cc
@@ -0,0 +1,47 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <jni.h>
+
+#include <map>
+#include <string>
+
+#include "base/android/jni_string.h"
+#include "base/metrics/field_trial.h"
+#include "base/metrics/field_trial_params.h"
+#include "jni/FieldTrialList_jni.h"
+
+using base::android::ConvertJavaStringToUTF8;
+using base::android::ConvertUTF8ToJavaString;
+using base::android::JavaParamRef;
+using base::android::ScopedJavaLocalRef;
+
+static ScopedJavaLocalRef<jstring> JNI_FieldTrialList_FindFullName(
+    JNIEnv* env,
+    const JavaParamRef<jclass>& clazz,
+    const JavaParamRef<jstring>& jtrial_name) {
+  std::string trial_name(ConvertJavaStringToUTF8(env, jtrial_name));
+  return ConvertUTF8ToJavaString(
+      env, base::FieldTrialList::FindFullName(trial_name));
+}
+
+static jboolean JNI_FieldTrialList_TrialExists(
+    JNIEnv* env,
+    const JavaParamRef<jclass>& clazz,
+    const JavaParamRef<jstring>& jtrial_name) {
+  std::string trial_name(ConvertJavaStringToUTF8(env, jtrial_name));
+  return base::FieldTrialList::TrialExists(trial_name);
+}
+
+static ScopedJavaLocalRef<jstring> JNI_FieldTrialList_GetVariationParameter(
+    JNIEnv* env,
+    const JavaParamRef<jclass>& clazz,
+    const JavaParamRef<jstring>& jtrial_name,
+    const JavaParamRef<jstring>& jparameter_key) {
+  std::map<std::string, std::string> parameters;
+  base::GetFieldTrialParams(ConvertJavaStringToUTF8(env, jtrial_name),
+                            &parameters);
+  return ConvertUTF8ToJavaString(
+      env, parameters[ConvertJavaStringToUTF8(env, jparameter_key)]);
+}
diff --git a/base/android/important_file_writer_android.cc b/base/android/important_file_writer_android.cc
new file mode 100644
index 0000000..fcaa3b1
--- /dev/null
+++ b/base/android/important_file_writer_android.cc
@@ -0,0 +1,37 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <string>
+
+#include "base/android/jni_string.h"
+#include "base/files/important_file_writer.h"
+#include "base/threading/thread_restrictions.h"
+#include "jni/ImportantFileWriterAndroid_jni.h"
+
+namespace base {
+namespace android {
+
+static jboolean JNI_ImportantFileWriterAndroid_WriteFileAtomically(
+    JNIEnv* env,
+    const JavaParamRef<jclass>& /* clazz */,
+    const JavaParamRef<jstring>& file_name,
+    const JavaParamRef<jbyteArray>& data) {
+  // This is called on the UI thread during shutdown to save tab data, so
+  // needs to enable IO.
+  base::ThreadRestrictions::ScopedAllowIO allow_io;
+  std::string native_file_name;
+  base::android::ConvertJavaStringToUTF8(env, file_name, &native_file_name);
+  base::FilePath path(native_file_name);
+  int data_length = env->GetArrayLength(data);
+  jbyte* native_data = env->GetByteArrayElements(data, NULL);
+  std::string native_data_string(reinterpret_cast<char *>(native_data),
+                                 data_length);
+  bool result = base::ImportantFileWriter::WriteFileAtomically(
+      path, native_data_string);
+  env->ReleaseByteArrayElements(data, native_data, JNI_ABORT);
+  return result;
+}
+
+}  // namespace android
+}  // namespace base
diff --git a/base/android/java/src/org/chromium/base/ActivityState.java b/base/android/java/src/org/chromium/base/ActivityState.java
new file mode 100644
index 0000000..b14814c
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/ActivityState.java
@@ -0,0 +1,48 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base;
+
+import android.support.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * A set of states that represent the last state change of an Activity.
+ */
+@Retention(RetentionPolicy.SOURCE)
+@IntDef({ActivityState.CREATED, ActivityState.STARTED, ActivityState.RESUMED, ActivityState.PAUSED,
+        ActivityState.STOPPED, ActivityState.DESTROYED})
+public @interface ActivityState {
+    /**
+     * Represents Activity#onCreate().
+     */
+    int CREATED = 1;
+
+    /**
+     * Represents Activity#onStart().
+     */
+    int STARTED = 2;
+
+    /**
+     * Represents Activity#onResume().
+     */
+    int RESUMED = 3;
+
+    /**
+     * Represents Activity#onPause().
+     */
+    int PAUSED = 4;
+
+    /**
+     * Represents Activity#onStop().
+     */
+    int STOPPED = 5;
+
+    /**
+     * Represents Activity#onDestroy().  This is also used when the state of an Activity is unknown.
+     */
+    int DESTROYED = 6;
+}
diff --git a/base/android/java/src/org/chromium/base/AnimationFrameTimeHistogram.java b/base/android/java/src/org/chromium/base/AnimationFrameTimeHistogram.java
new file mode 100644
index 0000000..ad5cdd8
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/AnimationFrameTimeHistogram.java
@@ -0,0 +1,145 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base;
+
+import android.animation.Animator;
+import android.animation.Animator.AnimatorListener;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.TimeAnimator;
+import android.animation.TimeAnimator.TimeListener;
+import android.util.Log;
+
+/**
+ * Record Android animation frame rate and save it to UMA histogram. This is mainly for monitoring
+ * any jankiness of short Chrome Android animations. It is limited to few seconds of recording.
+ */
+public class AnimationFrameTimeHistogram {
+    private static final String TAG = "AnimationFrameTimeHistogram";
+    private static final int MAX_FRAME_TIME_NUM = 600; // 10 sec on 60 fps.
+
+    private final Recorder mRecorder = new Recorder();
+    private final String mHistogramName;
+
+    /**
+     * @param histogramName The histogram name that the recorded frame times will be saved.
+     *                      This must be also defined in histograms.xml
+     * @return An AnimatorListener instance that records frame time histogram on start and end
+     *         automatically.
+     */
+    public static AnimatorListener getAnimatorRecorder(final String histogramName) {
+        return new AnimatorListenerAdapter() {
+            private final AnimationFrameTimeHistogram mAnimationFrameTimeHistogram =
+                    new AnimationFrameTimeHistogram(histogramName);
+
+            @Override
+            public void onAnimationStart(Animator animation) {
+                mAnimationFrameTimeHistogram.startRecording();
+            }
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mAnimationFrameTimeHistogram.endRecording();
+            }
+
+            @Override
+            public void onAnimationCancel(Animator animation) {
+                mAnimationFrameTimeHistogram.endRecording();
+            }
+        };
+    }
+
+    /**
+     * @param histogramName The histogram name that the recorded frame times will be saved.
+     *                      This must be also defined in histograms.xml
+     */
+    public AnimationFrameTimeHistogram(String histogramName) {
+        mHistogramName = histogramName;
+    }
+
+    /**
+     * Start recording frame times. The recording can fail if it exceeds a few seconds.
+     */
+    public void startRecording() {
+        mRecorder.startRecording();
+    }
+
+    /**
+     * End recording and save it to histogram. It won't save histogram if the recording wasn't
+     * successful.
+     */
+    public void endRecording() {
+        if (mRecorder.endRecording()) {
+            nativeSaveHistogram(mHistogramName,
+                    mRecorder.getFrameTimesMs(), mRecorder.getFrameTimesCount());
+        }
+        mRecorder.cleanUp();
+    }
+
+    /**
+     * Record Android animation frame rate and return the result.
+     */
+    private static class Recorder implements TimeListener {
+        // TODO(kkimlabs): If we can use in the future, migrate to Choreographer for minimal
+        //                 workload.
+        private final TimeAnimator mAnimator = new TimeAnimator();
+        private long[] mFrameTimesMs;
+        private int mFrameTimesCount;
+
+        private Recorder() {
+            mAnimator.setTimeListener(this);
+        }
+
+        private void startRecording() {
+            assert !mAnimator.isRunning();
+            mFrameTimesCount = 0;
+            mFrameTimesMs = new long[MAX_FRAME_TIME_NUM];
+            mAnimator.start();
+        }
+
+        /**
+         * @return Whether the recording was successful. If successful, the result is available via
+         *         getFrameTimesNs and getFrameTimesCount.
+         */
+        private boolean endRecording() {
+            boolean succeeded = mAnimator.isStarted();
+            mAnimator.end();
+            return succeeded;
+        }
+
+        private long[] getFrameTimesMs() {
+            return mFrameTimesMs;
+        }
+
+        private int getFrameTimesCount() {
+            return mFrameTimesCount;
+        }
+
+        /**
+         * Deallocates the temporary buffer to record frame times. Must be called after ending
+         * the recording and getting the result.
+         */
+        private void cleanUp() {
+            mFrameTimesMs = null;
+        }
+
+        @Override
+        public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) {
+            if (mFrameTimesCount == mFrameTimesMs.length) {
+                mAnimator.end();
+                cleanUp();
+                Log.w(TAG, "Animation frame time recording reached the maximum number. It's either"
+                        + "the animation took too long or recording end is not called.");
+                return;
+            }
+
+            // deltaTime is 0 for the first frame.
+            if (deltaTime > 0) {
+                mFrameTimesMs[mFrameTimesCount++] = deltaTime;
+            }
+        }
+    }
+
+    private native void nativeSaveHistogram(String histogramName, long[] frameTimesMs, int count);
+}
diff --git a/base/android/java/src/org/chromium/base/ApiCompatibilityUtils.java b/base/android/java/src/org/chromium/base/ApiCompatibilityUtils.java
new file mode 100644
index 0000000..d1c4693
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/ApiCompatibilityUtils.java
@@ -0,0 +1,705 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base;
+
+import android.annotation.TargetApi;
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.ActivityOptions;
+import android.app.PendingIntent;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.content.res.Resources.NotFoundException;
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.VectorDrawable;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.PowerManager;
+import android.os.Process;
+import android.os.StatFs;
+import android.os.StrictMode;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.support.annotation.NonNull;
+import android.text.Html;
+import android.text.Spanned;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+import android.view.inputmethod.InputMethodSubtype;
+import android.view.textclassifier.TextClassifier;
+import android.widget.TextView;
+
+import java.io.File;
+import java.io.UnsupportedEncodingException;
+
+/**
+ * Utility class to use new APIs that were added after ICS (API level 14).
+ */
+@TargetApi(Build.VERSION_CODES.LOLLIPOP)
+public class ApiCompatibilityUtils {
+    private ApiCompatibilityUtils() {
+    }
+
+    /**
+     * Compares two long values numerically. The value returned is identical to what would be
+     * returned by {@link Long#compare(long, long)} which is available since API level 19.
+     */
+    public static int compareLong(long lhs, long rhs) {
+        return lhs < rhs ? -1 : (lhs == rhs ? 0 : 1);
+    }
+
+    /**
+     * Compares two boolean values. The value returned is identical to what would be returned by
+     * {@link Boolean#compare(boolean, boolean)} which is available since API level 19.
+     */
+    public static int compareBoolean(boolean lhs, boolean rhs) {
+        return lhs == rhs ? 0 : lhs ? 1 : -1;
+    }
+
+    /**
+     * Checks that the object reference is not null and throws NullPointerException if it is.
+     * See {@link Objects#requireNonNull} which is available since API level 19.
+     * @param obj The object to check
+     */
+    @NonNull
+    public static <T> T requireNonNull(T obj) {
+        if (obj == null) throw new NullPointerException();
+        return obj;
+    }
+
+    /**
+     * Checks that the object reference is not null and throws NullPointerException if it is.
+     * See {@link Objects#requireNonNull} which is available since API level 19.
+     * @param obj The object to check
+     * @param message The message to put into NullPointerException
+     */
+    @NonNull
+    public static <T> T requireNonNull(T obj, String message) {
+        if (obj == null) throw new NullPointerException(message);
+        return obj;
+    }
+
+    /**
+     * {@link String#getBytes()} but specifying UTF-8 as the encoding and capturing the resulting
+     * UnsupportedEncodingException.
+     */
+    public static byte[] getBytesUtf8(String str) {
+        try {
+            return str.getBytes("UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            throw new IllegalStateException("UTF-8 encoding not available.", e);
+        }
+    }
+
+    /**
+     * Returns true if view's layout direction is right-to-left.
+     *
+     * @param view the View whose layout is being considered
+     */
+    public static boolean isLayoutRtl(View view) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+            return view.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
+        } else {
+            // All layouts are LTR before JB MR1.
+            return false;
+        }
+    }
+
+    /**
+     * @see Configuration#getLayoutDirection()
+     */
+    public static int getLayoutDirection(Configuration configuration) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+            return configuration.getLayoutDirection();
+        } else {
+            // All layouts are LTR before JB MR1.
+            return View.LAYOUT_DIRECTION_LTR;
+        }
+    }
+
+    /**
+     * @return True if the running version of the Android supports printing.
+     */
+    public static boolean isPrintingSupported() {
+        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
+    }
+
+    /**
+     * @return True if the running version of the Android supports elevation. Elevation of a view
+     * determines the visual appearance of its shadow.
+     */
+    public static boolean isElevationSupported() {
+        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
+    }
+
+    /**
+     * @see android.view.View#setLayoutDirection(int)
+     */
+    public static void setLayoutDirection(View view, int layoutDirection) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+            view.setLayoutDirection(layoutDirection);
+        } else {
+            // Do nothing. RTL layouts aren't supported before JB MR1.
+        }
+    }
+
+    /**
+     * @see android.view.View#setTextAlignment(int)
+     */
+    public static void setTextAlignment(View view, int textAlignment) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+            view.setTextAlignment(textAlignment);
+        } else {
+            // Do nothing. RTL text isn't supported before JB MR1.
+        }
+    }
+
+    /**
+     * @see android.view.View#setTextDirection(int)
+     */
+    public static void setTextDirection(View view, int textDirection) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+            view.setTextDirection(textDirection);
+        } else {
+            // Do nothing. RTL text isn't supported before JB MR1.
+        }
+    }
+
+    /**
+     * See {@link android.view.View#setLabelFor(int)}.
+     */
+    public static void setLabelFor(View labelView, int id) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+            labelView.setLabelFor(id);
+        } else {
+            // Do nothing. #setLabelFor() isn't supported before JB MR1.
+        }
+    }
+
+    /**
+     * @see android.widget.TextView#getCompoundDrawablesRelative()
+     */
+    public static Drawable[] getCompoundDrawablesRelative(TextView textView) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+            return textView.getCompoundDrawablesRelative();
+        } else {
+            return textView.getCompoundDrawables();
+        }
+    }
+
+    /**
+     * @see android.widget.TextView#setCompoundDrawablesRelative(Drawable, Drawable, Drawable,
+     *      Drawable)
+     */
+    public static void setCompoundDrawablesRelative(TextView textView, Drawable start, Drawable top,
+            Drawable end, Drawable bottom) {
+        if (Build.VERSION.SDK_INT == Build.VERSION_CODES.JELLY_BEAN_MR1) {
+            // On JB MR1, due to a platform bug, setCompoundDrawablesRelative() is a no-op if the
+            // view has ever been measured. As a workaround, use setCompoundDrawables() directly.
+            // See: http://crbug.com/368196 and http://crbug.com/361709
+            boolean isRtl = isLayoutRtl(textView);
+            textView.setCompoundDrawables(isRtl ? end : start, top, isRtl ? start : end, bottom);
+        } else if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR1) {
+            textView.setCompoundDrawablesRelative(start, top, end, bottom);
+        } else {
+            textView.setCompoundDrawables(start, top, end, bottom);
+        }
+    }
+
+    /**
+     * @see android.widget.TextView#setCompoundDrawablesRelativeWithIntrinsicBounds(Drawable,
+     *      Drawable, Drawable, Drawable)
+     */
+    public static void setCompoundDrawablesRelativeWithIntrinsicBounds(TextView textView,
+            Drawable start, Drawable top, Drawable end, Drawable bottom) {
+        if (Build.VERSION.SDK_INT == Build.VERSION_CODES.JELLY_BEAN_MR1) {
+            // Work around the platform bug described in setCompoundDrawablesRelative() above.
+            boolean isRtl = isLayoutRtl(textView);
+            textView.setCompoundDrawablesWithIntrinsicBounds(isRtl ? end : start, top,
+                    isRtl ? start : end, bottom);
+        } else if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR1) {
+            textView.setCompoundDrawablesRelativeWithIntrinsicBounds(start, top, end, bottom);
+        } else {
+            textView.setCompoundDrawablesWithIntrinsicBounds(start, top, end, bottom);
+        }
+    }
+
+    /**
+     * @see android.widget.TextView#setCompoundDrawablesRelativeWithIntrinsicBounds(int, int, int,
+     *      int)
+     */
+    public static void setCompoundDrawablesRelativeWithIntrinsicBounds(TextView textView,
+            int start, int top, int end, int bottom) {
+        if (Build.VERSION.SDK_INT == Build.VERSION_CODES.JELLY_BEAN_MR1) {
+            // Work around the platform bug described in setCompoundDrawablesRelative() above.
+            boolean isRtl = isLayoutRtl(textView);
+            textView.setCompoundDrawablesWithIntrinsicBounds(isRtl ? end : start, top,
+                    isRtl ? start : end, bottom);
+        } else if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR1) {
+            textView.setCompoundDrawablesRelativeWithIntrinsicBounds(start, top, end, bottom);
+        } else {
+            textView.setCompoundDrawablesWithIntrinsicBounds(start, top, end, bottom);
+        }
+    }
+
+    /**
+     * @see android.text.Html#toHtml(Spanned, int)
+     * @param option is ignored on below N
+     */
+    @SuppressWarnings("deprecation")
+    public static String toHtml(Spanned spanned, int option) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+            return Html.toHtml(spanned, option);
+        } else {
+            return Html.toHtml(spanned);
+        }
+    }
+
+    // These methods have a new name, and the old name is deprecated.
+
+    /**
+     * @see android.app.PendingIntent#getCreatorPackage()
+     */
+    @SuppressWarnings("deprecation")
+    public static String getCreatorPackage(PendingIntent intent) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+            return intent.getCreatorPackage();
+        } else {
+            return intent.getTargetPackage();
+        }
+    }
+
+    /**
+     * @see android.provider.Settings.Global#DEVICE_PROVISIONED
+     */
+    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
+    public static boolean isDeviceProvisioned(Context context) {
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) return true;
+        if (context == null) return true;
+        if (context.getContentResolver() == null) return true;
+        return Settings.Global.getInt(
+                context.getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 0) != 0;
+    }
+
+    /**
+     * @see android.app.Activity#finishAndRemoveTask()
+     */
+    public static void finishAndRemoveTask(Activity activity) {
+        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) {
+            activity.finishAndRemoveTask();
+        } else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP) {
+            // crbug.com/395772 : Fallback for Activity.finishAndRemoveTask() failing.
+            new FinishAndRemoveTaskWithRetry(activity).run();
+        } else {
+            activity.finish();
+        }
+    }
+
+    /**
+     * Set elevation if supported.
+     */
+    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+    public static boolean setElevation(View view, float elevationValue) {
+        if (!isElevationSupported()) return false;
+
+        view.setElevation(elevationValue);
+        return true;
+    }
+
+    /**
+     *  Gets an intent to start the Android system notification settings activity for an app.
+     *
+     *  @param context Context of the app whose settings intent should be returned.
+     */
+    public static Intent getNotificationSettingsIntent(Context context) {
+        Intent intent = new Intent();
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+            intent.setAction(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
+            intent.putExtra(Settings.EXTRA_APP_PACKAGE, context.getPackageName());
+        } else {
+            intent.setAction("android.settings.ACTION_APP_NOTIFICATION_SETTINGS");
+            intent.putExtra("app_package", context.getPackageName());
+            intent.putExtra("app_uid", context.getApplicationInfo().uid);
+        }
+        return intent;
+    }
+
+    private static class FinishAndRemoveTaskWithRetry implements Runnable {
+        private static final long RETRY_DELAY_MS = 500;
+        private static final long MAX_TRY_COUNT = 3;
+        private final Activity mActivity;
+        private int mTryCount;
+
+        FinishAndRemoveTaskWithRetry(Activity activity) {
+            mActivity = activity;
+        }
+
+        @Override
+        public void run() {
+            mActivity.finishAndRemoveTask();
+            mTryCount++;
+            if (!mActivity.isFinishing()) {
+                if (mTryCount < MAX_TRY_COUNT) {
+                    ThreadUtils.postOnUiThreadDelayed(this, RETRY_DELAY_MS);
+                } else {
+                    mActivity.finish();
+                }
+            }
+        }
+    }
+
+    /**
+     * @return Whether the screen of the device is interactive.
+     */
+    @SuppressWarnings("deprecation")
+    public static boolean isInteractive(Context context) {
+        PowerManager manager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
+            return manager.isInteractive();
+        } else {
+            return manager.isScreenOn();
+        }
+    }
+
+    @SuppressWarnings("deprecation")
+    public static int getActivityNewDocumentFlag() {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+            return Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
+        } else {
+            return Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET;
+        }
+    }
+
+    /**
+     * @see android.provider.Settings.Secure#SKIP_FIRST_USE_HINTS
+     */
+    public static boolean shouldSkipFirstUseHints(ContentResolver contentResolver) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+            return Settings.Secure.getInt(
+                    contentResolver, Settings.Secure.SKIP_FIRST_USE_HINTS, 0) != 0;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * @param activity Activity that should get the task description update.
+     * @param title Title of the activity.
+     * @param icon Icon of the activity.
+     * @param color Color of the activity. It must be a fully opaque color.
+     */
+    public static void setTaskDescription(Activity activity, String title, Bitmap icon, int color) {
+        // TaskDescription requires an opaque color.
+        assert Color.alpha(color) == 255;
+
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+            ActivityManager.TaskDescription description =
+                    new ActivityManager.TaskDescription(title, icon, color);
+            activity.setTaskDescription(description);
+        }
+    }
+
+    /**
+     * @see android.view.Window#setStatusBarColor(int color).
+     */
+    public static void setStatusBarColor(Window window, int statusBarColor) {
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return;
+
+        // If both system bars are black, we can remove these from our layout,
+        // removing or shrinking the SurfaceFlinger overlay required for our views.
+        // This benefits battery usage on L and M.  However, this no longer provides a battery
+        // benefit as of N and starts to cause flicker bugs on O, so don't bother on O and up.
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O && statusBarColor == Color.BLACK
+                && window.getNavigationBarColor() == Color.BLACK) {
+            window.clearFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
+        } else {
+            window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
+        }
+        window.setStatusBarColor(statusBarColor);
+    }
+
+    /**
+     * Sets the status bar icons to dark or light. Note that this is only valid for
+     * Android M+.
+     *
+     * @param rootView The root view used to request updates to the system UI theming.
+     * @param useDarkIcons Whether the status bar icons should be dark.
+     */
+    public static void setStatusBarIconColor(View rootView, boolean useDarkIcons) {
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return;
+
+        int systemUiVisibility = rootView.getSystemUiVisibility();
+        if (useDarkIcons) {
+            systemUiVisibility |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
+        } else {
+            systemUiVisibility &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
+        }
+        rootView.setSystemUiVisibility(systemUiVisibility);
+    }
+
+    /**
+     * @see android.content.res.Resources#getDrawable(int id).
+     * TODO(ltian): use {@link AppCompatResources} to parse drawable to prevent fail on
+     * {@link VectorDrawable}. (http://crbug.com/792129)
+     */
+    @SuppressWarnings("deprecation")
+    public static Drawable getDrawable(Resources res, int id) throws NotFoundException {
+        StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
+        try {
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+                return res.getDrawable(id, null);
+            } else {
+                return res.getDrawable(id);
+            }
+        } finally {
+            StrictMode.setThreadPolicy(oldPolicy);
+        }
+    }
+
+    /**
+     * @see android.content.res.Resources#getDrawableForDensity(int id, int density).
+     */
+    @SuppressWarnings("deprecation")
+    public static Drawable getDrawableForDensity(Resources res, int id, int density) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+            return res.getDrawableForDensity(id, density, null);
+        } else {
+            return res.getDrawableForDensity(id, density);
+        }
+    }
+
+    /**
+     * @see android.app.Activity#finishAfterTransition().
+     */
+    public static void finishAfterTransition(Activity activity) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+            activity.finishAfterTransition();
+        } else {
+            activity.finish();
+        }
+    }
+
+    /**
+     * @see android.content.pm.PackageManager#getUserBadgedIcon(Drawable, android.os.UserHandle).
+     */
+    public static Drawable getUserBadgedIcon(Context context, int id) {
+        Drawable drawable = getDrawable(context.getResources(), id);
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+            PackageManager packageManager = context.getPackageManager();
+            drawable = packageManager.getUserBadgedIcon(drawable, Process.myUserHandle());
+        }
+        return drawable;
+    }
+
+    /**
+     * @see android.content.pm.PackageManager#getUserBadgedDrawableForDensity(Drawable drawable,
+     * UserHandle user, Rect badgeLocation, int badgeDensity).
+     */
+    public static Drawable getUserBadgedDrawableForDensity(
+            Context context, Drawable drawable, Rect badgeLocation, int density) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+            PackageManager packageManager = context.getPackageManager();
+            return packageManager.getUserBadgedDrawableForDensity(
+                    drawable, Process.myUserHandle(), badgeLocation, density);
+        }
+        return drawable;
+    }
+
+    /**
+     * @see android.content.res.Resources#getColor(int id).
+     */
+    @SuppressWarnings("deprecation")
+    public static int getColor(Resources res, int id) throws NotFoundException {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+            return res.getColor(id, null);
+        } else {
+            return res.getColor(id);
+        }
+    }
+
+    /**
+     * @see android.graphics.drawable.Drawable#getColorFilter().
+     */
+    @SuppressWarnings("NewApi")
+    public static ColorFilter getColorFilter(Drawable drawable) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+            return drawable.getColorFilter();
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * @see android.widget.TextView#setTextAppearance(int id).
+     */
+    @SuppressWarnings("deprecation")
+    public static void setTextAppearance(TextView view, int id) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+            view.setTextAppearance(id);
+        } else {
+            view.setTextAppearance(view.getContext(), id);
+        }
+    }
+
+    /**
+     * See {@link android.os.StatFs#getAvailableBlocksLong}.
+     */
+    @SuppressWarnings("deprecation")
+    public static long getAvailableBlocks(StatFs statFs) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+            return statFs.getAvailableBlocksLong();
+        } else {
+            return statFs.getAvailableBlocks();
+        }
+    }
+
+    /**
+     * See {@link android.os.StatFs#getBlockCount}.
+     */
+    @SuppressWarnings("deprecation")
+    public static long getBlockCount(StatFs statFs) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+            return statFs.getBlockCountLong();
+        } else {
+            return statFs.getBlockCount();
+        }
+    }
+
+    /**
+     * See {@link android.os.StatFs#getBlockSize}.
+     */
+    @SuppressWarnings("deprecation")
+    public static long getBlockSize(StatFs statFs) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+            return statFs.getBlockSizeLong();
+        } else {
+            return statFs.getBlockSize();
+        }
+    }
+
+    /**
+     * @param context The Android context, used to retrieve the UserManager system service.
+     * @return Whether the device is running in demo mode.
+     */
+    @SuppressWarnings("NewApi")
+    public static boolean isDemoUser(Context context) {
+        // UserManager#isDemoUser() is only available in Android NMR1+.
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N_MR1) return false;
+
+        UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
+        return userManager.isDemoUser();
+    }
+
+    /**
+     * @see Context#checkPermission(String, int, int)
+     */
+    public static int checkPermission(Context context, String permission, int pid, int uid) {
+        try {
+            return context.checkPermission(permission, pid, uid);
+        } catch (RuntimeException e) {
+            // Some older versions of Android throw odd errors when checking for permissions, so
+            // just swallow the exception and treat it as the permission is denied.
+            // crbug.com/639099
+            return PackageManager.PERMISSION_DENIED;
+        }
+    }
+
+    /**
+     * @see android.view.inputmethod.InputMethodSubType#getLocate()
+     */
+    @SuppressWarnings("deprecation")
+    public static String getLocale(InputMethodSubtype inputMethodSubType) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+            return inputMethodSubType.getLanguageTag();
+        } else {
+            return inputMethodSubType.getLocale();
+        }
+    }
+
+    /**
+     * Get a URI for |file| which has the image capture. This function assumes that path of |file|
+     * is based on the result of UiUtils.getDirectoryForImageCapture().
+     *
+     * @param file image capture file.
+     * @return URI for |file|.
+     */
+    public static Uri getUriForImageCaptureFile(File file) {
+        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2
+                ? ContentUriUtils.getContentUriFromFile(file)
+                : Uri.fromFile(file);
+    }
+
+    /**
+     * Get the URI for a downloaded file.
+     *
+     * @param file A downloaded file.
+     * @return URI for |file|.
+     */
+    public static Uri getUriForDownloadedFile(File file) {
+        return Build.VERSION.SDK_INT > Build.VERSION_CODES.M
+                ? FileUtils.getUriForFile(file)
+                : Uri.fromFile(file);
+    }
+
+    /**
+     * @see android.view.Window#FEATURE_INDETERMINATE_PROGRESS
+     */
+    public static void setWindowIndeterminateProgress(Window window) {
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
+            @SuppressWarnings("deprecation")
+            int featureNumber = Window.FEATURE_INDETERMINATE_PROGRESS;
+
+            @SuppressWarnings("deprecation")
+            int featureValue = Window.PROGRESS_VISIBILITY_OFF;
+
+            window.setFeatureInt(featureNumber, featureValue);
+        }
+    }
+
+    /**
+     * @param activity The {@link Activity} to check.
+     * @return Whether or not {@code activity} is currently in Android N+ multi-window mode.
+     */
+    public static boolean isInMultiWindowMode(Activity activity) {
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
+            return false;
+        }
+        return activity.isInMultiWindowMode();
+    }
+
+    /**
+     * Disables the Smart Select {@link TextClassifier} for the given {@link TextView} instance.
+     * @param textView The {@link TextView} that should have its classifier disabled.
+     */
+    @TargetApi(Build.VERSION_CODES.O)
+    public static void disableSmartSelectionTextClassifier(TextView textView) {
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return;
+
+        textView.setTextClassifier(TextClassifier.NO_OP);
+    }
+
+    /**
+     * Creates an ActivityOptions Bundle with basic options and the LaunchDisplayId set.
+     * @param displayId The id of the display to launch into.
+     * @return The created bundle, or null if unsupported.
+     */
+    public static Bundle createLaunchDisplayIdActivityOptions(int displayId) {
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return null;
+
+        ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchDisplayId(displayId);
+        return options.toBundle();
+    }
+}
diff --git a/base/android/java/src/org/chromium/base/ApkAssets.java b/base/android/java/src/org/chromium/base/ApkAssets.java
new file mode 100644
index 0000000..19108e5
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/ApkAssets.java
@@ -0,0 +1,58 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base;
+
+import android.content.res.AssetFileDescriptor;
+import android.content.res.AssetManager;
+import android.util.Log;
+
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.JNINamespace;
+
+import java.io.IOException;
+
+/**
+ * A utility class to retrieve references to uncompressed assets insides the apk. A reference is
+ * defined as tuple (file descriptor, offset, size) enabling direct mapping without deflation.
+ * This can be used even within the renderer process, since it just dup's the apk's fd.
+ */
+@JNINamespace("base::android")
+public class ApkAssets {
+    private static final String LOGTAG = "ApkAssets";
+
+    @CalledByNative
+    public static long[] open(String fileName) {
+        AssetFileDescriptor afd = null;
+        try {
+            AssetManager manager = ContextUtils.getApplicationContext().getAssets();
+            afd = manager.openNonAssetFd(fileName);
+            return new long[] {afd.getParcelFileDescriptor().detachFd(), afd.getStartOffset(),
+                    afd.getLength()};
+        } catch (IOException e) {
+            // As a general rule there's no point logging here because the caller should handle
+            // receiving an fd of -1 sensibly, and the log message is either mirrored later, or
+            // unwanted (in the case where a missing file is expected), or wanted but will be
+            // ignored, as most non-fatal logs are.
+            // It makes sense to log here when the file exists, but is unable to be opened as an fd
+            // because (for example) it is unexpectedly compressed in an apk. In that case, the log
+            // message might save someone some time working out what has gone wrong.
+            // For that reason, we only suppress the message when the exception message doesn't look
+            // informative (Android framework passes the filename as the message on actual file not
+            // found, and the empty string also wouldn't give any useful information for debugging).
+            if (!e.getMessage().equals("") && !e.getMessage().equals(fileName)) {
+                Log.e(LOGTAG, "Error while loading asset " + fileName + ": " + e);
+            }
+            return new long[] {-1, -1, -1};
+        } finally {
+            try {
+                if (afd != null) {
+                    afd.close();
+                }
+            } catch (IOException e2) {
+                Log.e(LOGTAG, "Unable to close AssetFileDescriptor", e2);
+            }
+        }
+    }
+}
diff --git a/base/android/java/src/org/chromium/base/ApplicationStatus.java b/base/android/java/src/org/chromium/base/ApplicationStatus.java
new file mode 100644
index 0000000..9496da8
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/ApplicationStatus.java
@@ -0,0 +1,620 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.app.Application;
+import android.app.Application.ActivityLifecycleCallbacks;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.view.Window;
+
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.JNINamespace;
+
+import java.lang.ref.WeakReference;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Provides information about the current activity's status, and a way
+ * to register / unregister listeners for state changes.
+ */
+@JNINamespace("base::android")
+public class ApplicationStatus {
+    private static final String TOOLBAR_CALLBACK_INTERNAL_WRAPPER_CLASS =
+            "android.support.v7.internal.app.ToolbarActionBar$ToolbarCallbackWrapper";
+    // In builds using the --use_unpublished_apis flag, the ToolbarActionBar class name does not
+    // include the "internal" package.
+    private static final String TOOLBAR_CALLBACK_WRAPPER_CLASS =
+            "android.support.v7.app.ToolbarActionBar$ToolbarCallbackWrapper";
+    private static final String WINDOW_PROFILER_CALLBACK =
+            "com.android.tools.profiler.support.event.WindowProfilerCallback";
+
+    private static class ActivityInfo {
+        private int mStatus = ActivityState.DESTROYED;
+        private ObserverList<ActivityStateListener> mListeners = new ObserverList<>();
+
+        /**
+         * @return The current {@link ActivityState} of the activity.
+         */
+        @ActivityState
+        public int getStatus() {
+            return mStatus;
+        }
+
+        /**
+         * @param status The new {@link ActivityState} of the activity.
+         */
+        public void setStatus(@ActivityState int status) {
+            mStatus = status;
+        }
+
+        /**
+         * @return A list of {@link ActivityStateListener}s listening to this activity.
+         */
+        public ObserverList<ActivityStateListener> getListeners() {
+            return mListeners;
+        }
+    }
+
+    static {
+        // Chrome initializes this only for the main process. This assert aims to try and catch
+        // usages from GPU / renderers, while still allowing tests.
+        assert ContextUtils.isMainProcess()
+                || ContextUtils.getProcessName().contains(":test")
+            : "Cannot use ApplicationState from process: "
+                        + ContextUtils.getProcessName();
+    }
+
+    private static final Object sCurrentApplicationStateLock = new Object();
+
+    @SuppressLint("SupportAnnotationUsage")
+    @ApplicationState
+    // The getStateForApplication() historically returned ApplicationState.HAS_DESTROYED_ACTIVITIES
+    // when no activity has been observed.
+    private static Integer sCurrentApplicationState = ApplicationState.HAS_DESTROYED_ACTIVITIES;
+
+    /** Last activity that was shown (or null if none or it was destroyed). */
+    @SuppressLint("StaticFieldLeak")
+    private static Activity sActivity;
+
+    /** A lazily initialized listener that forwards application state changes to native. */
+    private static ApplicationStateListener sNativeApplicationStateListener;
+
+    private static boolean sIsInitialized;
+
+    /**
+     * A map of which observers listen to state changes from which {@link Activity}.
+     */
+    private static final Map<Activity, ActivityInfo> sActivityInfo = new ConcurrentHashMap<>();
+
+    /**
+     * A list of observers to be notified when any {@link Activity} has a state change.
+     */
+    private static final ObserverList<ActivityStateListener> sGeneralActivityStateListeners =
+            new ObserverList<>();
+
+    /**
+     * A list of observers to be notified when the visibility state of this {@link Application}
+     * changes.  See {@link #getStateForApplication()}.
+     */
+    private static final ObserverList<ApplicationStateListener> sApplicationStateListeners =
+            new ObserverList<>();
+
+    /**
+     * A list of observers to be notified when the window focus changes.
+     * See {@link #registerWindowFocusChangedListener}.
+     */
+    private static final ObserverList<WindowFocusChangedListener> sWindowFocusListeners =
+            new ObserverList<>();
+
+    /**
+     * Interface to be implemented by listeners.
+     */
+    public interface ApplicationStateListener {
+        /**
+         * Called when the application's state changes.
+         * @param newState The application state.
+         */
+        void onApplicationStateChange(@ApplicationState int newState);
+    }
+
+    /**
+     * Interface to be implemented by listeners.
+     */
+    public interface ActivityStateListener {
+        /**
+         * Called when the activity's state changes.
+         * @param activity The activity that had a state change.
+         * @param newState New activity state.
+         */
+        void onActivityStateChange(Activity activity, @ActivityState int newState);
+    }
+
+    /**
+     * Interface to be implemented by listeners for window focus events.
+     */
+    public interface WindowFocusChangedListener {
+        /**
+         * Called when the window focus changes for {@code activity}.
+         * @param activity The {@link Activity} that has a window focus changed event.
+         * @param hasFocus Whether or not {@code activity} gained or lost focus.
+         */
+        public void onWindowFocusChanged(Activity activity, boolean hasFocus);
+    }
+
+    private ApplicationStatus() {}
+
+    /**
+     * Registers a listener to receive window focus updates on activities in this application.
+     * @param listener Listener to receive window focus events.
+     */
+    public static void registerWindowFocusChangedListener(WindowFocusChangedListener listener) {
+        sWindowFocusListeners.addObserver(listener);
+    }
+
+    /**
+     * Unregisters a listener from receiving window focus updates on activities in this application.
+     * @param listener Listener that doesn't want to receive window focus events.
+     */
+    public static void unregisterWindowFocusChangedListener(WindowFocusChangedListener listener) {
+        sWindowFocusListeners.removeObserver(listener);
+    }
+
+    /**
+     * Intercepts calls to an existing Window.Callback. Most invocations are passed on directly
+     * to the composed Window.Callback but enables intercepting/manipulating others.
+     *
+     * This is used to relay window focus changes throughout the app and remedy a bug in the
+     * appcompat library.
+     */
+    private static class WindowCallbackProxy implements InvocationHandler {
+        private final Window.Callback mCallback;
+        private final Activity mActivity;
+
+        public WindowCallbackProxy(Activity activity, Window.Callback callback) {
+            mCallback = callback;
+            mActivity = activity;
+        }
+
+        @Override
+        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+            if (method.getName().equals("onWindowFocusChanged") && args.length == 1
+                    && args[0] instanceof Boolean) {
+                onWindowFocusChanged((boolean) args[0]);
+                return null;
+            } else {
+                try {
+                    return method.invoke(mCallback, args);
+                } catch (InvocationTargetException e) {
+                    // Special-case for when a method is not defined on the underlying
+                    // Window.Callback object. Because we're using a Proxy to forward all method
+                    // calls, this breaks the Android framework's handling for apps built against
+                    // an older SDK. The framework expects an AbstractMethodError but due to
+                    // reflection it becomes wrapped inside an InvocationTargetException. Undo the
+                    // wrapping to signal the framework accordingly.
+                    if (e.getCause() instanceof AbstractMethodError) {
+                        throw e.getCause();
+                    }
+                    throw e;
+                }
+            }
+        }
+
+        public void onWindowFocusChanged(boolean hasFocus) {
+            mCallback.onWindowFocusChanged(hasFocus);
+
+            for (WindowFocusChangedListener listener : sWindowFocusListeners) {
+                listener.onWindowFocusChanged(mActivity, hasFocus);
+            }
+        }
+    }
+
+    /**
+     * Initializes the activity status for a specified application.
+     *
+     * @param application The application whose status you wish to monitor.
+     */
+    public static void initialize(Application application) {
+        if (sIsInitialized) return;
+        sIsInitialized = true;
+
+        registerWindowFocusChangedListener(new WindowFocusChangedListener() {
+            @Override
+            public void onWindowFocusChanged(Activity activity, boolean hasFocus) {
+                if (!hasFocus || activity == sActivity) return;
+
+                int state = getStateForActivity(activity);
+
+                if (state != ActivityState.DESTROYED && state != ActivityState.STOPPED) {
+                    sActivity = activity;
+                }
+
+                // TODO(dtrainor): Notify of active activity change?
+            }
+        });
+
+        application.registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
+            @Override
+            public void onActivityCreated(final Activity activity, Bundle savedInstanceState) {
+                onStateChange(activity, ActivityState.CREATED);
+                Window.Callback callback = activity.getWindow().getCallback();
+                activity.getWindow().setCallback((Window.Callback) Proxy.newProxyInstance(
+                        Window.Callback.class.getClassLoader(), new Class[] {Window.Callback.class},
+                        new ApplicationStatus.WindowCallbackProxy(activity, callback)));
+            }
+
+            @Override
+            public void onActivityDestroyed(Activity activity) {
+                onStateChange(activity, ActivityState.DESTROYED);
+                checkCallback(activity);
+            }
+
+            @Override
+            public void onActivityPaused(Activity activity) {
+                onStateChange(activity, ActivityState.PAUSED);
+                checkCallback(activity);
+            }
+
+            @Override
+            public void onActivityResumed(Activity activity) {
+                onStateChange(activity, ActivityState.RESUMED);
+                checkCallback(activity);
+            }
+
+            @Override
+            public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
+                checkCallback(activity);
+            }
+
+            @Override
+            public void onActivityStarted(Activity activity) {
+                onStateChange(activity, ActivityState.STARTED);
+                checkCallback(activity);
+            }
+
+            @Override
+            public void onActivityStopped(Activity activity) {
+                onStateChange(activity, ActivityState.STOPPED);
+                checkCallback(activity);
+            }
+
+            private void checkCallback(Activity activity) {
+                if (BuildConfig.DCHECK_IS_ON) {
+                    Class<? extends Window.Callback> callback =
+                            activity.getWindow().getCallback().getClass();
+                    assert(Proxy.isProxyClass(callback)
+                            || callback.getName().equals(TOOLBAR_CALLBACK_WRAPPER_CLASS)
+                            || callback.getName().equals(TOOLBAR_CALLBACK_INTERNAL_WRAPPER_CLASS)
+                            || callback.getName().equals(WINDOW_PROFILER_CALLBACK));
+                }
+            }
+        });
+    }
+
+    /**
+     * Asserts that initialize method has been called.
+     */
+    private static void assertInitialized() {
+        if (!sIsInitialized) {
+            throw new IllegalStateException("ApplicationStatus has not been initialized yet.");
+        }
+    }
+
+    /**
+     * Must be called by the main activity when it changes state.
+     *
+     * @param activity Current activity.
+     * @param newState New state value.
+     */
+    private static void onStateChange(Activity activity, @ActivityState int newState) {
+        if (activity == null) throw new IllegalArgumentException("null activity is not supported");
+
+        if (sActivity == null
+                || newState == ActivityState.CREATED
+                || newState == ActivityState.RESUMED
+                || newState == ActivityState.STARTED) {
+            sActivity = activity;
+        }
+
+        int oldApplicationState = getStateForApplication();
+        ActivityInfo info;
+
+        synchronized (sCurrentApplicationStateLock) {
+            if (newState == ActivityState.CREATED) {
+                assert !sActivityInfo.containsKey(activity);
+                sActivityInfo.put(activity, new ActivityInfo());
+            }
+
+            info = sActivityInfo.get(activity);
+            info.setStatus(newState);
+
+            // Remove before calling listeners so that isEveryActivityDestroyed() returns false when
+            // this was the last activity.
+            if (newState == ActivityState.DESTROYED) {
+                sActivityInfo.remove(activity);
+                if (activity == sActivity) sActivity = null;
+            }
+
+            sCurrentApplicationState = determineApplicationState();
+        }
+
+        // Notify all state observers that are specifically listening to this activity.
+        for (ActivityStateListener listener : info.getListeners()) {
+            listener.onActivityStateChange(activity, newState);
+        }
+
+        // Notify all state observers that are listening globally for all activity state
+        // changes.
+        for (ActivityStateListener listener : sGeneralActivityStateListeners) {
+            listener.onActivityStateChange(activity, newState);
+        }
+
+        int applicationState = getStateForApplication();
+        if (applicationState != oldApplicationState) {
+            for (ApplicationStateListener listener : sApplicationStateListeners) {
+                listener.onApplicationStateChange(applicationState);
+            }
+        }
+    }
+
+    /**
+     * Testing method to update the state of the specified activity.
+     */
+    @VisibleForTesting
+    public static void onStateChangeForTesting(Activity activity, int newState) {
+        onStateChange(activity, newState);
+    }
+
+    /**
+     * @return The most recent focused {@link Activity} tracked by this class.  Being focused means
+     *         out of all the activities tracked here, it has most recently gained window focus.
+     */
+    public static Activity getLastTrackedFocusedActivity() {
+        return sActivity;
+    }
+
+    /**
+     * @return A {@link List} of all non-destroyed {@link Activity}s.
+     */
+    public static List<WeakReference<Activity>> getRunningActivities() {
+        assertInitialized();
+        List<WeakReference<Activity>> activities = new ArrayList<>();
+        for (Activity activity : sActivityInfo.keySet()) {
+            activities.add(new WeakReference<>(activity));
+        }
+        return activities;
+    }
+
+    /**
+     * Query the state for a given activity.  If the activity is not being tracked, this will
+     * return {@link ActivityState#DESTROYED}.
+     *
+     * <p>
+     * Please note that Chrome can have multiple activities running simultaneously.  Please also
+     * look at {@link #getStateForApplication()} for more details.
+     *
+     * <p>
+     * When relying on this method, be familiar with the expected life cycle state
+     * transitions:
+     * <a href="http://developer.android.com/guide/components/activities.html#Lifecycle">
+     *   Activity Lifecycle
+     * </a>
+     *
+     * <p>
+     * During activity transitions (activity B launching in front of activity A), A will completely
+     * paused before the creation of activity B begins.
+     *
+     * <p>
+     * A basic flow for activity A starting, followed by activity B being opened and then closed:
+     * <ul>
+     *   <li> -- Starting Activity A --
+     *   <li> Activity A - ActivityState.CREATED
+     *   <li> Activity A - ActivityState.STARTED
+     *   <li> Activity A - ActivityState.RESUMED
+     *   <li> -- Starting Activity B --
+     *   <li> Activity A - ActivityState.PAUSED
+     *   <li> Activity B - ActivityState.CREATED
+     *   <li> Activity B - ActivityState.STARTED
+     *   <li> Activity B - ActivityState.RESUMED
+     *   <li> Activity A - ActivityState.STOPPED
+     *   <li> -- Closing Activity B, Activity A regaining focus --
+     *   <li> Activity B - ActivityState.PAUSED
+     *   <li> Activity A - ActivityState.STARTED
+     *   <li> Activity A - ActivityState.RESUMED
+     *   <li> Activity B - ActivityState.STOPPED
+     *   <li> Activity B - ActivityState.DESTROYED
+     * </ul>
+     *
+     * @param activity The activity whose state is to be returned.
+     * @return The state of the specified activity (see {@link ActivityState}).
+     */
+    @ActivityState
+    public static int getStateForActivity(@Nullable Activity activity) {
+        ApplicationStatus.assertInitialized();
+        if (activity == null) return ActivityState.DESTROYED;
+        ActivityInfo info = sActivityInfo.get(activity);
+        return info != null ? info.getStatus() : ActivityState.DESTROYED;
+    }
+
+    /**
+     * @return The state of the application (see {@link ApplicationState}).
+     */
+    @ApplicationState
+    @CalledByNative
+    public static int getStateForApplication() {
+        synchronized (sCurrentApplicationStateLock) {
+            return sCurrentApplicationState;
+        }
+    }
+
+    /**
+     * Checks whether or not any Activity in this Application is visible to the user.  Note that
+     * this includes the PAUSED state, which can happen when the Activity is temporarily covered
+     * by another Activity's Fragment (e.g.).
+     * @return Whether any Activity under this Application is visible.
+     */
+    public static boolean hasVisibleActivities() {
+        int state = getStateForApplication();
+        return state == ApplicationState.HAS_RUNNING_ACTIVITIES
+                || state == ApplicationState.HAS_PAUSED_ACTIVITIES;
+    }
+
+    /**
+     * Checks to see if there are any active Activity instances being watched by ApplicationStatus.
+     * @return True if all Activities have been destroyed.
+     */
+    public static boolean isEveryActivityDestroyed() {
+        return sActivityInfo.isEmpty();
+    }
+
+    /**
+     * Registers the given listener to receive state changes for all activities.
+     * @param listener Listener to receive state changes.
+     */
+    public static void registerStateListenerForAllActivities(ActivityStateListener listener) {
+        sGeneralActivityStateListeners.addObserver(listener);
+    }
+
+    /**
+     * Registers the given listener to receive state changes for {@code activity}.  After a call to
+     * {@link ActivityStateListener#onActivityStateChange(Activity, int)} with
+     * {@link ActivityState#DESTROYED} all listeners associated with that particular
+     * {@link Activity} are removed.
+     * @param listener Listener to receive state changes.
+     * @param activity Activity to track or {@code null} to track all activities.
+     */
+    @SuppressLint("NewApi")
+    public static void registerStateListenerForActivity(ActivityStateListener listener,
+            Activity activity) {
+        if (activity == null) {
+            throw new IllegalStateException("Attempting to register listener on a null activity.");
+        }
+        ApplicationStatus.assertInitialized();
+
+        ActivityInfo info = sActivityInfo.get(activity);
+        if (info == null) {
+            throw new IllegalStateException(
+                    "Attempting to register listener on an untracked activity.");
+        }
+        assert info.getStatus() != ActivityState.DESTROYED;
+        info.getListeners().addObserver(listener);
+    }
+
+    /**
+     * Unregisters the given listener from receiving activity state changes.
+     * @param listener Listener that doesn't want to receive state changes.
+     */
+    public static void unregisterActivityStateListener(ActivityStateListener listener) {
+        sGeneralActivityStateListeners.removeObserver(listener);
+
+        // Loop through all observer lists for all activities and remove the listener.
+        for (ActivityInfo info : sActivityInfo.values()) {
+            info.getListeners().removeObserver(listener);
+        }
+    }
+
+    /**
+     * Registers the given listener to receive state changes for the application.
+     * @param listener Listener to receive state state changes.
+     */
+    public static void registerApplicationStateListener(ApplicationStateListener listener) {
+        sApplicationStateListeners.addObserver(listener);
+    }
+
+    /**
+     * Unregisters the given listener from receiving state changes.
+     * @param listener Listener that doesn't want to receive state changes.
+     */
+    public static void unregisterApplicationStateListener(ApplicationStateListener listener) {
+        sApplicationStateListeners.removeObserver(listener);
+    }
+
+    /**
+     * Robolectric JUnit tests create a new application between each test, while all the context
+     * in static classes isn't reset. This function allows to reset the application status to avoid
+     * being in a dirty state.
+     */
+    public static void destroyForJUnitTests() {
+        sApplicationStateListeners.clear();
+        sGeneralActivityStateListeners.clear();
+        sActivityInfo.clear();
+        sWindowFocusListeners.clear();
+        sIsInitialized = false;
+        synchronized (sCurrentApplicationStateLock) {
+            sCurrentApplicationState = determineApplicationState();
+        }
+        sActivity = null;
+        sNativeApplicationStateListener = null;
+    }
+
+    /**
+     * Registers the single thread-safe native activity status listener.
+     * This handles the case where the caller is not on the main thread.
+     * Note that this is used by a leaky singleton object from the native
+     * side, hence lifecycle management is greatly simplified.
+     */
+    @CalledByNative
+    private static void registerThreadSafeNativeApplicationStateListener() {
+        ThreadUtils.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                if (sNativeApplicationStateListener != null) return;
+
+                sNativeApplicationStateListener = new ApplicationStateListener() {
+                    @Override
+                    public void onApplicationStateChange(int newState) {
+                        nativeOnApplicationStateChange(newState);
+                    }
+                };
+                registerApplicationStateListener(sNativeApplicationStateListener);
+            }
+        });
+    }
+
+    /**
+     * Determines the current application state as defined by {@link ApplicationState}.  This will
+     * loop over all the activities and check their state to determine what the general application
+     * state should be.
+     * @return HAS_RUNNING_ACTIVITIES if any activity is not paused, stopped, or destroyed.
+     *         HAS_PAUSED_ACTIVITIES if none are running and one is paused.
+     *         HAS_STOPPED_ACTIVITIES if none are running/paused and one is stopped.
+     *         HAS_DESTROYED_ACTIVITIES if none are running/paused/stopped.
+     */
+    @ApplicationState
+    private static int determineApplicationState() {
+        boolean hasPausedActivity = false;
+        boolean hasStoppedActivity = false;
+
+        for (ActivityInfo info : sActivityInfo.values()) {
+            int state = info.getStatus();
+            if (state != ActivityState.PAUSED
+                    && state != ActivityState.STOPPED
+                    && state != ActivityState.DESTROYED) {
+                return ApplicationState.HAS_RUNNING_ACTIVITIES;
+            } else if (state == ActivityState.PAUSED) {
+                hasPausedActivity = true;
+            } else if (state == ActivityState.STOPPED) {
+                hasStoppedActivity = true;
+            }
+        }
+
+        if (hasPausedActivity) return ApplicationState.HAS_PAUSED_ACTIVITIES;
+        if (hasStoppedActivity) return ApplicationState.HAS_STOPPED_ACTIVITIES;
+        return ApplicationState.HAS_DESTROYED_ACTIVITIES;
+    }
+
+    // Called to notify the native side of state changes.
+    // IMPORTANT: This is always called on the main thread!
+    private static native void nativeOnApplicationStateChange(@ApplicationState int newState);
+}
diff --git a/base/android/java/src/org/chromium/base/BaseSwitches.java b/base/android/java/src/org/chromium/base/BaseSwitches.java
new file mode 100644
index 0000000..fe47cdd
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/BaseSwitches.java
@@ -0,0 +1,32 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base;
+
+/**
+ * Contains all of the command line switches that are specific to the base/
+ * portion of Chromium on Android.
+ */
+public abstract class BaseSwitches {
+    // Block ChildProcessMain thread of render process service until a Java debugger is attached.
+    // To pause even earlier: am set-debug-app org.chromium.chrome:sandbox_process0
+    // However, this flag is convenient when you don't know the process number, or want
+    // all renderers to pause (set-debug-app applies to only one process at a time).
+    public static final String RENDERER_WAIT_FOR_JAVA_DEBUGGER = "renderer-wait-for-java-debugger";
+
+    // Force low-end device mode when set.
+    public static final String ENABLE_LOW_END_DEVICE_MODE = "enable-low-end-device-mode";
+
+    // Force disabling of low-end device mode when set.
+    public static final String DISABLE_LOW_END_DEVICE_MODE = "disable-low-end-device-mode";
+
+    // Adds additional thread idle time information into the trace event output.
+    public static final String ENABLE_IDLE_TRACING = "enable-idle-tracing";
+
+    // Default country code to be used for search engine localization.
+    public static final String DEFAULT_COUNTRY_CODE_AT_INSTALL = "default-country-code";
+
+    // Prevent instantiation.
+    private BaseSwitches() {}
+}
diff --git a/base/android/java/src/org/chromium/base/Callback.java b/base/android/java/src/org/chromium/base/Callback.java
new file mode 100644
index 0000000..f5f20b9
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/Callback.java
@@ -0,0 +1,43 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base;
+
+import org.chromium.base.annotations.CalledByNative;
+
+/**
+ * A simple single-argument callback to handle the result of a computation.
+ *
+ * @param <T> The type of the computation's result.
+ */
+public interface Callback<T> {
+    /**
+     * Invoked with the result of a computation.
+     */
+    void onResult(T result);
+
+    /**
+     * JNI Generator does not know how to target static methods on interfaces
+     * (which is new in Java 8, and requires desugaring).
+     */
+    abstract class Helper {
+        @SuppressWarnings("unchecked")
+        @CalledByNative("Helper")
+        static void onObjectResultFromNative(Callback callback, Object result) {
+            callback.onResult(result);
+        }
+
+        @SuppressWarnings("unchecked")
+        @CalledByNative("Helper")
+        static void onBooleanResultFromNative(Callback callback, boolean result) {
+            callback.onResult(Boolean.valueOf(result));
+        }
+
+        @SuppressWarnings("unchecked")
+        @CalledByNative("Helper")
+        static void onIntResultFromNative(Callback callback, int result) {
+            callback.onResult(Integer.valueOf(result));
+        }
+    }
+}
diff --git a/base/android/java/src/org/chromium/base/CollectionUtil.java b/base/android/java/src/org/chromium/base/CollectionUtil.java
new file mode 100644
index 0000000..6093380
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/CollectionUtil.java
@@ -0,0 +1,99 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base;
+
+import android.support.annotation.NonNull;
+import android.util.Pair;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+/**
+ * Functions used for easier initialization of Java collections. Inspired by
+ * functionality in com.google.common.collect in Guava but cherry-picked to
+ * bare-minimum functionality to avoid bloat. (http://crbug.com/272790 provides
+ * further details)
+ */
+public final class CollectionUtil {
+    private CollectionUtil() {}
+
+    @SafeVarargs
+    public static <E> HashSet<E> newHashSet(E... elements) {
+        HashSet<E> set = new HashSet<E>(elements.length);
+        Collections.addAll(set, elements);
+        return set;
+    }
+
+    @SafeVarargs
+    public static <E> ArrayList<E> newArrayList(E... elements) {
+        ArrayList<E> list = new ArrayList<E>(elements.length);
+        Collections.addAll(list, elements);
+        return list;
+    }
+
+    @VisibleForTesting
+    public static <E> ArrayList<E> newArrayList(Iterable<E> iterable) {
+        ArrayList<E> list = new ArrayList<E>();
+        for (E element : iterable) {
+            list.add(element);
+        }
+        return list;
+    }
+
+    @SafeVarargs
+    public static <K, V> HashMap<K, V> newHashMap(Pair<? extends K, ? extends V>... entries) {
+        HashMap<K, V> map = new HashMap<>();
+        for (Pair<? extends K, ? extends V> entry : entries) {
+            map.put(entry.first, entry.second);
+        }
+        return map;
+    }
+
+    public static boolean[] booleanListToBooleanArray(@NonNull List<Boolean> list) {
+        boolean[] array = new boolean[list.size()];
+        for (int i = 0; i < list.size(); i++) {
+            array[i] = list.get(i);
+        }
+        return array;
+    }
+
+    public static int[] integerListToIntArray(@NonNull List<Integer> list) {
+        int[] array = new int[list.size()];
+        for (int i = 0; i < list.size(); i++) {
+            array[i] = list.get(i);
+        }
+        return array;
+    }
+
+    public static long[] longListToLongArray(@NonNull List<Long> list) {
+        long[] array = new long[list.size()];
+        for (int i = 0; i < list.size(); i++) {
+            array[i] = list.get(i);
+        }
+        return array;
+    }
+
+    // This is a utility helper method that adds functionality available in API 24 (see
+    // Collection.forEach).
+    public static <T> void forEach(Collection<? extends T> collection, Callback<T> worker) {
+        for (T entry : collection) worker.onResult(entry);
+    }
+
+    // This is a utility helper method that adds functionality available in API 24 (see
+    // Collection.forEach).
+    @SuppressWarnings("unchecked")
+    public static <K, V> void forEach(
+            Map<? extends K, ? extends V> map, Callback<Entry<K, V>> worker) {
+        for (Map.Entry<? extends K, ? extends V> entry : map.entrySet()) {
+            worker.onResult((Map.Entry<K, V>) entry);
+        }
+    }
+}
diff --git a/base/android/java/src/org/chromium/base/CommandLine.java b/base/android/java/src/org/chromium/base/CommandLine.java
new file mode 100644
index 0000000..963b146
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/CommandLine.java
@@ -0,0 +1,389 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base;
+
+import android.support.annotation.Nullable;
+import android.text.TextUtils;
+import android.util.Log;
+
+import org.chromium.base.annotations.MainDex;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Java mirror of base/command_line.h.
+ * Android applications don't have command line arguments. Instead, they're "simulated" by reading a
+ * file at a specific location early during startup. Applications each define their own files, e.g.,
+ * ContentShellApplication.COMMAND_LINE_FILE.
+**/
+@MainDex
+public abstract class CommandLine {
+    // Public abstract interface, implemented in derived classes.
+    // All these methods reflect their native-side counterparts.
+    /**
+     *  Returns true if this command line contains the given switch.
+     *  (Switch names ARE case-sensitive).
+     */
+    @VisibleForTesting
+    public abstract boolean hasSwitch(String switchString);
+
+    /**
+     * Return the value associated with the given switch, or null.
+     * @param switchString The switch key to lookup. It should NOT start with '--' !
+     * @return switch value, or null if the switch is not set or set to empty.
+     */
+    public abstract String getSwitchValue(String switchString);
+
+    /**
+     * Return the value associated with the given switch, or {@code defaultValue} if the switch
+     * was not specified.
+     * @param switchString The switch key to lookup. It should NOT start with '--' !
+     * @param defaultValue The default value to return if the switch isn't set.
+     * @return Switch value, or {@code defaultValue} if the switch is not set or set to empty.
+     */
+    public String getSwitchValue(String switchString, String defaultValue) {
+        String value = getSwitchValue(switchString);
+        return TextUtils.isEmpty(value) ? defaultValue : value;
+    }
+
+    /**
+     * Append a switch to the command line.  There is no guarantee
+     * this action happens before the switch is needed.
+     * @param switchString the switch to add.  It should NOT start with '--' !
+     */
+    @VisibleForTesting
+    public abstract void appendSwitch(String switchString);
+
+    /**
+     * Append a switch and value to the command line.  There is no
+     * guarantee this action happens before the switch is needed.
+     * @param switchString the switch to add.  It should NOT start with '--' !
+     * @param value the value for this switch.
+     * For example, --foo=bar becomes 'foo', 'bar'.
+     */
+    public abstract void appendSwitchWithValue(String switchString, String value);
+
+    /**
+     * Append switch/value items in "command line" format (excluding argv[0] program name).
+     * E.g. { '--gofast', '--username=fred' }
+     * @param array an array of switch or switch/value items in command line format.
+     *   Unlike the other append routines, these switches SHOULD start with '--' .
+     *   Unlike init(), this does not include the program name in array[0].
+     */
+    public abstract void appendSwitchesAndArguments(String[] array);
+
+    /**
+     * Determine if the command line is bound to the native (JNI) implementation.
+     * @return true if the underlying implementation is delegating to the native command line.
+     */
+    public boolean isNativeImplementation() {
+        return false;
+    }
+
+    /**
+     * Returns the switches and arguments passed into the program, with switches and their
+     * values coming before all of the arguments.
+     */
+    protected abstract String[] getCommandLineArguments();
+
+    /**
+     * Destroy the command line. Called when a different instance is set.
+     * @see #setInstance
+     */
+    protected void destroy() {}
+
+    private static final AtomicReference<CommandLine> sCommandLine =
+            new AtomicReference<CommandLine>();
+
+    /**
+     * @return true if the command line has already been initialized.
+     */
+    public static boolean isInitialized() {
+        return sCommandLine.get() != null;
+    }
+
+    // Equivalent to CommandLine::ForCurrentProcess in C++.
+    @VisibleForTesting
+    public static CommandLine getInstance() {
+        CommandLine commandLine = sCommandLine.get();
+        assert commandLine != null;
+        return commandLine;
+    }
+
+    /**
+     * Initialize the singleton instance, must be called exactly once (either directly or
+     * via one of the convenience wrappers below) before using the static singleton instance.
+     * @param args command line flags in 'argv' format: args[0] is the program name.
+     */
+    public static void init(@Nullable String[] args) {
+        setInstance(new JavaCommandLine(args));
+    }
+
+    /**
+     * Initialize the command line from the command-line file.
+     *
+     * @param file The fully qualified command line file.
+     */
+    public static void initFromFile(String file) {
+        char[] buffer = readFileAsUtf8(file);
+        init(buffer == null ? null : tokenizeQuotedArguments(buffer));
+    }
+
+    /**
+     * Resets both the java proxy and the native command lines. This allows the entire
+     * command line initialization to be re-run including the call to onJniLoaded.
+     */
+    @VisibleForTesting
+    public static void reset() {
+        setInstance(null);
+    }
+
+    /**
+     * Parse command line flags from a flat buffer, supporting double-quote enclosed strings
+     * containing whitespace. argv elements are derived by splitting the buffer on whitepace;
+     * double quote characters may enclose tokens containing whitespace; a double-quote literal
+     * may be escaped with back-slash. (Otherwise backslash is taken as a literal).
+     * @param buffer A command line in command line file format as described above.
+     * @return the tokenized arguments, suitable for passing to init().
+     */
+    @VisibleForTesting
+    static String[] tokenizeQuotedArguments(char[] buffer) {
+        // Just field trials can take up to 10K of command line.
+        if (buffer.length > 64 * 1024) {
+            // Check that our test runners are setting a reasonable number of flags.
+            throw new RuntimeException("Flags file too big: " + buffer.length);
+        }
+
+        ArrayList<String> args = new ArrayList<String>();
+        StringBuilder arg = null;
+        final char noQuote = '\0';
+        final char singleQuote = '\'';
+        final char doubleQuote = '"';
+        char currentQuote = noQuote;
+        for (char c : buffer) {
+            // Detect start or end of quote block.
+            if ((currentQuote == noQuote && (c == singleQuote || c == doubleQuote))
+                    || c == currentQuote) {
+                if (arg != null && arg.length() > 0 && arg.charAt(arg.length() - 1) == '\\') {
+                    // Last char was a backslash; pop it, and treat c as a literal.
+                    arg.setCharAt(arg.length() - 1, c);
+                } else {
+                    currentQuote = currentQuote == noQuote ? c : noQuote;
+                }
+            } else if (currentQuote == noQuote && Character.isWhitespace(c)) {
+                if (arg != null) {
+                    args.add(arg.toString());
+                    arg = null;
+                }
+            } else {
+                if (arg == null) arg = new StringBuilder();
+                arg.append(c);
+            }
+        }
+        if (arg != null) {
+            if (currentQuote != noQuote) {
+                Log.w(TAG, "Unterminated quoted string: " + arg);
+            }
+            args.add(arg.toString());
+        }
+        return args.toArray(new String[args.size()]);
+    }
+
+    private static final String TAG = "CommandLine";
+    private static final String SWITCH_PREFIX = "--";
+    private static final String SWITCH_TERMINATOR = SWITCH_PREFIX;
+    private static final String SWITCH_VALUE_SEPARATOR = "=";
+
+    public static void enableNativeProxy() {
+        // Make a best-effort to ensure we make a clean (atomic) switch over from the old to
+        // the new command line implementation. If another thread is modifying the command line
+        // when this happens, all bets are off. (As per the native CommandLine).
+        sCommandLine.set(new NativeCommandLine(getJavaSwitchesOrNull()));
+    }
+
+    @Nullable
+    public static String[] getJavaSwitchesOrNull() {
+        CommandLine commandLine = sCommandLine.get();
+        if (commandLine != null) {
+            return commandLine.getCommandLineArguments();
+        }
+        return null;
+    }
+
+    private static void setInstance(CommandLine commandLine) {
+        CommandLine oldCommandLine = sCommandLine.getAndSet(commandLine);
+        if (oldCommandLine != null) {
+            oldCommandLine.destroy();
+        }
+    }
+
+    /**
+     * @param fileName the file to read in.
+     * @return Array of chars read from the file, or null if the file cannot be read.
+     */
+    private static char[] readFileAsUtf8(String fileName) {
+        File f = new File(fileName);
+        try (FileReader reader = new FileReader(f)) {
+            char[] buffer = new char[(int) f.length()];
+            int charsRead = reader.read(buffer);
+            // charsRead < f.length() in the case of multibyte characters.
+            return Arrays.copyOfRange(buffer, 0, charsRead);
+        } catch (IOException e) {
+            return null; // Most likely file not found.
+        }
+    }
+
+    private CommandLine() {}
+
+    private static class JavaCommandLine extends CommandLine {
+        private HashMap<String, String> mSwitches = new HashMap<String, String>();
+        private ArrayList<String> mArgs = new ArrayList<String>();
+
+        // The arguments begin at index 1, since index 0 contains the executable name.
+        private int mArgsBegin = 1;
+
+        JavaCommandLine(@Nullable String[] args) {
+            if (args == null || args.length == 0 || args[0] == null) {
+                mArgs.add("");
+            } else {
+                mArgs.add(args[0]);
+                appendSwitchesInternal(args, 1);
+            }
+            // Invariant: we always have the argv[0] program name element.
+            assert mArgs.size() > 0;
+        }
+
+        @Override
+        protected String[] getCommandLineArguments() {
+            return mArgs.toArray(new String[mArgs.size()]);
+        }
+
+        @Override
+        public boolean hasSwitch(String switchString) {
+            return mSwitches.containsKey(switchString);
+        }
+
+        @Override
+        public String getSwitchValue(String switchString) {
+            // This is slightly round about, but needed for consistency with the NativeCommandLine
+            // version which does not distinguish empty values from key not present.
+            String value = mSwitches.get(switchString);
+            return value == null || value.isEmpty() ? null : value;
+        }
+
+        @Override
+        public void appendSwitch(String switchString) {
+            appendSwitchWithValue(switchString, null);
+        }
+
+        /**
+         * Appends a switch to the current list.
+         * @param switchString the switch to add.  It should NOT start with '--' !
+         * @param value the value for this switch.
+         */
+        @Override
+        public void appendSwitchWithValue(String switchString, String value) {
+            mSwitches.put(switchString, value == null ? "" : value);
+
+            // Append the switch and update the switches/arguments divider mArgsBegin.
+            String combinedSwitchString = SWITCH_PREFIX + switchString;
+            if (value != null && !value.isEmpty()) {
+                combinedSwitchString += SWITCH_VALUE_SEPARATOR + value;
+            }
+
+            mArgs.add(mArgsBegin++, combinedSwitchString);
+        }
+
+        @Override
+        public void appendSwitchesAndArguments(String[] array) {
+            appendSwitchesInternal(array, 0);
+        }
+
+        // Add the specified arguments, but skipping the first |skipCount| elements.
+        private void appendSwitchesInternal(String[] array, int skipCount) {
+            boolean parseSwitches = true;
+            for (String arg : array) {
+                if (skipCount > 0) {
+                    --skipCount;
+                    continue;
+                }
+
+                if (arg.equals(SWITCH_TERMINATOR)) {
+                    parseSwitches = false;
+                }
+
+                if (parseSwitches && arg.startsWith(SWITCH_PREFIX)) {
+                    String[] parts = arg.split(SWITCH_VALUE_SEPARATOR, 2);
+                    String value = parts.length > 1 ? parts[1] : null;
+                    appendSwitchWithValue(parts[0].substring(SWITCH_PREFIX.length()), value);
+                } else {
+                    mArgs.add(arg);
+                }
+            }
+        }
+    }
+
+    private static class NativeCommandLine extends CommandLine {
+        public NativeCommandLine(@Nullable String[] args) {
+            nativeInit(args);
+        }
+
+        @Override
+        public boolean hasSwitch(String switchString) {
+            return nativeHasSwitch(switchString);
+        }
+
+        @Override
+        public String getSwitchValue(String switchString) {
+            return nativeGetSwitchValue(switchString);
+        }
+
+        @Override
+        public void appendSwitch(String switchString) {
+            nativeAppendSwitch(switchString);
+        }
+
+        @Override
+        public void appendSwitchWithValue(String switchString, String value) {
+            nativeAppendSwitchWithValue(switchString, value);
+        }
+
+        @Override
+        public void appendSwitchesAndArguments(String[] array) {
+            nativeAppendSwitchesAndArguments(array);
+        }
+
+        @Override
+        public boolean isNativeImplementation() {
+            return true;
+        }
+
+        @Override
+        protected String[] getCommandLineArguments() {
+            assert false;
+            return null;
+        }
+
+        @Override
+        protected void destroy() {
+            // TODO(https://crbug.com/771205): Downgrade this to an assert once we have eliminated
+            // tests that do this.
+            throw new IllegalStateException("Can't destroy native command line after startup");
+        }
+    }
+
+    private static native void nativeInit(String[] args);
+    private static native boolean nativeHasSwitch(String switchString);
+    private static native String nativeGetSwitchValue(String switchString);
+    private static native void nativeAppendSwitch(String switchString);
+    private static native void nativeAppendSwitchWithValue(String switchString, String value);
+    private static native void nativeAppendSwitchesAndArguments(String[] array);
+}
diff --git a/base/android/java/src/org/chromium/base/CommandLineInitUtil.java b/base/android/java/src/org/chromium/base/CommandLineInitUtil.java
new file mode 100644
index 0000000..e51b95d
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/CommandLineInitUtil.java
@@ -0,0 +1,103 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.os.Build;
+import android.provider.Settings;
+import android.support.annotation.Nullable;
+
+import java.io.File;
+
+/**
+ * Provides implementation of command line initialization for Android.
+ */
+public final class CommandLineInitUtil {
+    /**
+     * The location of the command line file needs to be in a protected
+     * directory so requires root access to be tweaked, i.e., no other app in a
+     * regular (non-rooted) device can change this file's contents.
+     * See below for debugging on a regular (non-rooted) device.
+     */
+    private static final String COMMAND_LINE_FILE_PATH = "/data/local";
+
+    /**
+     * This path (writable by the shell in regular non-rooted "user" builds) is used when:
+     * 1) The "debug app" is set to the application calling this.
+     * and
+     * 2) ADB is enabled.
+     * 3) Force enabled by the embedder.
+     */
+    private static final String COMMAND_LINE_FILE_PATH_DEBUG_APP = "/data/local/tmp";
+
+    private CommandLineInitUtil() {
+    }
+
+    /**
+     * Initializes the CommandLine class, pulling command line arguments from {@code fileName}.
+     * @param fileName The name of the command line file to pull arguments from.
+     */
+    public static void initCommandLine(String fileName) {
+        initCommandLine(fileName, null);
+    }
+
+    /**
+     * Initializes the CommandLine class, pulling command line arguments from {@code fileName}.
+     * @param fileName The name of the command line file to pull arguments from.
+     * @param shouldUseDebugFlags If non-null, returns whether debug flags are allowed to be used.
+     */
+    public static void initCommandLine(
+            String fileName, @Nullable Supplier<Boolean> shouldUseDebugFlags) {
+        assert !CommandLine.isInitialized();
+        File commandLineFile = new File(COMMAND_LINE_FILE_PATH_DEBUG_APP, fileName);
+        // shouldUseDebugCommandLine() uses IPC, so don't bother calling it if no flags file exists.
+        boolean debugFlagsExist = commandLineFile.exists();
+        if (!debugFlagsExist || !shouldUseDebugCommandLine(shouldUseDebugFlags)) {
+            commandLineFile = new File(COMMAND_LINE_FILE_PATH, fileName);
+        }
+        CommandLine.initFromFile(commandLineFile.getPath());
+    }
+
+    /**
+     * Use an alternative path if:
+     * - The current build is "eng" or "userdebug", OR
+     * - adb is enabled and this is the debug app, OR
+     * - Force enabled by the embedder.
+     * @param shouldUseDebugFlags If non-null, returns whether debug flags are allowed to be used.
+     */
+    private static boolean shouldUseDebugCommandLine(
+            @Nullable Supplier<Boolean> shouldUseDebugFlags) {
+        if (shouldUseDebugFlags != null && shouldUseDebugFlags.get()) return true;
+        Context context = ContextUtils.getApplicationContext();
+        String debugApp = Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1
+                ? getDebugAppPreJBMR1(context)
+                : getDebugAppJBMR1(context);
+        // Check isDebugAndroid() last to get full code coverage when using userdebug devices.
+        return context.getPackageName().equals(debugApp) || BuildInfo.isDebugAndroid();
+    }
+
+    @SuppressLint("NewApi")
+    private static String getDebugAppJBMR1(Context context) {
+        boolean adbEnabled = Settings.Global.getInt(context.getContentResolver(),
+                Settings.Global.ADB_ENABLED, 0) == 1;
+        if (adbEnabled) {
+            return Settings.Global.getString(context.getContentResolver(),
+                    Settings.Global.DEBUG_APP);
+        }
+        return null;
+    }
+
+    @SuppressWarnings("deprecation")
+    private static String getDebugAppPreJBMR1(Context context) {
+        boolean adbEnabled = Settings.System.getInt(context.getContentResolver(),
+                Settings.System.ADB_ENABLED, 0) == 1;
+        if (adbEnabled) {
+            return Settings.System.getString(context.getContentResolver(),
+                    Settings.System.DEBUG_APP);
+        }
+        return null;
+    }
+}
diff --git a/base/android/java/src/org/chromium/base/ContentUriUtils.java b/base/android/java/src/org/chromium/base/ContentUriUtils.java
new file mode 100644
index 0000000..ba92a56
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/ContentUriUtils.java
@@ -0,0 +1,251 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.res.AssetFileDescriptor;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Build;
+import android.os.ParcelFileDescriptor;
+import android.provider.DocumentsContract;
+import android.util.Log;
+import android.webkit.MimeTypeMap;
+
+import org.chromium.base.annotations.CalledByNative;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+/**
+ * This class provides methods to access content URI schemes.
+ */
+public abstract class ContentUriUtils {
+    private static final String TAG = "ContentUriUtils";
+    private static FileProviderUtil sFileProviderUtil;
+
+    // Guards access to sFileProviderUtil.
+    private static final Object sLock = new Object();
+
+    /**
+     * Provides functionality to translate a file into a content URI for use
+     * with a content provider.
+     */
+    public interface FileProviderUtil {
+        /**
+         * Generate a content URI from the given file.
+         *
+         * @param file The file to be translated.
+         */
+        Uri getContentUriFromFile(File file);
+    }
+
+    // Prevent instantiation.
+    private ContentUriUtils() {}
+
+    public static void setFileProviderUtil(FileProviderUtil util) {
+        synchronized (sLock) {
+            sFileProviderUtil = util;
+        }
+    }
+
+    public static Uri getContentUriFromFile(File file) {
+        synchronized (sLock) {
+            if (sFileProviderUtil != null) {
+                return sFileProviderUtil.getContentUriFromFile(file);
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Opens the content URI for reading, and returns the file descriptor to
+     * the caller. The caller is responsible for closing the file descriptor.
+     *
+     * @param uriString the content URI to open
+     * @return file descriptor upon success, or -1 otherwise.
+     */
+    @CalledByNative
+    public static int openContentUriForRead(String uriString) {
+        AssetFileDescriptor afd = getAssetFileDescriptor(uriString);
+        if (afd != null) {
+            return afd.getParcelFileDescriptor().detachFd();
+        }
+        return -1;
+    }
+
+    /**
+     * Check whether a content URI exists.
+     *
+     * @param uriString the content URI to query.
+     * @return true if the URI exists, or false otherwise.
+     */
+    @CalledByNative
+    public static boolean contentUriExists(String uriString) {
+        AssetFileDescriptor asf = null;
+        try {
+            asf = getAssetFileDescriptor(uriString);
+            return asf != null;
+        } finally {
+            // Do not use StreamUtil.closeQuietly here, as AssetFileDescriptor
+            // does not implement Closeable until KitKat.
+            if (asf != null) {
+                try {
+                    asf.close();
+                } catch (IOException e) {
+                    // Closing quietly.
+                }
+            }
+        }
+    }
+
+    /**
+     * Retrieve the MIME type for the content URI.
+     *
+     * @param uriString the content URI to look up.
+     * @return MIME type or null if the input params are empty or invalid.
+     */
+    @CalledByNative
+    public static String getMimeType(String uriString) {
+        ContentResolver resolver = ContextUtils.getApplicationContext().getContentResolver();
+        Uri uri = Uri.parse(uriString);
+        if (isVirtualDocument(uri)) {
+            String[] streamTypes = resolver.getStreamTypes(uri, "*/*");
+            return (streamTypes != null && streamTypes.length > 0) ? streamTypes[0] : null;
+        }
+        return resolver.getType(uri);
+    }
+
+    /**
+     * Helper method to open a content URI and returns the ParcelFileDescriptor.
+     *
+     * @param uriString the content URI to open.
+     * @return AssetFileDescriptor of the content URI, or NULL if the file does not exist.
+     */
+    private static AssetFileDescriptor getAssetFileDescriptor(String uriString) {
+        ContentResolver resolver = ContextUtils.getApplicationContext().getContentResolver();
+        Uri uri = Uri.parse(uriString);
+
+        try {
+            if (isVirtualDocument(uri)) {
+                String[] streamTypes = resolver.getStreamTypes(uri, "*/*");
+                if (streamTypes != null && streamTypes.length > 0) {
+                    AssetFileDescriptor afd =
+                            resolver.openTypedAssetFileDescriptor(uri, streamTypes[0], null);
+                    if (afd != null && afd.getStartOffset() != 0) {
+                        // Do not use StreamUtil.closeQuietly here, as AssetFileDescriptor
+                        // does not implement Closeable until KitKat.
+                        try {
+                            afd.close();
+                        } catch (IOException e) {
+                            // Closing quietly.
+                        }
+                        throw new SecurityException("Cannot open files with non-zero offset type.");
+                    }
+                    return afd;
+                }
+            } else {
+                ParcelFileDescriptor pfd = resolver.openFileDescriptor(uri, "r");
+                if (pfd != null) {
+                    return new AssetFileDescriptor(pfd, 0, AssetFileDescriptor.UNKNOWN_LENGTH);
+                }
+            }
+        } catch (FileNotFoundException e) {
+            Log.w(TAG, "Cannot find content uri: " + uriString, e);
+        } catch (SecurityException e) {
+            Log.w(TAG, "Cannot open content uri: " + uriString, e);
+        } catch (Exception e) {
+            Log.w(TAG, "Unknown content uri: " + uriString, e);
+        }
+        return null;
+    }
+
+    /**
+     * Method to resolve the display name of a content URI.
+     *
+     * @param uri         the content URI to be resolved.
+     * @param context     {@link Context} in interest.
+     * @param columnField the column field to query.
+     * @return the display name of the @code uri if present in the database
+     * or an empty string otherwise.
+     */
+    public static String getDisplayName(Uri uri, Context context, String columnField) {
+        if (uri == null) return "";
+        ContentResolver contentResolver = context.getContentResolver();
+        try (Cursor cursor = contentResolver.query(uri, null, null, null, null)) {
+            if (cursor != null && cursor.getCount() >= 1) {
+                cursor.moveToFirst();
+                int displayNameIndex = cursor.getColumnIndex(columnField);
+                if (displayNameIndex == -1) {
+                    return "";
+                }
+                String displayName = cursor.getString(displayNameIndex);
+                // For Virtual documents, try to modify the file extension so it's compatible
+                // with the alternative MIME type.
+                if (hasVirtualFlag(cursor)) {
+                    String[] mimeTypes = contentResolver.getStreamTypes(uri, "*/*");
+                    if (mimeTypes != null && mimeTypes.length > 0) {
+                        String ext =
+                                MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeTypes[0]);
+                        if (ext != null) {
+                            // Just append, it's simpler and more secure than altering an
+                            // existing extension.
+                            displayName += "." + ext;
+                        }
+                    }
+                }
+                return displayName;
+            }
+        } catch (NullPointerException e) {
+            // Some android models don't handle the provider call correctly.
+            // see crbug.com/345393
+            return "";
+        }
+        return "";
+    }
+
+    /**
+     * Checks whether the passed Uri represents a virtual document.
+     *
+     * @param uri the content URI to be resolved.
+     * @return True for virtual file, false for any other file.
+     */
+    private static boolean isVirtualDocument(Uri uri) {
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return false;
+        if (uri == null) return false;
+        if (!DocumentsContract.isDocumentUri(ContextUtils.getApplicationContext(), uri)) {
+            return false;
+        }
+        ContentResolver contentResolver = ContextUtils.getApplicationContext().getContentResolver();
+        try (Cursor cursor = contentResolver.query(uri, null, null, null, null)) {
+            if (cursor != null && cursor.getCount() >= 1) {
+                cursor.moveToFirst();
+                return hasVirtualFlag(cursor);
+            }
+        } catch (NullPointerException e) {
+            // Some android models don't handle the provider call correctly.
+            // see crbug.com/345393
+            return false;
+        }
+        return false;
+    }
+
+    /**
+     * Checks whether the passed cursor for a document has a virtual document flag.
+     *
+     * The called must close the passed cursor.
+     *
+     * @param cursor Cursor with COLUMN_FLAGS.
+     * @return True for virtual file, false for any other file.
+     */
+    private static boolean hasVirtualFlag(Cursor cursor) {
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) return false;
+        int index = cursor.getColumnIndex(DocumentsContract.Document.COLUMN_FLAGS);
+        return index > -1
+                && (cursor.getLong(index) & DocumentsContract.Document.FLAG_VIRTUAL_DOCUMENT) != 0;
+    }
+}
diff --git a/base/android/java/src/org/chromium/base/CpuFeatures.java b/base/android/java/src/org/chromium/base/CpuFeatures.java
new file mode 100644
index 0000000..ae4969c
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/CpuFeatures.java
@@ -0,0 +1,42 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base;
+
+import org.chromium.base.annotations.JNINamespace;
+
+// The only purpose of this class is to allow sending CPU properties
+// from the browser process to sandboxed renderer processes. This is
+// needed because sandboxed processes cannot, on ARM, query the kernel
+// about the CPU's properties by parsing /proc, so this operation must
+// be performed in the browser process, and the result passed to
+// renderer ones.
+//
+// For more context, see http://crbug.com/164154
+//
+// Technically, this is a wrapper around the native NDK cpufeatures
+// library. The exact CPU features bits are never used in Java so
+// there is no point in duplicating their definitions here.
+//
+@JNINamespace("base::android")
+public abstract class CpuFeatures {
+    /**
+     * Return the number of CPU Cores on the device.
+     */
+    public static int getCount() {
+        return nativeGetCoreCount();
+    }
+
+    /**
+     * Return the CPU feature mask.
+     * This is a 64-bit integer that corresponds to the CPU's features.
+     * The value comes directly from android_getCpuFeatures().
+     */
+    public static long getMask() {
+        return nativeGetCpuFeatures();
+    }
+
+    private static native int nativeGetCoreCount();
+    private static native long nativeGetCpuFeatures();
+}
diff --git a/base/android/java/src/org/chromium/base/EarlyTraceEvent.java b/base/android/java/src/org/chromium/base/EarlyTraceEvent.java
new file mode 100644
index 0000000..0f64fc2
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/EarlyTraceEvent.java
@@ -0,0 +1,299 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base;
+
+import android.annotation.SuppressLint;
+import android.os.Build;
+import android.os.Process;
+import android.os.StrictMode;
+import android.os.SystemClock;
+
+import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.MainDex;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/** Support for early tracing, before the native library is loaded.
+ *
+ * This is limited, as:
+ * - Arguments are not supported
+ * - Thread time is not reported
+ * - Two events with the same name cannot be in progress at the same time.
+ *
+ * Events recorded here are buffered in Java until the native library is available. Then it waits
+ * for the completion of pending events, and sends the events to the native side.
+ *
+ * Locking: This class is threadsafe. It is enabled when general tracing is, and then disabled when
+ *          tracing is enabled from the native side. Event completions are still processed as long
+ *          as some are pending, then early tracing is permanently disabled after dumping the
+ *          events.  This means that if any early event is still pending when tracing is disabled,
+ *          all early events are dropped.
+ */
+@JNINamespace("base::android")
+@MainDex
+public class EarlyTraceEvent {
+    // Must be kept in sync with the native kAndroidTraceConfigFile.
+    private static final String TRACE_CONFIG_FILENAME = "/data/local/chrome-trace-config.json";
+
+    /** Single trace event. */
+    @VisibleForTesting
+    static final class Event {
+        final String mName;
+        final int mThreadId;
+        final long mBeginTimeNanos;
+        final long mBeginThreadTimeMillis;
+        long mEndTimeNanos;
+        long mEndThreadTimeMillis;
+
+        Event(String name) {
+            mName = name;
+            mThreadId = Process.myTid();
+            mBeginTimeNanos = elapsedRealtimeNanos();
+            mBeginThreadTimeMillis = SystemClock.currentThreadTimeMillis();
+        }
+
+        void end() {
+            assert mEndTimeNanos == 0;
+            assert mEndThreadTimeMillis == 0;
+            mEndTimeNanos = elapsedRealtimeNanos();
+            mEndThreadTimeMillis = SystemClock.currentThreadTimeMillis();
+        }
+
+        @VisibleForTesting
+        @SuppressLint("NewApi")
+        static long elapsedRealtimeNanos() {
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+                return SystemClock.elapsedRealtimeNanos();
+            } else {
+                return SystemClock.elapsedRealtime() * 1000000;
+            }
+        }
+    }
+
+    @VisibleForTesting
+    static final class AsyncEvent {
+        final boolean mIsStart;
+        final String mName;
+        final long mId;
+        final long mTimestampNanos;
+
+        AsyncEvent(String name, long id, boolean isStart) {
+            mName = name;
+            mId = id;
+            mIsStart = isStart;
+            mTimestampNanos = Event.elapsedRealtimeNanos();
+        }
+    }
+
+    // State transitions are:
+    // - enable(): DISABLED -> ENABLED
+    // - disable(): ENABLED -> FINISHING
+    // - Once there are no pending events: FINISHING -> FINISHED.
+    @VisibleForTesting static final int STATE_DISABLED = 0;
+    @VisibleForTesting static final int STATE_ENABLED = 1;
+    @VisibleForTesting static final int STATE_FINISHING = 2;
+    @VisibleForTesting static final int STATE_FINISHED = 3;
+
+    // Locks the fields below.
+    private static final Object sLock = new Object();
+
+    @VisibleForTesting static volatile int sState = STATE_DISABLED;
+    // Not final as these object are not likely to be used at all.
+    @VisibleForTesting static List<Event> sCompletedEvents;
+    @VisibleForTesting
+    static Map<String, Event> sPendingEventByKey;
+    @VisibleForTesting static List<AsyncEvent> sAsyncEvents;
+    @VisibleForTesting static List<String> sPendingAsyncEvents;
+
+    /** @see TraceEvent#MaybeEnableEarlyTracing().
+     */
+    static void maybeEnable() {
+        ThreadUtils.assertOnUiThread();
+        boolean shouldEnable = false;
+        // Checking for the trace config filename touches the disk.
+        StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
+        try {
+            if (CommandLine.getInstance().hasSwitch("trace-startup")) {
+                shouldEnable = true;
+            } else {
+                try {
+                    shouldEnable = (new File(TRACE_CONFIG_FILENAME)).exists();
+                } catch (SecurityException e) {
+                    // Access denied, not enabled.
+                }
+            }
+        } finally {
+            StrictMode.setThreadPolicy(oldPolicy);
+        }
+        if (shouldEnable) enable();
+    }
+
+    @VisibleForTesting
+    static void enable() {
+        synchronized (sLock) {
+            if (sState != STATE_DISABLED) return;
+            sCompletedEvents = new ArrayList<Event>();
+            sPendingEventByKey = new HashMap<String, Event>();
+            sAsyncEvents = new ArrayList<AsyncEvent>();
+            sPendingAsyncEvents = new ArrayList<String>();
+            sState = STATE_ENABLED;
+        }
+    }
+
+    /**
+     * Disables Early tracing.
+     *
+     * Once this is called, no new event will be registered. However, end() calls are still recorded
+     * as long as there are pending events. Once there are none left, pass the events to the native
+     * side.
+     */
+    static void disable() {
+        synchronized (sLock) {
+            if (!enabled()) return;
+            sState = STATE_FINISHING;
+            maybeFinishLocked();
+        }
+    }
+
+    /**
+     * Returns whether early tracing is currently active.
+     *
+     * Active means that Early Tracing is either enabled or waiting to complete pending events.
+     */
+    static boolean isActive() {
+        int state = sState;
+        return (state == STATE_ENABLED || state == STATE_FINISHING);
+    }
+
+    static boolean enabled() {
+        return sState == STATE_ENABLED;
+    }
+
+    /** @see {@link TraceEvent#begin()}. */
+    public static void begin(String name) {
+        // begin() and end() are going to be called once per TraceEvent, this avoids entering a
+        // synchronized block at each and every call.
+        if (!enabled()) return;
+        Event event = new Event(name);
+        Event conflictingEvent;
+        synchronized (sLock) {
+            if (!enabled()) return;
+            conflictingEvent = sPendingEventByKey.put(makeEventKeyForCurrentThread(name), event);
+        }
+        if (conflictingEvent != null) {
+            throw new IllegalArgumentException(
+                    "Multiple pending trace events can't have the same name");
+        }
+    }
+
+    /** @see {@link TraceEvent#end()}. */
+    public static void end(String name) {
+        if (!isActive()) return;
+        synchronized (sLock) {
+            if (!isActive()) return;
+            Event event = sPendingEventByKey.remove(makeEventKeyForCurrentThread(name));
+            if (event == null) return;
+            event.end();
+            sCompletedEvents.add(event);
+            if (sState == STATE_FINISHING) maybeFinishLocked();
+        }
+    }
+
+    /** @see {@link TraceEvent#startAsync()}. */
+    public static void startAsync(String name, long id) {
+        if (!enabled()) return;
+        AsyncEvent event = new AsyncEvent(name, id, true /*isStart*/);
+        synchronized (sLock) {
+            if (!enabled()) return;
+            sAsyncEvents.add(event);
+            sPendingAsyncEvents.add(name);
+        }
+    }
+
+    /** @see {@link TraceEvent#finishAsync()}. */
+    public static void finishAsync(String name, long id) {
+        if (!isActive()) return;
+        AsyncEvent event = new AsyncEvent(name, id, false /*isStart*/);
+        synchronized (sLock) {
+            if (!isActive()) return;
+            if (!sPendingAsyncEvents.remove(name)) return;
+            sAsyncEvents.add(event);
+            if (sState == STATE_FINISHING) maybeFinishLocked();
+        }
+    }
+
+    @VisibleForTesting
+    static void resetForTesting() {
+        sState = EarlyTraceEvent.STATE_DISABLED;
+        sCompletedEvents = null;
+        sPendingEventByKey = null;
+        sAsyncEvents = null;
+        sPendingAsyncEvents = null;
+    }
+
+    private static void maybeFinishLocked() {
+        if (!sCompletedEvents.isEmpty()) {
+            dumpEvents(sCompletedEvents);
+            sCompletedEvents.clear();
+        }
+        if (!sAsyncEvents.isEmpty()) {
+            dumpAsyncEvents(sAsyncEvents);
+            sAsyncEvents.clear();
+        }
+        if (sPendingEventByKey.isEmpty() && sPendingAsyncEvents.isEmpty()) {
+            sState = STATE_FINISHED;
+            sPendingEventByKey = null;
+            sCompletedEvents = null;
+            sPendingAsyncEvents = null;
+            sAsyncEvents = null;
+        }
+    }
+
+    private static void dumpEvents(List<Event> events) {
+        long offsetNanos = getOffsetNanos();
+        for (Event e : events) {
+            nativeRecordEarlyEvent(e.mName, e.mBeginTimeNanos + offsetNanos,
+                    e.mEndTimeNanos + offsetNanos, e.mThreadId,
+                    e.mEndThreadTimeMillis - e.mBeginThreadTimeMillis);
+        }
+    }
+    private static void dumpAsyncEvents(List<AsyncEvent> events) {
+        long offsetNanos = getOffsetNanos();
+        for (AsyncEvent e : events) {
+            if (e.mIsStart) {
+                nativeRecordEarlyStartAsyncEvent(e.mName, e.mId, e.mTimestampNanos + offsetNanos);
+            } else {
+                nativeRecordEarlyFinishAsyncEvent(e.mName, e.mId, e.mTimestampNanos + offsetNanos);
+            }
+        }
+    }
+
+    private static long getOffsetNanos() {
+        long nativeNowNanos = TimeUtils.nativeGetTimeTicksNowUs() * 1000;
+        long javaNowNanos = Event.elapsedRealtimeNanos();
+        return nativeNowNanos - javaNowNanos;
+    }
+
+    /**
+     * Returns a key which consists of |name| and the ID of the current thread.
+     * The key is used with pending events making them thread-specific, thus avoiding
+     * an exception when similarly named events are started from multiple threads.
+     */
+    @VisibleForTesting
+    static String makeEventKeyForCurrentThread(String name) {
+        return name + "@" + Process.myTid();
+    }
+
+    private static native void nativeRecordEarlyEvent(String name, long beginTimNanos,
+            long endTimeNanos, int threadId, long threadDurationMillis);
+    private static native void nativeRecordEarlyStartAsyncEvent(
+            String name, long id, long timestamp);
+    private static native void nativeRecordEarlyFinishAsyncEvent(
+            String name, long id, long timestamp);
+}
diff --git a/base/android/java/src/org/chromium/base/EventLog.java b/base/android/java/src/org/chromium/base/EventLog.java
new file mode 100644
index 0000000..f889175
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/EventLog.java
@@ -0,0 +1,20 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base;
+
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.JNINamespace;
+
+/**
+ * A simple interface to Android's EventLog to be used by native code.
+ */
+@JNINamespace("base::android")
+public class EventLog {
+
+    @CalledByNative
+    public static void writeEvent(int tag, int value) {
+        android.util.EventLog.writeEvent(tag, value);
+    }
+}
diff --git a/base/android/java/src/org/chromium/base/FieldTrialList.java b/base/android/java/src/org/chromium/base/FieldTrialList.java
new file mode 100644
index 0000000..c3468a4
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/FieldTrialList.java
@@ -0,0 +1,46 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base;
+
+import org.chromium.base.annotations.MainDex;
+
+/**
+ * Helper to get field trial information.
+ */
+@MainDex
+public class FieldTrialList {
+
+    private FieldTrialList() {}
+
+    /**
+     * @param trialName The name of the trial to get the group for.
+     * @return The group name chosen for the named trial, or the empty string if the trial does
+     *         not exist.
+     */
+    public static String findFullName(String trialName) {
+        return nativeFindFullName(trialName);
+    }
+
+    /**
+     * @param trialName The name of the trial to get the group for.
+     * @return Whether the trial exists or not.
+     */
+    public static boolean trialExists(String trialName) {
+        return nativeTrialExists(trialName);
+    }
+
+    /**
+     * @param trialName    The name of the trial with the parameter.
+     * @param parameterKey The key of the parameter.
+     * @return The value of the parameter or an empty string if not found.
+     */
+    public static String getVariationParameter(String trialName, String parameterKey) {
+        return nativeGetVariationParameter(trialName, parameterKey);
+    }
+
+    private static native String nativeFindFullName(String trialName);
+    private static native boolean nativeTrialExists(String trialName);
+    private static native String nativeGetVariationParameter(String trialName, String parameterKey);
+}
diff --git a/base/android/java/src/org/chromium/base/FileUtils.java b/base/android/java/src/org/chromium/base/FileUtils.java
new file mode 100644
index 0000000..e44cd92
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/FileUtils.java
@@ -0,0 +1,149 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base;
+
+import android.content.Context;
+import android.net.Uri;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * Helper methods for dealing with Files.
+ */
+public class FileUtils {
+    private static final String TAG = "FileUtils";
+
+    /**
+     * Delete the given File and (if it's a directory) everything within it.
+     */
+    public static void recursivelyDeleteFile(File currentFile) {
+        ThreadUtils.assertOnBackgroundThread();
+        if (currentFile.isDirectory()) {
+            File[] files = currentFile.listFiles();
+            if (files != null) {
+                for (File file : files) {
+                    recursivelyDeleteFile(file);
+                }
+            }
+        }
+
+        if (!currentFile.delete()) Log.e(TAG, "Failed to delete: " + currentFile);
+    }
+
+    /**
+     * Delete the given files or directories by calling {@link #recursivelyDeleteFile(File)}.
+     * @param files The files to delete.
+     */
+    public static void batchDeleteFiles(List<File> files) {
+        ThreadUtils.assertOnBackgroundThread();
+
+        for (File file : files) {
+            if (file.exists()) recursivelyDeleteFile(file);
+        }
+    }
+
+    /**
+     * Extracts an asset from the app's APK to a file.
+     * @param context
+     * @param assetName Name of the asset to extract.
+     * @param dest File to extract the asset to.
+     * @return true on success.
+     */
+    public static boolean extractAsset(Context context, String assetName, File dest) {
+        InputStream inputStream = null;
+        OutputStream outputStream = null;
+        try {
+            inputStream = context.getAssets().open(assetName);
+            outputStream = new BufferedOutputStream(new FileOutputStream(dest));
+            byte[] buffer = new byte[8192];
+            int c;
+            while ((c = inputStream.read(buffer)) != -1) {
+                outputStream.write(buffer, 0, c);
+            }
+            inputStream.close();
+            outputStream.close();
+            return true;
+        } catch (IOException e) {
+            if (inputStream != null) {
+                try {
+                    inputStream.close();
+                } catch (IOException ex) {
+                }
+            }
+            if (outputStream != null) {
+                try {
+                    outputStream.close();
+                } catch (IOException ex) {
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Atomically copies the data from an input stream into an output file.
+     * @param is Input file stream to read data from.
+     * @param outFile Output file path.
+     * @param buffer Caller-provided buffer. Provided to avoid allocating the same
+     *         buffer on each call when copying several files in sequence.
+     * @throws IOException in case of I/O error.
+     */
+    public static void copyFileStreamAtomicWithBuffer(InputStream is, File outFile, byte[] buffer)
+            throws IOException {
+        File tmpOutputFile = new File(outFile.getPath() + ".tmp");
+        try (OutputStream os = new FileOutputStream(tmpOutputFile)) {
+            Log.i(TAG, "Writing to %s", outFile);
+
+            int count = 0;
+            while ((count = is.read(buffer, 0, buffer.length)) != -1) {
+                os.write(buffer, 0, count);
+            }
+        }
+        if (!tmpOutputFile.renameTo(outFile)) {
+            throw new IOException();
+        }
+    }
+
+    /**
+     * Returns a URI that points at the file.
+     * @param file File to get a URI for.
+     * @return URI that points at that file, either as a content:// URI or a file:// URI.
+     */
+    public static Uri getUriForFile(File file) {
+        // TODO(crbug/709584): Uncomment this when http://crbug.com/709584 has been fixed.
+        // assert !ThreadUtils.runningOnUiThread();
+        Uri uri = null;
+
+        try {
+            // Try to obtain a content:// URI, which is preferred to a file:// URI so that
+            // receiving apps don't attempt to determine the file's mime type (which often fails).
+            uri = ContentUriUtils.getContentUriFromFile(file);
+        } catch (IllegalArgumentException e) {
+            Log.e(TAG, "Could not create content uri: " + e);
+        }
+
+        if (uri == null) uri = Uri.fromFile(file);
+
+        return uri;
+    }
+
+    /**
+     * Returns the file extension, or an empty string if none.
+     * @param file Name of the file, with or without the full path.
+     * @return empty string if no extension, extension otherwise.
+     */
+    public static String getExtension(String file) {
+        int index = file.lastIndexOf('.');
+        if (index == -1) return "";
+        return file.substring(index + 1).toLowerCase(Locale.US);
+    }
+}
diff --git a/base/android/java/src/org/chromium/base/ImportantFileWriterAndroid.java b/base/android/java/src/org/chromium/base/ImportantFileWriterAndroid.java
new file mode 100644
index 0000000..cbaf7f7
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/ImportantFileWriterAndroid.java
@@ -0,0 +1,31 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base;
+
+import org.chromium.base.annotations.JNINamespace;
+
+/**
+ * This class provides an interface to the native class for writing
+ * important data files without risking data loss.
+ */
+@JNINamespace("base::android")
+public class ImportantFileWriterAndroid {
+
+    /**
+     * Write a binary file atomically.
+     *
+     * This either writes all the data or leaves the file unchanged.
+     *
+     * @param fileName The complete path of the file to be written
+     * @param data The data to be written to the file
+     * @return true if the data was written to the file, false if not.
+     */
+    public static boolean writeFileAtomically(String fileName, byte[] data) {
+        return nativeWriteFileAtomically(fileName, data);
+    }
+
+    private static native boolean nativeWriteFileAtomically(
+            String fileName, byte[] data);
+}
diff --git a/base/android/java/src/org/chromium/base/JNIUtils.java b/base/android/java/src/org/chromium/base/JNIUtils.java
new file mode 100644
index 0000000..3fcec91
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/JNIUtils.java
@@ -0,0 +1,46 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base;
+
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.MainDex;
+
+/**
+ * This class provides JNI-related methods to the native library.
+ */
+@MainDex
+public class JNIUtils {
+    private static Boolean sSelectiveJniRegistrationEnabled;
+
+    /**
+     * This returns a ClassLoader that is capable of loading Chromium Java code. Such a ClassLoader
+     * is needed for the few cases where the JNI mechanism is unable to automatically determine the
+     * appropriate ClassLoader instance.
+     */
+    @CalledByNative
+    public static Object getClassLoader() {
+        return JNIUtils.class.getClassLoader();
+    }
+
+    /**
+     * @return whether or not the current process supports selective JNI registration.
+     */
+    @CalledByNative
+    public static boolean isSelectiveJniRegistrationEnabled() {
+        if (sSelectiveJniRegistrationEnabled == null) {
+            sSelectiveJniRegistrationEnabled = false;
+        }
+        return sSelectiveJniRegistrationEnabled;
+    }
+
+    /**
+     * Allow this process to selectively perform JNI registration. This must be called before
+     * loading native libraries or it will have no effect.
+     */
+    public static void enableSelectiveJniRegistration() {
+        assert sSelectiveJniRegistrationEnabled == null;
+        sSelectiveJniRegistrationEnabled = true;
+    }
+}
diff --git a/base/android/java/src/org/chromium/base/JavaHandlerThread.java b/base/android/java/src/org/chromium/base/JavaHandlerThread.java
new file mode 100644
index 0000000..9a1c924
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/JavaHandlerThread.java
@@ -0,0 +1,119 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base;
+
+import android.os.Build;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.MainDex;
+
+import java.lang.Thread.UncaughtExceptionHandler;
+
+/**
+ * Thread in Java with an Android Handler. This class is not thread safe.
+ */
+@JNINamespace("base::android")
+@MainDex
+public class JavaHandlerThread {
+    private final HandlerThread mThread;
+
+    private Throwable mUnhandledException;
+
+    /**
+     * Construct a java-only instance. Can be connected with native side later.
+     * Useful for cases where a java thread is needed before native library is loaded.
+     */
+    public JavaHandlerThread(String name, int priority) {
+        mThread = new HandlerThread(name, priority);
+    }
+
+    @CalledByNative
+    private static JavaHandlerThread create(String name, int priority) {
+        return new JavaHandlerThread(name, priority);
+    }
+
+    public Looper getLooper() {
+        assert hasStarted();
+        return mThread.getLooper();
+    }
+
+    public void maybeStart() {
+        if (hasStarted()) return;
+        mThread.start();
+    }
+
+    @CalledByNative
+    private void startAndInitialize(final long nativeThread, final long nativeEvent) {
+        maybeStart();
+        new Handler(mThread.getLooper()).post(new Runnable() {
+            @Override
+            public void run() {
+                nativeInitializeThread(nativeThread, nativeEvent);
+            }
+        });
+    }
+
+    @CalledByNative
+    private void quitThreadSafely(final long nativeThread) {
+        // Allow pending java tasks to run, but don't run any delayed or newly queued up tasks.
+        new Handler(mThread.getLooper()).post(new Runnable() {
+            @Override
+            public void run() {
+                mThread.quit();
+                nativeOnLooperStopped(nativeThread);
+            }
+        });
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
+            // When we can, signal that new tasks queued up won't be run.
+            mThread.getLooper().quitSafely();
+        }
+    }
+
+    @CalledByNative
+    private void joinThread() {
+        boolean joined = false;
+        while (!joined) {
+            try {
+                mThread.join();
+                joined = true;
+            } catch (InterruptedException e) {
+            }
+        }
+    }
+
+    private boolean hasStarted() {
+        return mThread.getState() != Thread.State.NEW;
+    }
+
+    @CalledByNative
+    private boolean isAlive() {
+        return mThread.isAlive();
+    }
+
+    // This should *only* be used for tests. In production we always need to call the original
+    // uncaught exception handler (the framework's) after any uncaught exception handling we do, as
+    // it generates crash dumps and kills the process.
+    @CalledByNative
+    private void listenForUncaughtExceptionsForTesting() {
+        mThread.setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
+            @Override
+            public void uncaughtException(Thread t, Throwable e) {
+                mUnhandledException = e;
+            }
+        });
+    }
+
+    @CalledByNative
+    private Throwable getUncaughtExceptionIfAny() {
+        return mUnhandledException;
+    }
+
+    private native void nativeInitializeThread(long nativeJavaHandlerThread, long nativeEvent);
+    private native void nativeOnLooperStopped(long nativeJavaHandlerThread);
+}
diff --git a/base/android/java/src/org/chromium/base/LocaleUtils.java b/base/android/java/src/org/chromium/base/LocaleUtils.java
new file mode 100644
index 0000000..05d3902
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/LocaleUtils.java
@@ -0,0 +1,207 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base;
+
+import android.annotation.TargetApi;
+import android.os.Build;
+import android.os.LocaleList;
+import android.text.TextUtils;
+
+import org.chromium.base.annotations.CalledByNative;
+
+import java.util.ArrayList;
+import java.util.Locale;
+
+/**
+ * This class provides the locale related methods.
+ */
+public class LocaleUtils {
+    /**
+     * Guards this class from being instantiated.
+     */
+    private LocaleUtils() {
+    }
+
+    /**
+     * Java keeps deprecated language codes for Hebrew, Yiddish and Indonesian but Chromium uses
+     * updated ones. Similarly, Android uses "tl" while Chromium uses "fil" for Tagalog/Filipino.
+     * So apply a mapping here.
+     * See http://developer.android.com/reference/java/util/Locale.html
+     * @return a updated language code for Chromium with given language string.
+     */
+    public static String getUpdatedLanguageForChromium(String language) {
+        // IMPORTANT: Keep in sync with the mapping found in:
+        // build/android/gyp/util/resource_utils.py
+        switch (language) {
+            case "iw":
+                return "he"; // Hebrew
+            case "ji":
+                return "yi"; // Yiddish
+            case "in":
+                return "id"; // Indonesian
+            case "tl":
+                return "fil"; // Filipino
+            default:
+                return language;
+        }
+    }
+
+    /**
+     * @return a locale with updated language codes for Chromium, with translated modern language
+     *         codes used by Chromium.
+     */
+    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+    @VisibleForTesting
+    public static Locale getUpdatedLocaleForChromium(Locale locale) {
+        String language = locale.getLanguage();
+        String languageForChrome = getUpdatedLanguageForChromium(language);
+        if (languageForChrome.equals(language)) {
+            return locale;
+        }
+        return new Locale.Builder().setLocale(locale).setLanguage(languageForChrome).build();
+    }
+
+    /**
+     * Android uses "tl" while Chromium uses "fil" for Tagalog/Filipino.
+     * So apply a mapping here.
+     * See http://developer.android.com/reference/java/util/Locale.html
+     * @return a updated language code for Android with given language string.
+     */
+    public static String getUpdatedLanguageForAndroid(String language) {
+        // IMPORTANT: Keep in sync with the mapping found in:
+        // build/android/gyp/util/resource_utils.py
+        switch (language) {
+            case "und":
+                return ""; // Undefined
+            case "fil":
+                return "tl"; // Filipino
+            default:
+                return language;
+        }
+    }
+
+    /**
+     * @return a locale with updated language codes for Android, from translated modern language
+     *         codes used by Chromium.
+     */
+    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+    @VisibleForTesting
+    public static Locale getUpdatedLocaleForAndroid(Locale locale) {
+        String language = locale.getLanguage();
+        String languageForAndroid = getUpdatedLanguageForAndroid(language);
+        if (languageForAndroid.equals(language)) {
+            return locale;
+        }
+        return new Locale.Builder().setLocale(locale).setLanguage(languageForAndroid).build();
+    }
+
+    /**
+     * This function creates a Locale object from xx-XX style string where xx is language code
+     * and XX is a country code. This works for API level lower than 21.
+     * @return the locale that best represents the language tag.
+     */
+    public static Locale forLanguageTagCompat(String languageTag) {
+        String[] tag = languageTag.split("-");
+        if (tag.length == 0) {
+            return new Locale("");
+        }
+        String language = getUpdatedLanguageForAndroid(tag[0]);
+        if ((language.length() != 2 && language.length() != 3)) {
+            return new Locale("");
+        }
+        if (tag.length == 1) {
+            return new Locale(language);
+        }
+        String country = tag[1];
+        if (country.length() != 2 && country.length() != 3) {
+            return new Locale(language);
+        }
+        return new Locale(language, country);
+    }
+
+    /**
+     * This function creates a Locale object from xx-XX style string where xx is language code
+     * and XX is a country code.
+     * @return the locale that best represents the language tag.
+     */
+    public static Locale forLanguageTag(String languageTag) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+            Locale locale = Locale.forLanguageTag(languageTag);
+            return getUpdatedLocaleForAndroid(locale);
+        }
+        return forLanguageTagCompat(languageTag);
+    }
+
+    /**
+     * Converts Locale object to the BCP 47 compliant string format.
+     * This works for API level lower than 24.
+     *
+     * Note that for Android M or before, we cannot use Locale.getLanguage() and
+     * Locale.toLanguageTag() for this purpose. Since Locale.getLanguage() returns deprecated
+     * language code even if the Locale object is constructed with updated language code. As for
+     * Locale.toLanguageTag(), it does a special conversion from deprecated language code to updated
+     * one, but it is only usable for Android N or after.
+     * @return a well-formed IETF BCP 47 language tag with language and country code that
+     *         represents this locale.
+     */
+    public static String toLanguageTag(Locale locale) {
+        String language = getUpdatedLanguageForChromium(locale.getLanguage());
+        String country = locale.getCountry();
+        if (language.equals("no") && country.equals("NO") && locale.getVariant().equals("NY")) {
+            return "nn-NO";
+        }
+        return country.isEmpty() ? language : language + "-" + country;
+    }
+
+    /**
+     * Converts LocaleList object to the comma separated BCP 47 compliant string format.
+     *
+     * @return a well-formed IETF BCP 47 language tag with language and country code that
+     *         represents this locale list.
+     */
+    @TargetApi(Build.VERSION_CODES.N)
+    public static String toLanguageTags(LocaleList localeList) {
+        ArrayList<String> newLocaleList = new ArrayList<>();
+        for (int i = 0; i < localeList.size(); i++) {
+            Locale locale = getUpdatedLocaleForChromium(localeList.get(i));
+            newLocaleList.add(toLanguageTag(locale));
+        }
+        return TextUtils.join(",", newLocaleList);
+    }
+
+    /**
+     * @return a comma separated language tags string that represents a default locale.
+     *         Each language tag is well-formed IETF BCP 47 language tag with language and country
+     *         code.
+     */
+    @CalledByNative
+    public static String getDefaultLocaleString() {
+        return toLanguageTag(Locale.getDefault());
+    }
+
+    /**
+     * @return a comma separated language tags string that represents a default locale or locales.
+     *         Each language tag is well-formed IETF BCP 47 language tag with language and country
+     *         code.
+     */
+    public static String getDefaultLocaleListString() {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+            return toLanguageTags(LocaleList.getDefault());
+        }
+        return getDefaultLocaleString();
+    }
+
+    /**
+     * @return The default country code set during install.
+     */
+    @CalledByNative
+    private static String getDefaultCountryCode() {
+        CommandLine commandLine = CommandLine.getInstance();
+        return commandLine.hasSwitch(BaseSwitches.DEFAULT_COUNTRY_CODE_AT_INSTALL)
+                ? commandLine.getSwitchValue(BaseSwitches.DEFAULT_COUNTRY_CODE_AT_INSTALL)
+                : Locale.getDefault().getCountry();
+    }
+
+}
diff --git a/base/android/java/src/org/chromium/base/MemoryPressureListener.java b/base/android/java/src/org/chromium/base/MemoryPressureListener.java
new file mode 100644
index 0000000..6c80970
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/MemoryPressureListener.java
@@ -0,0 +1,130 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base;
+
+import android.app.Activity;
+import android.content.ComponentCallbacks2;
+
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.MainDex;
+import org.chromium.base.memory.MemoryPressureCallback;
+
+/**
+ * This class is Java equivalent of base::MemoryPressureListener: it distributes pressure
+ * signals to callbacks.
+ *
+ * The class also serves as an entry point to the native side - once native code is ready,
+ * it adds native callback.
+ *
+ * notifyMemoryPressure() is called exclusively by MemoryPressureMonitor, which
+ * monitors and throttles pressure signals.
+ *
+ * NOTE: this class should only be used on UiThread as defined by ThreadUtils (which is
+ *       Android main thread for Chrome, but can be some other thread for WebView).
+ */
+@MainDex
+public class MemoryPressureListener {
+    /**
+     * Sending an intent with this action to Chrome will cause it to issue a call to onLowMemory
+     * thus simulating a low memory situations.
+     */
+    private static final String ACTION_LOW_MEMORY = "org.chromium.base.ACTION_LOW_MEMORY";
+
+    /**
+     * Sending an intent with this action to Chrome will cause it to issue a call to onTrimMemory
+     * thus simulating a low memory situations.
+     */
+    private static final String ACTION_TRIM_MEMORY = "org.chromium.base.ACTION_TRIM_MEMORY";
+
+    /**
+     * Sending an intent with this action to Chrome will cause it to issue a call to onTrimMemory
+     * with notification level TRIM_MEMORY_RUNNING_CRITICAL thus simulating a low memory situation
+     */
+    private static final String ACTION_TRIM_MEMORY_RUNNING_CRITICAL =
+            "org.chromium.base.ACTION_TRIM_MEMORY_RUNNING_CRITICAL";
+
+    /**
+     * Sending an intent with this action to Chrome will cause it to issue a call to onTrimMemory
+     * with notification level TRIM_MEMORY_MODERATE thus simulating a low memory situation
+     */
+    private static final String ACTION_TRIM_MEMORY_MODERATE =
+            "org.chromium.base.ACTION_TRIM_MEMORY_MODERATE";
+
+    private static final ObserverList<MemoryPressureCallback> sCallbacks = new ObserverList<>();
+
+    /**
+     * Called by the native side to add native callback.
+     */
+    @CalledByNative
+    private static void addNativeCallback() {
+        addCallback(MemoryPressureListener::nativeOnMemoryPressure);
+    }
+
+    /**
+     * Adds a memory pressure callback.
+     * Callback is only added once, regardless of the number of addCallback() calls.
+     * This method should be called only on ThreadUtils.UiThread.
+     */
+    public static void addCallback(MemoryPressureCallback callback) {
+        sCallbacks.addObserver(callback);
+    }
+
+    /**
+     * Removes previously added memory pressure callback.
+     * This method should be called only on ThreadUtils.UiThread.
+     */
+    public static void removeCallback(MemoryPressureCallback callback) {
+        sCallbacks.removeObserver(callback);
+    }
+
+    /**
+     * Distributes |pressure| to all callbacks.
+     * This method should be called only on ThreadUtils.UiThread.
+     */
+    public static void notifyMemoryPressure(@MemoryPressureLevel int pressure) {
+        for (MemoryPressureCallback callback : sCallbacks) {
+            callback.onPressure(pressure);
+        }
+    }
+
+    /**
+     * Used by applications to simulate a memory pressure signal. By throwing certain intent
+     * actions.
+     */
+    public static boolean handleDebugIntent(Activity activity, String action) {
+        if (ACTION_LOW_MEMORY.equals(action)) {
+            simulateLowMemoryPressureSignal(activity);
+        } else if (ACTION_TRIM_MEMORY.equals(action)) {
+            simulateTrimMemoryPressureSignal(activity, ComponentCallbacks2.TRIM_MEMORY_COMPLETE);
+        } else if (ACTION_TRIM_MEMORY_RUNNING_CRITICAL.equals(action)) {
+            simulateTrimMemoryPressureSignal(activity,
+                    ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL);
+        } else if (ACTION_TRIM_MEMORY_MODERATE.equals(action)) {
+            simulateTrimMemoryPressureSignal(activity, ComponentCallbacks2.TRIM_MEMORY_MODERATE);
+        } else {
+            return false;
+        }
+
+        return true;
+    }
+
+    private static void simulateLowMemoryPressureSignal(Activity activity) {
+        // The Application and the Activity each have a list of callbacks they notify when this
+        // method is called.  Notifying these will simulate the event at the App/Activity level
+        // as well as trigger the listener bound from native in this process.
+        activity.getApplication().onLowMemory();
+        activity.onLowMemory();
+    }
+
+    private static void simulateTrimMemoryPressureSignal(Activity activity, int level) {
+        // The Application and the Activity each have a list of callbacks they notify when this
+        // method is called.  Notifying these will simulate the event at the App/Activity level
+        // as well as trigger the listener bound from native in this process.
+        activity.getApplication().onTrimMemory(level);
+        activity.onTrimMemory(level);
+    }
+
+    private static native void nativeOnMemoryPressure(@MemoryPressureLevel int pressure);
+}
diff --git a/base/android/java/src/org/chromium/base/NonThreadSafe.java b/base/android/java/src/org/chromium/base/NonThreadSafe.java
new file mode 100644
index 0000000..53f38d2
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/NonThreadSafe.java
@@ -0,0 +1,41 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base;
+
+/**
+ * NonThreadSafe is a helper class used to help verify that methods of a
+ * class are called from the same thread.
+ */
+public class NonThreadSafe {
+    private Long mThreadId;
+
+    public NonThreadSafe() {
+        ensureThreadIdAssigned();
+    }
+
+    /**
+     * Changes the thread that is checked for in CalledOnValidThread. This may
+     * be useful when an object may be created on one thread and then used
+     * exclusively on another thread.
+     */
+    @VisibleForTesting
+    public synchronized void detachFromThread() {
+        mThreadId = null;
+    }
+
+    /**
+     * Checks if the method is called on the valid thread.
+     * Assigns the current thread if no thread was assigned.
+     */
+    @SuppressWarnings("NoSynchronizedMethodCheck")
+    public synchronized boolean calledOnValidThread() {
+        ensureThreadIdAssigned();
+        return mThreadId.equals(Thread.currentThread().getId());
+    }
+
+    private void ensureThreadIdAssigned() {
+        if (mThreadId == null) mThreadId = Thread.currentThread().getId();
+    }
+}
diff --git a/base/android/java/src/org/chromium/base/ObserverList.java b/base/android/java/src/org/chromium/base/ObserverList.java
new file mode 100644
index 0000000..59276c6
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/ObserverList.java
@@ -0,0 +1,249 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+
+import javax.annotation.concurrent.NotThreadSafe;
+
+/**
+ * A container for a list of observers.
+ * <p/>
+ * 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.
+ * <p/>
+ * The implementation (and the interface) is heavily influenced by the C++ ObserverList.
+ * Notable differences:
+ *   - The iterator implements NOTIFY_EXISTING_ONLY.
+ *   - The range-based for loop is left to the clients to implement in terms of iterator().
+ * <p/>
+ * This class is not threadsafe. Observers MUST be added, removed and will be notified on the same
+ * thread this is created.
+ *
+ * @param <E> The type of observers that this list should hold.
+ */
+@NotThreadSafe
+public class ObserverList<E> implements Iterable<E> {
+    /**
+     * Extended iterator interface that provides rewind functionality.
+     */
+    public interface RewindableIterator<E> extends Iterator<E> {
+        /**
+         * Rewind the iterator back to the beginning.
+         *
+         * If we need to iterate multiple times, we can avoid iterator object reallocation by using
+         * this method.
+         */
+        public void rewind();
+    }
+
+    public final List<E> mObservers = new ArrayList<E>();
+    private int mIterationDepth;
+    private int mCount;
+    private boolean mNeedsCompact;
+
+    public ObserverList() {}
+
+    /**
+     * Add an observer to the list.
+     * <p/>
+     * An observer should not be added to the same list more than once. If an iteration is already
+     * in progress, this observer will be not be visible during that iteration.
+     *
+     * @return true if the observer list changed as a result of the call.
+     */
+    public boolean addObserver(E obs) {
+        // Avoid adding null elements to the list as they may be removed on a compaction.
+        if (obs == null || mObservers.contains(obs)) {
+            return false;
+        }
+
+        // Structurally modifying the underlying list here. This means we
+        // cannot use the underlying list's iterator to iterate over the list.
+        boolean result = mObservers.add(obs);
+        assert result;
+
+        ++mCount;
+        return true;
+    }
+
+    /**
+     * Remove an observer from the list if it is in the list.
+     *
+     * @return true if an element was removed as a result of this call.
+     */
+    public boolean removeObserver(E obs) {
+        if (obs == null) {
+            return false;
+        }
+
+        int index = mObservers.indexOf(obs);
+        if (index == -1) {
+            return false;
+        }
+
+        if (mIterationDepth == 0) {
+            // No one is iterating over the list.
+            mObservers.remove(index);
+        } else {
+            mNeedsCompact = true;
+            mObservers.set(index, null);
+        }
+        --mCount;
+        assert mCount >= 0;
+
+        return true;
+    }
+
+    public boolean hasObserver(E obs) {
+        return mObservers.contains(obs);
+    }
+
+    public void clear() {
+        mCount = 0;
+
+        if (mIterationDepth == 0) {
+            mObservers.clear();
+            return;
+        }
+
+        int size = mObservers.size();
+        mNeedsCompact |= size != 0;
+        for (int i = 0; i < size; i++) {
+            mObservers.set(i, null);
+        }
+    }
+
+    @Override
+    public Iterator<E> iterator() {
+        return new ObserverListIterator();
+    }
+
+    /**
+     * It's the same as {@link ObserverList#iterator()} but the return type is
+     * {@link RewindableIterator}. Use this iterator type if you need to use
+     * {@link RewindableIterator#rewind()}.
+     */
+    public RewindableIterator<E> rewindableIterator() {
+        return new ObserverListIterator();
+    }
+
+    /**
+     * Returns the number of observers currently registered in the ObserverList.
+     * This is equivalent to the number of non-empty spaces in |mObservers|.
+     */
+    public int size() {
+        return mCount;
+    }
+
+    /**
+     * Returns true if the ObserverList contains no observers.
+     */
+    public boolean isEmpty() {
+        return mCount == 0;
+    }
+
+    /**
+     * Compact the underlying list be removing null elements.
+     * <p/>
+     * Should only be called when mIterationDepth is zero.
+     */
+    private void compact() {
+        assert mIterationDepth == 0;
+        for (int i = mObservers.size() - 1; i >= 0; i--) {
+            if (mObservers.get(i) == null) {
+                mObservers.remove(i);
+            }
+        }
+    }
+
+    private void incrementIterationDepth() {
+        mIterationDepth++;
+    }
+
+    private void decrementIterationDepthAndCompactIfNeeded() {
+        mIterationDepth--;
+        assert mIterationDepth >= 0;
+        if (mIterationDepth > 0) return;
+        if (!mNeedsCompact) return;
+        mNeedsCompact = false;
+        compact();
+    }
+
+    /**
+     * Returns the size of the underlying storage of the ObserverList.
+     * It will take into account the empty spaces inside |mObservers|.
+     */
+    private int capacity() {
+        return mObservers.size();
+    }
+
+    private E getObserverAt(int index) {
+        return mObservers.get(index);
+    }
+
+    private class ObserverListIterator implements RewindableIterator<E> {
+        private int mListEndMarker;
+        private int mIndex;
+        private boolean mIsExhausted;
+
+        private ObserverListIterator() {
+            ObserverList.this.incrementIterationDepth();
+            mListEndMarker = ObserverList.this.capacity();
+        }
+
+        @Override
+        public void rewind() {
+            compactListIfNeeded();
+            ObserverList.this.incrementIterationDepth();
+            mListEndMarker = ObserverList.this.capacity();
+            mIsExhausted = false;
+            mIndex = 0;
+        }
+
+        @Override
+        public boolean hasNext() {
+            int lookupIndex = mIndex;
+            while (lookupIndex < mListEndMarker
+                    && ObserverList.this.getObserverAt(lookupIndex) == null) {
+                lookupIndex++;
+            }
+            if (lookupIndex < mListEndMarker) return true;
+
+            // We have reached the end of the list, allow for compaction.
+            compactListIfNeeded();
+            return false;
+        }
+
+        @Override
+        public E next() {
+            // Advance if the current element is null.
+            while (mIndex < mListEndMarker && ObserverList.this.getObserverAt(mIndex) == null) {
+                mIndex++;
+            }
+            if (mIndex < mListEndMarker) return ObserverList.this.getObserverAt(mIndex++);
+
+            // We have reached the end of the list, allow for compaction.
+            compactListIfNeeded();
+            throw new NoSuchElementException();
+        }
+
+        @Override
+        public void remove() {
+            throw new UnsupportedOperationException();
+        }
+
+        private void compactListIfNeeded() {
+            if (!mIsExhausted) {
+                mIsExhausted = true;
+                ObserverList.this.decrementIterationDepthAndCompactIfNeeded();
+            }
+        }
+    }
+}
diff --git a/base/android/java/src/org/chromium/base/PathService.java b/base/android/java/src/org/chromium/base/PathService.java
new file mode 100644
index 0000000..9807c2e
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/PathService.java
@@ -0,0 +1,26 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base;
+
+import org.chromium.base.annotations.JNINamespace;
+
+/**
+ * This class provides java side access to the native PathService.
+ */
+@JNINamespace("base::android")
+public abstract class PathService {
+
+    // Must match the value of DIR_MODULE in base/base_paths.h!
+    public static final int DIR_MODULE = 3;
+
+    // Prevent instantiation.
+    private PathService() {}
+
+    public static void override(int what, String path) {
+        nativeOverride(what, path);
+    }
+
+    private static native void nativeOverride(int what, String path);
+}
diff --git a/base/android/java/src/org/chromium/base/PathUtils.java b/base/android/java/src/org/chromium/base/PathUtils.java
new file mode 100644
index 0000000..e6fc802
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/PathUtils.java
@@ -0,0 +1,263 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.os.Build;
+import android.os.Environment;
+import android.os.SystemClock;
+import android.system.Os;
+import android.text.TextUtils;
+
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.MainDex;
+import org.chromium.base.metrics.RecordHistogram;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * This class provides the path related methods for the native library.
+ */
+@MainDex
+public abstract class PathUtils {
+    private static final String TAG = "PathUtils";
+    private static final String THUMBNAIL_DIRECTORY_NAME = "textures";
+
+    private static final int DATA_DIRECTORY = 0;
+    private static final int THUMBNAIL_DIRECTORY = 1;
+    private static final int CACHE_DIRECTORY = 2;
+    private static final int NUM_DIRECTORIES = 3;
+    private static final AtomicBoolean sInitializationStarted = new AtomicBoolean();
+    private static AsyncTask<Void, Void, String[]> sDirPathFetchTask;
+
+    // If the AsyncTask started in setPrivateDataDirectorySuffix() fails to complete by the time we
+    // need the values, we will need the suffix so that we can restart the task synchronously on
+    // the UI thread.
+    private static String sDataDirectorySuffix;
+    private static String sCacheSubDirectory;
+
+    // Prevent instantiation.
+    private PathUtils() {}
+
+    /**
+     * Initialization-on-demand holder. This exists for thread-safe lazy initialization. It will
+     * cause getOrComputeDirectoryPaths() to be called (safely) the first time DIRECTORY_PATHS is
+     * accessed.
+     *
+     * <p>See https://en.wikipedia.org/wiki/Initialization-on-demand_holder_idiom.
+     */
+    private static class Holder {
+        private static final String[] DIRECTORY_PATHS = getOrComputeDirectoryPaths();
+    }
+
+    /**
+     * Get the directory paths from sDirPathFetchTask if available, or compute it synchronously
+     * on the UI thread otherwise. This should only be called as part of Holder's initialization
+     * above to guarantee thread-safety as part of the initialization-on-demand holder idiom.
+     */
+    private static String[] getOrComputeDirectoryPaths() {
+        try {
+            // We need to call sDirPathFetchTask.cancel() here to prevent races. If it returns
+            // true, that means that the task got canceled successfully (and thus, it did not
+            // finish running its task). Otherwise, it failed to cancel, meaning that it was
+            // already finished.
+            if (sDirPathFetchTask.cancel(false)) {
+                // Allow disk access here because we have no other choice.
+                try (StrictModeContext unused = StrictModeContext.allowDiskWrites()) {
+                    // sDirPathFetchTask did not complete. We have to run the code it was supposed
+                    // to be responsible for synchronously on the UI thread.
+                    return PathUtils.setPrivateDataDirectorySuffixInternal();
+                }
+            } else {
+                // sDirPathFetchTask succeeded, and the values we need should be ready to access
+                // synchronously in its internal future.
+                return sDirPathFetchTask.get();
+            }
+        } catch (InterruptedException e) {
+        } catch (ExecutionException e) {
+        }
+
+        return null;
+    }
+
+    @SuppressLint("NewApi")
+    private static void chmod(String path, int mode) {
+        // Both Os.chmod and ErrnoException require SDK >= 21. But while Dalvik on < 21 tolerates
+        // Os.chmod, it throws VerifyError for ErrnoException, so catch Exception instead.
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return;
+        try {
+            Os.chmod(path, mode);
+        } catch (Exception e) {
+            Log.e(TAG, "Failed to set permissions for path \"" + path + "\"");
+        }
+    }
+
+    /**
+     * Fetch the path of the directory where private data is to be stored by the application. This
+     * is meant to be called in an AsyncTask in setPrivateDataDirectorySuffix(), but if we need the
+     * result before the AsyncTask has had a chance to finish, then it's best to cancel the task
+     * and run it on the UI thread instead, inside getOrComputeDirectoryPaths().
+     *
+     * @see Context#getDir(String, int)
+     */
+    private static String[] setPrivateDataDirectorySuffixInternal() {
+        String[] paths = new String[NUM_DIRECTORIES];
+        Context appContext = ContextUtils.getApplicationContext();
+        paths[DATA_DIRECTORY] = appContext.getDir(
+                sDataDirectorySuffix, Context.MODE_PRIVATE).getPath();
+        // MODE_PRIVATE results in rwxrwx--x, but we want rwx------, as a defence-in-depth measure.
+        chmod(paths[DATA_DIRECTORY], 0700);
+        paths[THUMBNAIL_DIRECTORY] = appContext.getDir(
+                THUMBNAIL_DIRECTORY_NAME, Context.MODE_PRIVATE).getPath();
+        if (appContext.getCacheDir() != null) {
+            if (sCacheSubDirectory == null) {
+                paths[CACHE_DIRECTORY] = appContext.getCacheDir().getPath();
+            } else {
+                paths[CACHE_DIRECTORY] =
+                        new File(appContext.getCacheDir(), sCacheSubDirectory).getPath();
+            }
+        }
+        return paths;
+    }
+
+    /**
+     * Starts an asynchronous task to fetch the path of the directory where private data is to be
+     * stored by the application.
+     *
+     * <p>This task can run long (or more likely be delayed in a large task queue), in which case we
+     * want to cancel it and run on the UI thread instead. Unfortunately, this means keeping a bit
+     * of extra static state - we need to store the suffix and the application context in case we
+     * need to try to re-execute later.
+     *
+     * @param suffix The private data directory suffix.
+     * @param cacheSubDir The subdirectory in the cache directory to use, if non-null.
+     * @see Context#getDir(String, int)
+     */
+    public static void setPrivateDataDirectorySuffix(String suffix, String cacheSubDir) {
+        // This method should only be called once, but many tests end up calling it multiple times,
+        // so adding a guard here.
+        if (!sInitializationStarted.getAndSet(true)) {
+            assert ContextUtils.getApplicationContext() != null;
+            sDataDirectorySuffix = suffix;
+            sCacheSubDirectory = cacheSubDir;
+            sDirPathFetchTask = new AsyncTask<Void, Void, String[]>() {
+                @Override
+                protected String[] doInBackground(Void... unused) {
+                    return PathUtils.setPrivateDataDirectorySuffixInternal();
+                }
+            }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+        }
+    }
+
+    public static void setPrivateDataDirectorySuffix(String suffix) {
+        setPrivateDataDirectorySuffix(suffix, null);
+    }
+
+    /**
+     * @param index The index of the cached directory path.
+     * @return The directory path requested.
+     */
+    private static String getDirectoryPath(int index) {
+        return Holder.DIRECTORY_PATHS[index];
+    }
+
+    /**
+     * @return the private directory that is used to store application data.
+     */
+    @CalledByNative
+    public static String getDataDirectory() {
+        assert sDirPathFetchTask != null : "setDataDirectorySuffix must be called first.";
+        return getDirectoryPath(DATA_DIRECTORY);
+    }
+
+    /**
+     * @return the cache directory.
+     */
+    @CalledByNative
+    public static String getCacheDirectory() {
+        assert sDirPathFetchTask != null : "setDataDirectorySuffix must be called first.";
+        return getDirectoryPath(CACHE_DIRECTORY);
+    }
+
+    @CalledByNative
+    public static String getThumbnailCacheDirectory() {
+        assert sDirPathFetchTask != null : "setDataDirectorySuffix must be called first.";
+        return getDirectoryPath(THUMBNAIL_DIRECTORY);
+    }
+
+    /**
+     * @return the public downloads directory.
+     */
+    @SuppressWarnings("unused")
+    @CalledByNative
+    private static String getDownloadsDirectory() {
+        // Temporarily allowing disk access while fixing. TODO: http://crbug.com/508615
+        try (StrictModeContext unused = StrictModeContext.allowDiskReads()) {
+            long time = SystemClock.elapsedRealtime();
+            String downloadsPath =
+                    Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
+                            .getPath();
+            RecordHistogram.recordTimesHistogram("Android.StrictMode.DownloadsDir",
+                    SystemClock.elapsedRealtime() - time, TimeUnit.MILLISECONDS);
+            return downloadsPath;
+        }
+    }
+
+    /**
+     * @return Download directories including the default storage directory on SD card, and a
+     * private directory on external SD card.
+     */
+    @SuppressWarnings("unused")
+    @CalledByNative
+    public static String[] getAllPrivateDownloadsDirectories() {
+        File[] files;
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+            try (StrictModeContext unused = StrictModeContext.allowDiskWrites()) {
+                files = ContextUtils.getApplicationContext().getExternalFilesDirs(
+                        Environment.DIRECTORY_DOWNLOADS);
+            }
+        } else {
+            files = new File[] {Environment.getExternalStorageDirectory()};
+        }
+
+        ArrayList<String> absolutePaths = new ArrayList<String>();
+        for (int i = 0; i < files.length; ++i) {
+            if (files[i] == null || TextUtils.isEmpty(files[i].getAbsolutePath())) continue;
+            absolutePaths.add(files[i].getAbsolutePath());
+        }
+
+        return absolutePaths.toArray(new String[absolutePaths.size()]);
+    }
+
+    /**
+     * @return the path to native libraries.
+     */
+    @SuppressWarnings("unused")
+    @CalledByNative
+    private static String getNativeLibraryDirectory() {
+        ApplicationInfo ai = ContextUtils.getApplicationContext().getApplicationInfo();
+        if ((ai.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0
+                || (ai.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
+            return ai.nativeLibraryDir;
+        }
+
+        return "/system/lib/";
+    }
+
+    /**
+     * @return the external storage directory.
+     */
+    @SuppressWarnings("unused")
+    @CalledByNative
+    public static String getExternalStorageDirectory() {
+        return Environment.getExternalStorageDirectory().getAbsolutePath();
+    }
+}
diff --git a/base/android/java/src/org/chromium/base/PowerMonitor.java b/base/android/java/src/org/chromium/base/PowerMonitor.java
new file mode 100644
index 0000000..ae36a75
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/PowerMonitor.java
@@ -0,0 +1,80 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.BatteryManager;
+
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.JNINamespace;
+
+/**
+ * Integrates native PowerMonitor with the java side.
+ */
+@JNINamespace("base::android")
+public class PowerMonitor  {
+    private static PowerMonitor sInstance;
+
+    private boolean mIsBatteryPower;
+
+    public static void createForTests() {
+        // Applications will create this once the JNI side has been fully wired up both sides. For
+        // tests, we just need native -> java, that is, we don't need to notify java -> native on
+        // creation.
+        sInstance = new PowerMonitor();
+    }
+
+    /**
+     * Create a PowerMonitor instance if none exists.
+     */
+    public static void create() {
+        ThreadUtils.assertOnUiThread();
+
+        if (sInstance != null) return;
+
+        Context context = ContextUtils.getApplicationContext();
+        sInstance = new PowerMonitor();
+        IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
+        Intent batteryStatusIntent = context.registerReceiver(null, ifilter);
+        if (batteryStatusIntent != null) onBatteryChargingChanged(batteryStatusIntent);
+
+        IntentFilter powerConnectedFilter = new IntentFilter();
+        powerConnectedFilter.addAction(Intent.ACTION_POWER_CONNECTED);
+        powerConnectedFilter.addAction(Intent.ACTION_POWER_DISCONNECTED);
+        context.registerReceiver(new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                PowerMonitor.onBatteryChargingChanged(intent);
+            }
+        }, powerConnectedFilter);
+    }
+
+    private PowerMonitor() {
+    }
+
+    private static void onBatteryChargingChanged(Intent intent) {
+        assert sInstance != null;
+        int chargePlug = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
+        // If we're not plugged, assume we're running on battery power.
+        sInstance.mIsBatteryPower = chargePlug != BatteryManager.BATTERY_PLUGGED_USB
+                && chargePlug != BatteryManager.BATTERY_PLUGGED_AC;
+        nativeOnBatteryChargingChanged();
+    }
+
+    @CalledByNative
+    private static boolean isBatteryPower() {
+        // Creation of the PowerMonitor can be deferred based on the browser startup path.  If the
+        // battery power is requested prior to the browser triggering the creation, force it to be
+        // created now.
+        if (sInstance == null) create();
+
+        return sInstance.mIsBatteryPower;
+    }
+
+    private static native void nativeOnBatteryChargingChanged();
+}
diff --git a/base/android/java/src/org/chromium/base/Promise.java b/base/android/java/src/org/chromium/base/Promise.java
new file mode 100644
index 0000000..4319148
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/Promise.java
@@ -0,0 +1,294 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base;
+
+import android.os.Handler;
+import android.support.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * A Promise class to be used as a placeholder for a result that will be provided asynchronously.
+ * It must only be accessed from a single thread.
+ * @param <T> The type the Promise will be fulfilled with.
+ */
+public class Promise<T> {
+    // TODO(peconn): Implement rejection handlers that can recover from rejection.
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({UNFULFILLED, FULFILLED, REJECTED})
+    private @interface PromiseState {}
+
+    private static final int UNFULFILLED = 0;
+    private static final int FULFILLED = 1;
+    private static final int REJECTED = 2;
+
+    @PromiseState
+    private int mState = UNFULFILLED;
+
+    private T mResult;
+    private final List<Callback<T>> mFulfillCallbacks = new LinkedList<>();
+
+    private Exception mRejectReason;
+    private final List<Callback<Exception>> mRejectCallbacks = new LinkedList<>();
+
+    private final Thread mThread = Thread.currentThread();
+    private final Handler mHandler = new Handler();
+
+    private boolean mThrowingRejectionHandler;
+
+    /**
+     * A function class for use when chaining Promises with {@link Promise#then(Function)}.
+     * @param <A> The type of the function input.
+     * @param <R> The type of the function output.
+     */
+    public interface Function<A, R> {
+        R apply(A argument);
+    }
+
+    /**
+     * A function class for use when chaining Promises with {@link Promise#then(AsyncFunction)}.
+     * @param <A> The type of the function input.
+     * @param <R> The type of the function output.
+     */
+    public interface AsyncFunction<A, R> {
+        Promise<R> apply(A argument);
+    }
+
+    /**
+     * An exception class for when a rejected Promise is not handled and cannot pass the rejection
+     * to a subsequent Promise.
+     */
+    public static class UnhandledRejectionException extends RuntimeException {
+        public UnhandledRejectionException(String message, Throwable cause) {
+            super(message, cause);
+        }
+    }
+
+    /**
+     * Convenience method that calls {@link #then(Callback, Callback)} providing a rejection
+     * {@link Callback} that throws a {@link UnhandledRejectionException}. Only use this on
+     * Promises that do not have rejection handlers or dependant Promises.
+     */
+    public void then(Callback<T> onFulfill) {
+        checkThread();
+
+        // Allow multiple single argument then(Callback)'s, but don't bother adding duplicate
+        // throwing rejection handlers.
+        if (mThrowingRejectionHandler) {
+            thenInner(onFulfill);
+            return;
+        }
+
+        assert mRejectCallbacks.size() == 0 : "Do not call the single argument "
+            + "Promise.then(Callback) on a Promise that already has a rejection handler.";
+
+        Callback<Exception> onReject = reason -> {
+            throw new UnhandledRejectionException(
+                    "Promise was rejected without a rejection handler.", reason);
+        };
+
+        then(onFulfill, onReject);
+        mThrowingRejectionHandler = true;
+    }
+
+    /**
+     * Queues {@link Callback}s to be run when the Promise is either fulfilled or rejected. If the
+     * Promise is already fulfilled or rejected, the appropriate callback will be run on the next
+     * iteration of the message loop.
+     *
+     * @param onFulfill The Callback to be called on fulfillment.
+     * @param onReject The Callback to be called on rejection. The argument to onReject will
+     *         may be null if the Promise was rejected manually.
+     */
+    public void then(Callback<T> onFulfill, Callback<Exception> onReject) {
+        checkThread();
+        thenInner(onFulfill);
+        exceptInner(onReject);
+    }
+
+    /**
+     * Adds a rejection handler to the Promise. This handler will be called if this Promise or any
+     * Promises this Promise depends on is rejected or fails. The {@link Callback} will be given
+     * the exception that caused the rejection, or null if the rejection was manual (caused by a
+     * call to {@link #reject()}.
+     */
+    public void except(Callback<Exception> onReject) {
+        checkThread();
+        exceptInner(onReject);
+    }
+
+    private void thenInner(Callback<T> onFulfill) {
+        if (mState == FULFILLED) {
+            postCallbackToLooper(onFulfill, mResult);
+        } else if (mState == UNFULFILLED) {
+            mFulfillCallbacks.add(onFulfill);
+        }
+    }
+
+    private void exceptInner(Callback<Exception> onReject) {
+        assert !mThrowingRejectionHandler : "Do not add an exception handler to a Promise you have "
+            + "called the single argument Promise.then(Callback) on.";
+
+        if (mState == REJECTED) {
+            postCallbackToLooper(onReject, mRejectReason);
+        } else if (mState == UNFULFILLED) {
+            mRejectCallbacks.add(onReject);
+        }
+    }
+
+    /**
+     * Queues a {@link Promise.Function} to be run when the Promise is fulfilled. When this Promise
+     * is fulfilled, the function will be run and its result will be place in the returned Promise.
+     */
+    public <R> Promise<R> then(final Function<T, R> function) {
+        checkThread();
+
+        // Create a new Promise to store the result of the function.
+        final Promise<R> promise = new Promise<>();
+
+        // Once this Promise is fulfilled:
+        // - Apply the given function to the result.
+        // - Fulfill the new Promise.
+        thenInner(result -> {
+            try {
+                promise.fulfill(function.apply(result));
+            } catch (Exception e) {
+                // If function application fails, reject the next Promise.
+                promise.reject(e);
+            }
+        });
+
+        // If this Promise is rejected, reject the next Promise.
+        exceptInner(promise::reject);
+
+        return promise;
+    }
+
+    /**
+     * Queues a {@link Promise.AsyncFunction} to be run when the Promise is fulfilled. When this
+     * Promise is fulfilled, the AsyncFunction will be run. When the result of the AsyncFunction is
+     * available, it will be placed in the returned Promise.
+     */
+    public <R> Promise<R> then(final AsyncFunction<T, R> function) {
+        checkThread();
+
+        // Create a new Promise to be returned.
+        final Promise<R> promise = new Promise<>();
+
+        // Once this Promise is fulfilled:
+        // - Apply the given function to the result (giving us an inner Promise).
+        // - On fulfillment of this inner Promise, fulfill our return Promise.
+        thenInner(result -> {
+            try {
+                // When the inner Promise is fulfilled, fulfill the return Promise.
+                // Alternatively, if the inner Promise is rejected, reject the return Promise.
+                function.apply(result).then(promise::fulfill, promise::reject);
+            } catch (Exception e) {
+                // If creating the inner Promise failed, reject the next Promise.
+                promise.reject(e);
+            }
+        });
+
+        // If this Promise is rejected, reject the next Promise.
+        exceptInner(promise::reject);
+
+        return promise;
+    }
+
+    /**
+     * Fulfills the Promise with the result and passes it to any {@link Callback}s previously queued
+     * on the next iteration of the message loop.
+     */
+    public void fulfill(final T result) {
+        checkThread();
+        assert mState == UNFULFILLED;
+
+        mState = FULFILLED;
+        mResult = result;
+
+        for (final Callback<T> callback : mFulfillCallbacks) {
+            postCallbackToLooper(callback, result);
+        }
+
+        mFulfillCallbacks.clear();
+    }
+
+    /**
+     * Rejects the Promise, rejecting all those Promises that rely on it.
+     *
+     * This may throw an exception if a dependent Promise fails to handle the rejection, so it is
+     * important to make it explicit when a Promise may be rejected, so that users of that Promise
+     * know to provide rejection handling.
+     */
+    public void reject(final Exception reason) {
+        checkThread();
+        assert mState == UNFULFILLED;
+
+        mState = REJECTED;
+        mRejectReason = reason;
+
+        for (final Callback<Exception> callback : mRejectCallbacks) {
+            postCallbackToLooper(callback, reason);
+        }
+        mRejectCallbacks.clear();
+    }
+
+    /**
+     * Rejects a Promise, see {@link #reject(Exception)}.
+     */
+    public void reject() {
+        reject(null);
+    }
+
+    /**
+     * Returns whether the promise is fulfilled.
+     */
+    public boolean isFulfilled() {
+        checkThread();
+        return mState == FULFILLED;
+    }
+
+    /**
+     * Returns whether the promise is rejected.
+     */
+    public boolean isRejected() {
+        checkThread();
+        return mState == REJECTED;
+    }
+
+    /**
+     * Must be called after the promise has been fulfilled.
+     *
+     * @return The promised result.
+     */
+    public T getResult() {
+        assert isFulfilled();
+        return mResult;
+    }
+
+    /**
+     * Convenience method to return a Promise fulfilled with the given result.
+     */
+    public static <T> Promise<T> fulfilled(T result) {
+        Promise<T> promise = new Promise<>();
+        promise.fulfill(result);
+        return promise;
+    }
+
+    private void checkThread() {
+        assert mThread == Thread.currentThread() : "Promise must only be used on a single Thread.";
+    }
+
+    // We use a different template parameter here so this can be used for both T and Throwables.
+    private <S> void postCallbackToLooper(final Callback<S> callback, final S result) {
+        // Post the callbacks to the Thread looper so we don't get a long chain of callbacks
+        // holding up the thread.
+        mHandler.post(() -> callback.onResult(result));
+    }
+}
diff --git a/base/android/java/src/org/chromium/base/SecureRandomInitializer.java b/base/android/java/src/org/chromium/base/SecureRandomInitializer.java
new file mode 100644
index 0000000..bfd7b49
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/SecureRandomInitializer.java
@@ -0,0 +1,35 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base;
+
+import android.annotation.SuppressLint;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.security.SecureRandom;
+
+/**
+ * This class contains code to initialize a SecureRandom generator securely on Android platforms
+ * <= 4.3. See
+ * {@link http://android-developers.blogspot.com/2013/08/some-securerandom-thoughts.html}.
+ */
+// TODO(crbug.com/635567): Fix this properly.
+@SuppressLint("SecureRandom")
+public class SecureRandomInitializer {
+    private static final int NUM_RANDOM_BYTES = 16;
+
+    /**
+     * Safely initializes the random number generator, by seeding it with data from /dev/urandom.
+     */
+    public static void initialize(SecureRandom generator) throws IOException {
+        try (FileInputStream fis = new FileInputStream("/dev/urandom")) {
+            byte[] seedBytes = new byte[NUM_RANDOM_BYTES];
+            if (fis.read(seedBytes) != seedBytes.length) {
+                throw new IOException("Failed to get enough random data.");
+            }
+            generator.setSeed(seedBytes);
+        }
+    }
+}
diff --git a/base/android/java/src/org/chromium/base/StreamUtil.java b/base/android/java/src/org/chromium/base/StreamUtil.java
new file mode 100644
index 0000000..f8cbfee
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/StreamUtil.java
@@ -0,0 +1,28 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base;
+
+import java.io.Closeable;
+import java.io.IOException;
+
+/**
+ * Helper methods to deal with stream related tasks.
+ */
+public class StreamUtil {
+    /**
+     * Handle closing a {@link java.io.Closeable} via {@link java.io.Closeable#close()} and catch
+     * the potentially thrown {@link java.io.IOException}.
+     * @param closeable The Closeable to be closed.
+     */
+    public static void closeQuietly(Closeable closeable) {
+        if (closeable == null) return;
+
+        try {
+            closeable.close();
+        } catch (IOException ex) {
+            // Ignore the exception on close.
+        }
+    }
+}
diff --git a/base/android/java/src/org/chromium/base/SysUtils.java b/base/android/java/src/org/chromium/base/SysUtils.java
new file mode 100644
index 0000000..d4eb30d
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/SysUtils.java
@@ -0,0 +1,199 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base;
+
+import android.annotation.TargetApi;
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Build;
+import android.os.StrictMode;
+import android.util.Log;
+
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.metrics.CachedMetrics;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Exposes system related information about the current device.
+ */
+@JNINamespace("base::android")
+public class SysUtils {
+    // A device reporting strictly more total memory in megabytes cannot be considered 'low-end'.
+    private static final int ANDROID_LOW_MEMORY_DEVICE_THRESHOLD_MB = 512;
+    private static final int ANDROID_O_LOW_MEMORY_DEVICE_THRESHOLD_MB = 1024;
+
+    private static final String TAG = "SysUtils";
+
+    private static Boolean sLowEndDevice;
+    private static Integer sAmountOfPhysicalMemoryKB;
+
+    private static CachedMetrics.BooleanHistogramSample sLowEndMatches =
+            new CachedMetrics.BooleanHistogramSample("Android.SysUtilsLowEndMatches");
+
+    private SysUtils() { }
+
+    /**
+     * Return the amount of physical memory on this device in kilobytes.
+     * @return Amount of physical memory in kilobytes, or 0 if there was
+     *         an error trying to access the information.
+     */
+    private static int detectAmountOfPhysicalMemoryKB() {
+        // Extract total memory RAM size by parsing /proc/meminfo, note that
+        // this is exactly what the implementation of sysconf(_SC_PHYS_PAGES)
+        // does. However, it can't be called because this method must be
+        // usable before any native code is loaded.
+
+        // An alternative is to use ActivityManager.getMemoryInfo(), but this
+        // requires a valid ActivityManager handle, which can only come from
+        // a valid Context object, which itself cannot be retrieved
+        // during early startup, where this method is called. And making it
+        // an explicit parameter here makes all call paths _much_ more
+        // complicated.
+
+        Pattern pattern = Pattern.compile("^MemTotal:\\s+([0-9]+) kB$");
+        // Synchronously reading files in /proc in the UI thread is safe.
+        StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
+        try {
+            FileReader fileReader = new FileReader("/proc/meminfo");
+            try {
+                BufferedReader reader = new BufferedReader(fileReader);
+                try {
+                    String line;
+                    for (;;) {
+                        line = reader.readLine();
+                        if (line == null) {
+                            Log.w(TAG, "/proc/meminfo lacks a MemTotal entry?");
+                            break;
+                        }
+                        Matcher m = pattern.matcher(line);
+                        if (!m.find()) continue;
+
+                        int totalMemoryKB = Integer.parseInt(m.group(1));
+                        // Sanity check.
+                        if (totalMemoryKB <= 1024) {
+                            Log.w(TAG, "Invalid /proc/meminfo total size in kB: " + m.group(1));
+                            break;
+                        }
+
+                        return totalMemoryKB;
+                    }
+
+                } finally {
+                    reader.close();
+                }
+            } finally {
+                fileReader.close();
+            }
+        } catch (Exception e) {
+            Log.w(TAG, "Cannot get total physical size from /proc/meminfo", e);
+        } finally {
+            StrictMode.setThreadPolicy(oldPolicy);
+        }
+
+        return 0;
+    }
+
+    /**
+     * @return Whether or not this device should be considered a low end device.
+     */
+    @CalledByNative
+    public static boolean isLowEndDevice() {
+        if (sLowEndDevice == null) {
+            sLowEndDevice = detectLowEndDevice();
+        }
+        return sLowEndDevice.booleanValue();
+    }
+
+    /**
+     * @return Whether or not this device should be considered a low end device.
+     */
+    public static int amountOfPhysicalMemoryKB() {
+        if (sAmountOfPhysicalMemoryKB == null) {
+            sAmountOfPhysicalMemoryKB = detectAmountOfPhysicalMemoryKB();
+        }
+        return sAmountOfPhysicalMemoryKB.intValue();
+    }
+
+    /**
+     * @return Whether or not the system has low available memory.
+     */
+    @CalledByNative
+    public static boolean isCurrentlyLowMemory() {
+        ActivityManager am =
+                (ActivityManager) ContextUtils.getApplicationContext().getSystemService(
+                        Context.ACTIVITY_SERVICE);
+        ActivityManager.MemoryInfo info = new ActivityManager.MemoryInfo();
+        am.getMemoryInfo(info);
+        return info.lowMemory;
+    }
+
+    /**
+     * Resets the cached value, if any.
+     */
+    @VisibleForTesting
+    public static void resetForTesting() {
+        sLowEndDevice = null;
+        sAmountOfPhysicalMemoryKB = null;
+    }
+
+    public static boolean hasCamera(final Context context) {
+        final PackageManager pm = context.getPackageManager();
+        // JellyBean support.
+        boolean hasCamera = pm.hasSystemFeature(PackageManager.FEATURE_CAMERA);
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+            hasCamera |= pm.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY);
+        }
+        return hasCamera;
+    }
+
+    @TargetApi(Build.VERSION_CODES.KITKAT)
+    private static boolean detectLowEndDevice() {
+        assert CommandLine.isInitialized();
+        if (CommandLine.getInstance().hasSwitch(BaseSwitches.ENABLE_LOW_END_DEVICE_MODE)) {
+            return true;
+        }
+        if (CommandLine.getInstance().hasSwitch(BaseSwitches.DISABLE_LOW_END_DEVICE_MODE)) {
+            return false;
+        }
+
+        sAmountOfPhysicalMemoryKB = detectAmountOfPhysicalMemoryKB();
+        boolean isLowEnd = true;
+        if (sAmountOfPhysicalMemoryKB <= 0) {
+            isLowEnd = false;
+        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+            isLowEnd = sAmountOfPhysicalMemoryKB / 1024 <= ANDROID_O_LOW_MEMORY_DEVICE_THRESHOLD_MB;
+        } else {
+            isLowEnd = sAmountOfPhysicalMemoryKB / 1024 <= ANDROID_LOW_MEMORY_DEVICE_THRESHOLD_MB;
+        }
+
+        // For evaluation purposes check whether our computation agrees with Android API value.
+        Context appContext = ContextUtils.getApplicationContext();
+        boolean isLowRam = false;
+        if (appContext != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+            isLowRam = ((ActivityManager) ContextUtils.getApplicationContext().getSystemService(
+                                Context.ACTIVITY_SERVICE))
+                               .isLowRamDevice();
+        }
+        sLowEndMatches.record(isLowEnd == isLowRam);
+
+        return isLowEnd;
+    }
+
+    /**
+     * Creates a new trace event to log the number of minor / major page faults, if tracing is
+     * enabled.
+     */
+    public static void logPageFaultCountToTracing() {
+        nativeLogPageFaultCountToTracing();
+    }
+
+    private static native void nativeLogPageFaultCountToTracing();
+}
diff --git a/base/android/java/src/org/chromium/base/ThrowUncaughtException.java b/base/android/java/src/org/chromium/base/ThrowUncaughtException.java
new file mode 100644
index 0000000..d5f18a2
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/ThrowUncaughtException.java
@@ -0,0 +1,21 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base;
+
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.MainDex;
+
+@MainDex
+abstract class ThrowUncaughtException {
+    @CalledByNative
+    private static void post() {
+        ThreadUtils.postOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                throw new RuntimeException("Intentional exception not caught by JNI");
+            }
+        });
+    }
+}
diff --git a/base/android/java/src/org/chromium/base/TimeUtils.java b/base/android/java/src/org/chromium/base/TimeUtils.java
new file mode 100644
index 0000000..dcacabf
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/TimeUtils.java
@@ -0,0 +1,18 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base;
+
+import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.MainDex;
+
+/** Time-related utilities. */
+@JNINamespace("base::android")
+@MainDex
+public class TimeUtils {
+    private TimeUtils() {}
+
+    /** Returns TimeTicks::Now() in microseconds. */
+    public static native long nativeGetTimeTicksNowUs();
+}
diff --git a/base/android/java/src/org/chromium/base/TraceEvent.java b/base/android/java/src/org/chromium/base/TraceEvent.java
new file mode 100644
index 0000000..9659090
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/TraceEvent.java
@@ -0,0 +1,387 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base;
+
+import android.os.Looper;
+import android.os.MessageQueue;
+import android.os.SystemClock;
+import android.util.Log;
+import android.util.Printer;
+
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.MainDex;
+/**
+ * Java mirror of Chrome trace event API. See base/trace_event/trace_event.h.
+ *
+ * To get scoped trace events, use the "try with resource" construct, for instance:
+ * <pre>{@code
+ * try (TraceEvent e = TraceEvent.scoped("MyTraceEvent")) {
+ *   // code.
+ * }
+ * }</pre>
+ *
+ * It is OK to use tracing before the native library has loaded, in a slightly restricted fashion.
+ * @see EarlyTraceEvent for details.
+ */
+@JNINamespace("base::android")
+@MainDex
+public class TraceEvent implements AutoCloseable {
+    private static volatile boolean sEnabled;
+    private static volatile boolean sATraceEnabled; // True when taking an Android systrace.
+
+    private static class BasicLooperMonitor implements Printer {
+        private static final String EARLY_TOPLEVEL_TASK_NAME = "Looper.dispatchMessage: ";
+
+        @Override
+        public void println(final String line) {
+            if (line.startsWith(">")) {
+                beginHandling(line);
+            } else {
+                assert line.startsWith("<");
+                endHandling(line);
+            }
+        }
+
+        void beginHandling(final String line) {
+            // May return an out-of-date value. this is not an issue as EarlyTraceEvent#begin()
+            // will filter the event in this case.
+            boolean earlyTracingActive = EarlyTraceEvent.isActive();
+            if (sEnabled || earlyTracingActive) {
+                String target = getTarget(line);
+                if (sEnabled) {
+                    nativeBeginToplevel(target);
+                } else if (earlyTracingActive) {
+                    // Synthesize a task name instead of using a parameter, as early tracing doesn't
+                    // support parameters.
+                    EarlyTraceEvent.begin(EARLY_TOPLEVEL_TASK_NAME + target);
+                }
+            }
+        }
+
+        void endHandling(final String line) {
+            if (EarlyTraceEvent.isActive()) {
+                EarlyTraceEvent.end(EARLY_TOPLEVEL_TASK_NAME + getTarget(line));
+            }
+            if (sEnabled) nativeEndToplevel();
+        }
+
+        /**
+         * Android Looper formats |line| as ">>>>> Dispatching to (TARGET) [...]" since at least
+         * 2009 (Donut). Extracts the TARGET part of the message.
+         */
+        private static String getTarget(String logLine) {
+            int start = logLine.indexOf('(', 21); // strlen(">>>>> Dispatching to ")
+            int end = start == -1 ? -1 : logLine.indexOf(')', start);
+            return end != -1 ? logLine.substring(start + 1, end) : "";
+        }
+    }
+
+    /**
+     * A class that records, traces and logs statistics about the UI thead's Looper.
+     * The output of this class can be used in a number of interesting ways:
+     * <p>
+     * <ol><li>
+     * When using chrometrace, there will be a near-continuous line of
+     * measurements showing both event dispatches as well as idles;
+     * </li><li>
+     * Logging messages are output for events that run too long on the
+     * event dispatcher, making it easy to identify problematic areas;
+     * </li><li>
+     * Statistics are output whenever there is an idle after a non-trivial
+     * amount of activity, allowing information to be gathered about task
+     * density and execution cadence on the Looper;
+     * </li></ol>
+     * <p>
+     * The class attaches itself as an idle handler to the main Looper, and
+     * monitors the execution of events and idle notifications. Task counters
+     * accumulate between idle notifications and get reset when a new idle
+     * notification is received.
+     */
+    private static final class IdleTracingLooperMonitor extends BasicLooperMonitor
+            implements MessageQueue.IdleHandler {
+        // Tags for dumping to logcat or TraceEvent
+        private static final String TAG = "TraceEvent.LooperMonitor";
+        private static final String IDLE_EVENT_NAME = "Looper.queueIdle";
+
+        // Calculation constants
+        private static final long FRAME_DURATION_MILLIS = 1000L / 60L; // 60 FPS
+        // A reasonable threshold for defining a Looper event as "long running"
+        private static final long MIN_INTERESTING_DURATION_MILLIS =
+                FRAME_DURATION_MILLIS;
+        // A reasonable threshold for a "burst" of tasks on the Looper
+        private static final long MIN_INTERESTING_BURST_DURATION_MILLIS =
+                MIN_INTERESTING_DURATION_MILLIS * 3;
+
+        // Stats tracking
+        private long mLastIdleStartedAt;
+        private long mLastWorkStartedAt;
+        private int mNumTasksSeen;
+        private int mNumIdlesSeen;
+        private int mNumTasksSinceLastIdle;
+
+        // State
+        private boolean mIdleMonitorAttached;
+
+        // Called from within the begin/end methods only.
+        // This method can only execute on the looper thread, because that is
+        // the only thread that is permitted to call Looper.myqueue().
+        private final void syncIdleMonitoring() {
+            if (sEnabled && !mIdleMonitorAttached) {
+                // approximate start time for computational purposes
+                mLastIdleStartedAt = SystemClock.elapsedRealtime();
+                Looper.myQueue().addIdleHandler(this);
+                mIdleMonitorAttached = true;
+                Log.v(TAG, "attached idle handler");
+            } else if (mIdleMonitorAttached && !sEnabled) {
+                Looper.myQueue().removeIdleHandler(this);
+                mIdleMonitorAttached = false;
+                Log.v(TAG, "detached idle handler");
+            }
+        }
+
+        @Override
+        final void beginHandling(final String line) {
+            // Close-out any prior 'idle' period before starting new task.
+            if (mNumTasksSinceLastIdle == 0) {
+                TraceEvent.end(IDLE_EVENT_NAME);
+            }
+            mLastWorkStartedAt = SystemClock.elapsedRealtime();
+            syncIdleMonitoring();
+            super.beginHandling(line);
+        }
+
+        @Override
+        final void endHandling(final String line) {
+            final long elapsed = SystemClock.elapsedRealtime()
+                    - mLastWorkStartedAt;
+            if (elapsed > MIN_INTERESTING_DURATION_MILLIS) {
+                traceAndLog(Log.WARN, "observed a task that took "
+                        + elapsed + "ms: " + line);
+            }
+            super.endHandling(line);
+            syncIdleMonitoring();
+            mNumTasksSeen++;
+            mNumTasksSinceLastIdle++;
+        }
+
+        private static void traceAndLog(int level, String message) {
+            TraceEvent.instant("TraceEvent.LooperMonitor:IdleStats", message);
+            Log.println(level, TAG, message);
+        }
+
+        @Override
+        public final boolean queueIdle() {
+            final long now =  SystemClock.elapsedRealtime();
+            if (mLastIdleStartedAt == 0) mLastIdleStartedAt = now;
+            final long elapsed = now - mLastIdleStartedAt;
+            mNumIdlesSeen++;
+            TraceEvent.begin(IDLE_EVENT_NAME, mNumTasksSinceLastIdle + " tasks since last idle.");
+            if (elapsed > MIN_INTERESTING_BURST_DURATION_MILLIS) {
+                // Dump stats
+                String statsString = mNumTasksSeen + " tasks and "
+                        + mNumIdlesSeen + " idles processed so far, "
+                        + mNumTasksSinceLastIdle + " tasks bursted and "
+                        + elapsed + "ms elapsed since last idle";
+                traceAndLog(Log.DEBUG, statsString);
+            }
+            mLastIdleStartedAt = now;
+            mNumTasksSinceLastIdle = 0;
+            return true; // stay installed
+        }
+    }
+
+    // Holder for monitor avoids unnecessary construction on non-debug runs
+    private static final class LooperMonitorHolder {
+        private static final BasicLooperMonitor sInstance =
+                CommandLine.getInstance().hasSwitch(BaseSwitches.ENABLE_IDLE_TRACING)
+                ? new IdleTracingLooperMonitor() : new BasicLooperMonitor();
+    }
+
+    private final String mName;
+
+    /**
+     * Constructor used to support the "try with resource" construct.
+     */
+    private TraceEvent(String name, String arg) {
+        mName = name;
+        begin(name, arg);
+    }
+
+    @Override
+    public void close() {
+        end(mName);
+    }
+
+    /**
+     * Factory used to support the "try with resource" construct.
+     *
+     * Note that if tracing is not enabled, this will not result in allocating an object.
+     *
+     * @param name Trace event name.
+     * @param name The arguments of the event.
+     * @return a TraceEvent, or null if tracing is not enabled.
+     */
+    public static TraceEvent scoped(String name, String arg) {
+        if (!(EarlyTraceEvent.enabled() || enabled())) return null;
+        return new TraceEvent(name, arg);
+    }
+
+    /**
+     * Similar to {@link #scoped(String, String arg)}, but uses null for |arg|.
+     */
+    public static TraceEvent scoped(String name) {
+        return scoped(name, null);
+    }
+
+    /**
+     * Register an enabled observer, such that java traces are always enabled with native.
+     */
+    public static void registerNativeEnabledObserver() {
+        nativeRegisterEnabledObserver();
+    }
+
+    /**
+     * Notification from native that tracing is enabled/disabled.
+     */
+    @CalledByNative
+    public static void setEnabled(boolean enabled) {
+        if (enabled) EarlyTraceEvent.disable();
+        // Only disable logging if Chromium enabled it originally, so as to not disrupt logging done
+        // by other applications
+        if (sEnabled != enabled) {
+            sEnabled = enabled;
+            // Android M+ systrace logs this on its own. Only log it if not writing to Android
+            // systrace.
+            if (sATraceEnabled) return;
+            ThreadUtils.getUiThreadLooper().setMessageLogging(
+                    enabled ? LooperMonitorHolder.sInstance : null);
+        }
+    }
+
+    /**
+     * May enable early tracing depending on the environment.
+     *
+     * Must be called after the command-line has been read.
+     */
+    public static void maybeEnableEarlyTracing() {
+        EarlyTraceEvent.maybeEnable();
+        if (EarlyTraceEvent.isActive()) {
+            ThreadUtils.getUiThreadLooper().setMessageLogging(LooperMonitorHolder.sInstance);
+        }
+    }
+
+    /**
+     * Enables or disabled Android systrace path of Chrome tracing. If enabled, all Chrome
+     * traces will be also output to Android systrace. Because of the overhead of Android
+     * systrace, this is for WebView only.
+     */
+    public static void setATraceEnabled(boolean enabled) {
+        if (sATraceEnabled == enabled) return;
+        sATraceEnabled = enabled;
+        if (enabled) {
+            // Calls TraceEvent.setEnabled(true) via
+            // TraceLog::EnabledStateObserver::OnTraceLogEnabled
+            nativeStartATrace();
+        } else {
+            // Calls TraceEvent.setEnabled(false) via
+            // TraceLog::EnabledStateObserver::OnTraceLogDisabled
+            nativeStopATrace();
+        }
+    }
+
+    /**
+     * @return True if tracing is enabled, false otherwise.
+     * It is safe to call trace methods without checking if TraceEvent
+     * is enabled.
+     */
+    public static boolean enabled() {
+        return sEnabled;
+    }
+
+    /**
+     * Triggers the 'instant' native trace event with no arguments.
+     * @param name The name of the event.
+     */
+    public static void instant(String name) {
+        if (sEnabled) nativeInstant(name, null);
+    }
+
+    /**
+     * Triggers the 'instant' native trace event.
+     * @param name The name of the event.
+     * @param arg  The arguments of the event.
+     */
+    public static void instant(String name, String arg) {
+        if (sEnabled) nativeInstant(name, arg);
+    }
+
+    /**
+     * Triggers the 'start' native trace event with no arguments.
+     * @param name The name of the event.
+     * @param id   The id of the asynchronous event.
+     */
+    public static void startAsync(String name, long id) {
+        EarlyTraceEvent.startAsync(name, id);
+        if (sEnabled) nativeStartAsync(name, id);
+    }
+
+    /**
+     * Triggers the 'finish' native trace event with no arguments.
+     * @param name The name of the event.
+     * @param id   The id of the asynchronous event.
+     */
+    public static void finishAsync(String name, long id) {
+        EarlyTraceEvent.finishAsync(name, id);
+        if (sEnabled) nativeFinishAsync(name, id);
+    }
+
+    /**
+     * Triggers the 'begin' native trace event with no arguments.
+     * @param name The name of the event.
+     */
+    public static void begin(String name) {
+        begin(name, null);
+    }
+
+    /**
+     * Triggers the 'begin' native trace event.
+     * @param name The name of the event.
+     * @param arg  The arguments of the event.
+     */
+    public static void begin(String name, String arg) {
+        EarlyTraceEvent.begin(name);
+        if (sEnabled) nativeBegin(name, arg);
+    }
+
+    /**
+     * Triggers the 'end' native trace event with no arguments.
+     * @param name The name of the event.
+     */
+    public static void end(String name) {
+        end(name, null);
+    }
+
+    /**
+     * Triggers the 'end' native trace event.
+     * @param name The name of the event.
+     * @param arg  The arguments of the event.
+     */
+    public static void end(String name, String arg) {
+        EarlyTraceEvent.end(name);
+        if (sEnabled) nativeEnd(name, arg);
+    }
+
+    private static native void nativeRegisterEnabledObserver();
+    private static native void nativeStartATrace();
+    private static native void nativeStopATrace();
+    private static native void nativeInstant(String name, String arg);
+    private static native void nativeBegin(String name, String arg);
+    private static native void nativeEnd(String name, String arg);
+    private static native void nativeBeginToplevel(String target);
+    private static native void nativeEndToplevel();
+    private static native void nativeStartAsync(String name, long id);
+    private static native void nativeFinishAsync(String name, long id);
+}
diff --git a/base/android/java/src/org/chromium/base/UnguessableToken.java b/base/android/java/src/org/chromium/base/UnguessableToken.java
new file mode 100644
index 0000000..4b1619d
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/UnguessableToken.java
@@ -0,0 +1,91 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import org.chromium.base.annotations.CalledByNative;
+
+/**
+ * This class mirrors unguessable_token.h .  Since tokens are passed by value,
+ * we don't bother to maintain a native token.  This implements Parcelable so
+ * that it may be sent via binder.
+ *
+ * To get one of these from native, one must start with a
+ * base::UnguessableToken, then create a Java object from it.  See
+ * jni_unguessable_token.h for information.
+ */
+public class UnguessableToken implements Parcelable {
+    private final long mHigh;
+    private final long mLow;
+
+    private UnguessableToken(long high, long low) {
+        mHigh = high;
+        mLow = low;
+    }
+
+    @CalledByNative
+    private static UnguessableToken create(long high, long low) {
+        return new UnguessableToken(high, low);
+    }
+
+    @CalledByNative
+    public long getHighForSerialization() {
+        return mHigh;
+    }
+
+    @CalledByNative
+    public long getLowForSerialization() {
+        return mLow;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeLong(mHigh);
+        dest.writeLong(mLow);
+    }
+
+    public static final Parcelable.Creator<UnguessableToken> CREATOR =
+            new Parcelable.Creator<UnguessableToken>() {
+                @Override
+                public UnguessableToken createFromParcel(Parcel source) {
+                    long high = source.readLong();
+                    long low = source.readLong();
+                    if (high == 0 || low == 0) {
+                        // Refuse to create an empty UnguessableToken.
+                        return null;
+                    }
+                    return new UnguessableToken(high, low);
+                }
+
+                @Override
+                public UnguessableToken[] newArray(int size) {
+                    return new UnguessableToken[size];
+                }
+            };
+
+    // To avoid unwieldy calls in JNI for tests, parcel and unparcel.
+    // TODO(liberato): It would be nice if we could include this only with a
+    // java driver that's linked only with unit tests, but i don't see a way
+    // to do that.
+    @CalledByNative
+    private UnguessableToken parcelAndUnparcelForTesting() {
+        Parcel parcel = Parcel.obtain();
+        writeToParcel(parcel, 0);
+
+        // Rewind the parcel and un-parcel.
+        parcel.setDataPosition(0);
+        UnguessableToken token = CREATOR.createFromParcel(parcel);
+        parcel.recycle();
+
+        return token;
+    }
+};
diff --git a/base/android/java/src/org/chromium/base/annotations/DoNotInline.java b/base/android/java/src/org/chromium/base/annotations/DoNotInline.java
new file mode 100644
index 0000000..9252f3a
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/annotations/DoNotInline.java
@@ -0,0 +1,20 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * The annotated method or class should never be inlined.
+ *
+ * The annotated method (or methods on the annotated class) are guaranteed not to be inlined by
+ * Proguard. Other optimizations may still apply.
+ */
+@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.TYPE})
+@Retention(RetentionPolicy.CLASS)
+public @interface DoNotInline {}
diff --git a/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java b/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java
new file mode 100644
index 0000000..5bc6204
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java
@@ -0,0 +1,829 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base.library_loader;
+
+import static org.chromium.base.metrics.CachedMetrics.EnumeratedHistogramSample;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.os.Build;
+import android.os.Build.VERSION_CODES;
+import android.os.Process;
+import android.os.StrictMode;
+import android.os.SystemClock;
+import android.support.annotation.NonNull;
+import android.support.v4.content.ContextCompat;
+import android.system.Os;
+
+import org.chromium.base.AsyncTask;
+import org.chromium.base.BuildConfig;
+import org.chromium.base.BuildInfo;
+import org.chromium.base.CommandLine;
+import org.chromium.base.ContextUtils;
+import org.chromium.base.FileUtils;
+import org.chromium.base.Log;
+import org.chromium.base.SysUtils;
+import org.chromium.base.TraceEvent;
+import org.chromium.base.VisibleForTesting;
+import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.MainDex;
+import org.chromium.base.metrics.RecordHistogram;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.zip.ZipFile;
+
+import javax.annotation.Nullable;
+
+/**
+ * This class provides functionality to load and register the native libraries.
+ * Callers are allowed to separate loading the libraries from initializing them.
+ * This may be an advantage for Android Webview, where the libraries can be loaded
+ * by the zygote process, but then needs per process initialization after the
+ * application processes are forked from the zygote process.
+ *
+ * The libraries may be loaded and initialized from any thread. Synchronization
+ * primitives are used to ensure that overlapping requests from different
+ * threads are handled sequentially.
+ *
+ * See also base/android/library_loader/library_loader_hooks.cc, which contains
+ * the native counterpart to this class.
+ */
+@MainDex
+@JNINamespace("base::android")
+public class LibraryLoader {
+    private static final String TAG = "LibraryLoader";
+
+    // Set to true to enable debug logs.
+    private static final boolean DEBUG = false;
+
+    // Experience shows that on some devices, the PackageManager fails to properly extract
+    // native shared libraries to the /data partition at installation or upgrade time,
+    // which creates all kind of chaos (https://crbug.com/806998).
+    //
+    // We implement a fallback when we detect the issue by manually extracting the library
+    // into Chromium's own data directory, then retrying to load the new library from here.
+    //
+    // This will work for any device running K-. Starting with Android L, render processes
+    // cannot access the file system anymore, and extraction will always fail for them.
+    // However, the issue doesn't seem to appear in the field for Android L.
+    //
+    // Also, starting with M, the issue doesn't exist if shared libraries are stored
+    // uncompressed in the APK (as Chromium does), because the system linker can access them
+    // directly, and the PackageManager will thus never extract them in the first place.
+    static public final boolean PLATFORM_REQUIRES_NATIVE_FALLBACK_EXTRACTION =
+            Build.VERSION.SDK_INT <= VERSION_CODES.KITKAT;
+
+    // Location of extracted native libraries.
+    private static final String LIBRARY_DIR = "native_libraries";
+
+    // SharedPreferences key for "don't prefetch libraries" flag
+    private static final String DONT_PREFETCH_LIBRARIES_KEY = "dont_prefetch_libraries";
+
+    private static final EnumeratedHistogramSample sRelinkerCountHistogram =
+            new EnumeratedHistogramSample("ChromiumAndroidLinker.RelinkerFallbackCount", 2);
+
+    // The singleton instance of LibraryLoader. Never null (not final for tests).
+    private static LibraryLoader sInstance = new LibraryLoader();
+
+    // One-way switch becomes true when the libraries are initialized (
+    // by calling nativeLibraryLoaded, which forwards to LibraryLoaded(...) in
+    // library_loader_hooks.cc).
+    // Note that this member should remain a one-way switch, since it accessed from multiple
+    // threads without a lock.
+    private volatile boolean mInitialized;
+
+    // One-way switch that becomes true once
+    // {@link asyncPrefetchLibrariesToMemory} has been called.
+    private final AtomicBoolean mPrefetchLibraryHasBeenCalled = new AtomicBoolean();
+
+    // Guards all fields below.
+    private final Object mLock = new Object();
+
+    private NativeLibraryPreloader mLibraryPreloader;
+    private boolean mLibraryPreloaderCalled;
+
+    // One-way switch becomes true when the libraries are loaded.
+    private boolean mLoaded;
+
+    // One-way switch becomes true when the Java command line is switched to
+    // native.
+    private boolean mCommandLineSwitched;
+
+    // One-way switches recording attempts to use Relro sharing in the browser.
+    // The flags are used to report UMA stats later.
+    private boolean mIsUsingBrowserSharedRelros;
+    private boolean mLoadAtFixedAddressFailed;
+
+    // One-way switch becomes true if the Chromium library was loaded from the
+    // APK file directly.
+    private boolean mLibraryWasLoadedFromApk;
+
+    // The type of process the shared library is loaded in.
+    private @LibraryProcessType int mLibraryProcessType;
+
+    // The number of milliseconds it took to load all the native libraries, which
+    // will be reported via UMA. Set once when the libraries are done loading.
+    private long mLibraryLoadTimeMs;
+
+    // The return value of NativeLibraryPreloader.loadLibrary(), which will be reported
+    // via UMA, it is initialized to the invalid value which shouldn't showup in UMA
+    // report.
+    private int mLibraryPreloaderStatus = -1;
+
+    /**
+     * Call this method to determine if this chromium project must
+     * use this linker. If not, System.loadLibrary() should be used to load
+     * libraries instead.
+     */
+    public static boolean useCrazyLinker() {
+        // TODO(digit): Remove this early return GVR is loadable.
+        // A non-monochrome APK (such as ChromePublic.apk or ChromeModernPublic.apk) on N+ cannot
+        // use the Linker because the latter is incompatible with the GVR library. Fall back
+        // to using System.loadLibrary() or System.load() at the cost of no RELRO sharing.
+        //
+        // A non-monochrome APK (such as ChromePublic.apk) can be installed on N+ in these
+        // circumstances:
+        // * installing APK manually
+        // * after OTA from M to N
+        // * side-installing Chrome (possibly from another release channel)
+        // * Play Store bugs leading to incorrect APK flavor being installed
+        //
+        if (Build.VERSION.SDK_INT >= VERSION_CODES.N) return false;
+
+        // The auto-generated NativeLibraries.sUseLinker variable will be true if the
+        // build has not explicitly disabled Linker features.
+        return NativeLibraries.sUseLinker;
+    }
+
+    /**
+     * Call this method to determine if the chromium project must load the library
+     * directly from a zip file.
+     */
+    private static boolean isInZipFile() {
+        // The auto-generated NativeLibraries.sUseLibraryInZipFile variable will be true
+        // iff the library remains embedded in the APK zip file on the target.
+        return NativeLibraries.sUseLibraryInZipFile;
+    }
+
+    /**
+     * Set native library preloader, if set, the NativeLibraryPreloader.loadLibrary will be invoked
+     * before calling System.loadLibrary, this only applies when not using the chromium linker.
+     *
+     * @param loader the NativeLibraryPreloader, it shall only be set once and before the
+     *               native library loaded.
+     */
+    public void setNativeLibraryPreloader(NativeLibraryPreloader loader) {
+        synchronized (mLock) {
+            assert mLibraryPreloader == null && !mLoaded;
+            mLibraryPreloader = loader;
+        }
+    }
+
+    public static LibraryLoader getInstance() {
+        return sInstance;
+    }
+
+    private LibraryLoader() {}
+
+    /**
+     *  This method blocks until the library is fully loaded and initialized.
+     *
+     * @param processType the process the shared library is loaded in.
+     */
+    public void ensureInitialized(@LibraryProcessType int processType) throws ProcessInitException {
+        synchronized (mLock) {
+            if (mInitialized) {
+                // Already initialized, nothing to do.
+                return;
+            }
+            loadAlreadyLocked(ContextUtils.getApplicationContext());
+            initializeAlreadyLocked(processType);
+        }
+    }
+
+    /**
+     * Calls native library preloader (see {@link #setNativeLibraryPreloader}) with the app
+     * context. If there is no preloader set, this function does nothing.
+     * Preloader is called only once, so calling it explicitly via this method means
+     * that it won't be (implicitly) called during library loading.
+     */
+    public void preloadNow() {
+        preloadNowOverrideApplicationContext(ContextUtils.getApplicationContext());
+    }
+
+    /**
+     * Similar to {@link #preloadNow}, but allows specifying app context to use.
+     */
+    public void preloadNowOverrideApplicationContext(Context appContext) {
+        synchronized (mLock) {
+            if (!useCrazyLinker()) {
+                preloadAlreadyLocked(appContext);
+            }
+        }
+    }
+
+    private void preloadAlreadyLocked(Context appContext) {
+        try (TraceEvent te = TraceEvent.scoped("LibraryLoader.preloadAlreadyLocked")) {
+            // Preloader uses system linker, we shouldn't preload if Chromium linker is used.
+            assert !useCrazyLinker();
+            if (mLibraryPreloader != null && !mLibraryPreloaderCalled) {
+                mLibraryPreloaderStatus = mLibraryPreloader.loadLibrary(appContext);
+                mLibraryPreloaderCalled = true;
+            }
+        }
+    }
+
+    /**
+     * Checks if library is fully loaded and initialized.
+     */
+    public boolean isInitialized() {
+        return mInitialized;
+    }
+
+    /**
+     * Loads the library and blocks until the load completes. The caller is responsible
+     * for subsequently calling ensureInitialized().
+     * May be called on any thread, but should only be called once. Note the thread
+     * this is called on will be the thread that runs the native code's static initializers.
+     * See the comment in doInBackground() for more considerations on this.
+     *
+     * @throws ProcessInitException if the native library failed to load.
+     */
+    public void loadNow() throws ProcessInitException {
+        loadNowOverrideApplicationContext(ContextUtils.getApplicationContext());
+    }
+
+    /**
+     * Override kept for callers that need to load from a different app context. Do not use unless
+     * specifically required to load from another context that is not the current process's app
+     * context.
+     *
+     * @param appContext The overriding app context to be used to load libraries.
+     * @throws ProcessInitException if the native library failed to load with this context.
+     */
+    public void loadNowOverrideApplicationContext(Context appContext) throws ProcessInitException {
+        synchronized (mLock) {
+            if (mLoaded && appContext != ContextUtils.getApplicationContext()) {
+                throw new IllegalStateException("Attempt to load again from alternate context.");
+            }
+            loadAlreadyLocked(appContext);
+        }
+    }
+
+    /**
+     * Initializes the library here and now: must be called on the thread that the
+     * native will call its "main" thread. The library must have previously been
+     * loaded with loadNow.
+     *
+     * @param processType the process the shared library is loaded in.
+     */
+    public void initialize(@LibraryProcessType int processType) throws ProcessInitException {
+        synchronized (mLock) {
+            initializeAlreadyLocked(processType);
+        }
+    }
+
+    /**
+     * Disables prefetching for subsequent runs. The value comes from "DontPrefetchLibraries"
+     * finch experiment, and is pushed on every run. I.e. the effect of the finch experiment
+     * lags by one run, which is the best we can do considering that prefetching happens way
+     * before finch is initialized. Note that since LibraryLoader is in //base, it can't depend
+     * on ChromeFeatureList, and has to rely on external code pushing the value.
+     *
+     * @param dontPrefetch whether not to prefetch libraries
+     */
+    public static void setDontPrefetchLibrariesOnNextRuns(boolean dontPrefetch) {
+        ContextUtils.getAppSharedPreferences()
+                .edit()
+                .putBoolean(DONT_PREFETCH_LIBRARIES_KEY, dontPrefetch)
+                .apply();
+    }
+
+    /**
+     * @return whether not to prefetch libraries (see setDontPrefetchLibrariesOnNextRun()).
+     */
+    private static boolean isNotPrefetchingLibraries() {
+        // This might be the first time getAppSharedPreferences() is used, so relax strict mode
+        // to allow disk reads.
+        StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
+        try {
+            return ContextUtils.getAppSharedPreferences().getBoolean(
+                    DONT_PREFETCH_LIBRARIES_KEY, false);
+        } finally {
+            StrictMode.setThreadPolicy(oldPolicy);
+        }
+    }
+
+    /** Prefetches the native libraries in a background thread.
+     *
+     * Launches an AsyncTask that, through a short-lived forked process, reads a
+     * part of each page of the native library.  This is done to warm up the
+     * page cache, turning hard page faults into soft ones.
+     *
+     * This is done this way, as testing shows that fadvise(FADV_WILLNEED) is
+     * detrimental to the startup time.
+     */
+    public void asyncPrefetchLibrariesToMemory() {
+        SysUtils.logPageFaultCountToTracing();
+        if (isNotPrefetchingLibraries()) return;
+
+        final boolean coldStart = mPrefetchLibraryHasBeenCalled.compareAndSet(false, true);
+
+        // Collection should start close to the native library load, but doesn't have
+        // to be simultaneous with it. Also, don't prefetch in this case, as this would
+        // skew the results.
+        if (coldStart && CommandLine.getInstance().hasSwitch("log-native-library-residency")) {
+            // nativePeriodicallyCollectResidency() sleeps, run it on another thread,
+            // and not on the AsyncTask thread pool.
+            new Thread(LibraryLoader::nativePeriodicallyCollectResidency).start();
+            return;
+        }
+
+        new LibraryPrefetchTask(coldStart).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+    }
+
+    private static class LibraryPrefetchTask extends AsyncTask<Void, Void, Void> {
+        private final boolean mColdStart;
+
+        public LibraryPrefetchTask(boolean coldStart) {
+            mColdStart = coldStart;
+        }
+
+        @Override
+        protected Void doInBackground(Void... params) {
+            try (TraceEvent e = TraceEvent.scoped("LibraryLoader.asyncPrefetchLibrariesToMemory")) {
+                int percentage = nativePercentageOfResidentNativeLibraryCode();
+                // Arbitrary percentage threshold. If most of the native library is already
+                // resident (likely with monochrome), don't bother creating a prefetch process.
+                boolean prefetch = mColdStart && percentage < 90;
+                if (prefetch) {
+                    nativeForkAndPrefetchNativeLibrary();
+                }
+                if (percentage != -1) {
+                    String histogram = "LibraryLoader.PercentageOfResidentCodeBeforePrefetch"
+                            + (mColdStart ? ".ColdStartup" : ".WarmStartup");
+                    RecordHistogram.recordPercentageHistogram(histogram, percentage);
+                }
+            }
+            return null;
+        }
+    }
+
+    // Helper for loadAlreadyLocked(). Load a native shared library with the Chromium linker.
+    // Sets UMA flags depending on the results of loading.
+    private void loadLibraryWithCustomLinkerAlreadyLocked(
+            Linker linker, @Nullable String zipFilePath, String libFilePath) {
+        assert Thread.holdsLock(mLock);
+        if (linker.isUsingBrowserSharedRelros()) {
+            // If the browser is set to attempt shared RELROs then we try first with shared
+            // RELROs enabled, and if that fails then retry without.
+            mIsUsingBrowserSharedRelros = true;
+            try {
+                linker.loadLibrary(libFilePath);
+            } catch (UnsatisfiedLinkError e) {
+                Log.w(TAG, "Failed to load native library with shared RELRO, retrying without");
+                mLoadAtFixedAddressFailed = true;
+                linker.loadLibraryNoFixedAddress(libFilePath);
+            }
+        } else {
+            // No attempt to use shared RELROs in the browser, so load as normal.
+            linker.loadLibrary(libFilePath);
+        }
+
+        // Loaded successfully, so record if we loaded directly from an APK.
+        if (zipFilePath != null) {
+            mLibraryWasLoadedFromApk = true;
+        }
+    }
+
+    static void incrementRelinkerCountHitHistogram() {
+        sRelinkerCountHistogram.record(1);
+    }
+
+    static void incrementRelinkerCountNotHitHistogram() {
+        sRelinkerCountHistogram.record(0);
+    }
+
+    // Experience shows that on some devices, the system sometimes fails to extract native libraries
+    // at installation or update time from the APK. This function will extract the library and
+    // return the extracted file path.
+    static String getExtractedLibraryPath(Context appContext, String libName) {
+        assert PLATFORM_REQUIRES_NATIVE_FALLBACK_EXTRACTION;
+        Log.w(TAG, "Failed to load libName %s, attempting fallback extraction then trying again",
+                libName);
+        String libraryEntry = LibraryLoader.makeLibraryPathInZipFile(libName, false, false);
+        return extractFileIfStale(appContext, libraryEntry, makeLibraryDirAndSetPermission());
+    }
+
+    // Invoke either Linker.loadLibrary(...), System.loadLibrary(...) or System.load(...),
+    // triggering JNI_OnLoad in native code.
+    // TODO(crbug.com/635567): Fix this properly.
+    @SuppressLint({"DefaultLocale", "NewApi", "UnsafeDynamicallyLoadedCode"})
+    private void loadAlreadyLocked(Context appContext) throws ProcessInitException {
+        try (TraceEvent te = TraceEvent.scoped("LibraryLoader.loadAlreadyLocked")) {
+            if (!mLoaded) {
+                assert !mInitialized;
+
+                long startTime = SystemClock.uptimeMillis();
+
+                if (useCrazyLinker()) {
+                    // Load libraries using the Chromium linker.
+                    Linker linker = Linker.getInstance();
+
+                    String apkFilePath =
+                            isInZipFile() ? appContext.getApplicationInfo().sourceDir : null;
+                    linker.prepareLibraryLoad(apkFilePath);
+
+                    for (String library : NativeLibraries.LIBRARIES) {
+                        // Don't self-load the linker. This is because the build system is
+                        // not clever enough to understand that all the libraries packaged
+                        // in the final .apk don't need to be explicitly loaded.
+                        if (linker.isChromiumLinkerLibrary(library)) {
+                            if (DEBUG) Log.i(TAG, "ignoring self-linker load");
+                            continue;
+                        }
+
+                        // Determine where the library should be loaded from.
+                        String libFilePath = System.mapLibraryName(library);
+                        if (apkFilePath != null) {
+                            Log.i(TAG, " Loading " + library + " from within " + apkFilePath);
+                        } else {
+                            Log.i(TAG, "Loading " + library);
+                        }
+
+                        try {
+                            // Load the library using this Linker. May throw UnsatisfiedLinkError.
+                            loadLibraryWithCustomLinkerAlreadyLocked(
+                                    linker, apkFilePath, libFilePath);
+                            incrementRelinkerCountNotHitHistogram();
+                        } catch (UnsatisfiedLinkError e) {
+                            if (!isInZipFile()
+                                    && PLATFORM_REQUIRES_NATIVE_FALLBACK_EXTRACTION) {
+                                loadLibraryWithCustomLinkerAlreadyLocked(
+                                        linker, null, getExtractedLibraryPath(appContext, library));
+                                incrementRelinkerCountHitHistogram();
+                            } else {
+                                Log.e(TAG, "Unable to load library: " + library);
+                                throw(e);
+                            }
+                        }
+                    }
+
+                    linker.finishLibraryLoad();
+                } else {
+                    setEnvForNative();
+                    preloadAlreadyLocked(appContext);
+
+                    // If the libraries are located in the zip file, assert that the device API
+                    // level is M or higher. On devices lower than M, the libraries should
+                    // always be loaded by Linker.
+                    assert !isInZipFile() || Build.VERSION.SDK_INT >= VERSION_CODES.M;
+
+                    // Load libraries using the system linker.
+                    for (String library : NativeLibraries.LIBRARIES) {
+                        try {
+                            if (!isInZipFile()) {
+                                // The extract and retry logic isn't needed because this path is
+                                // used only for local development.
+                                System.loadLibrary(library);
+                            } else {
+                                // Load directly from the APK.
+                                boolean is64Bit = Process.is64Bit();
+                                String zipFilePath = appContext.getApplicationInfo().sourceDir;
+                                // In API level 23 and above, it’s possible to open a .so file
+                                // directly from the APK of the path form
+                                // "my_zip_file.zip!/libs/libstuff.so". See:
+                                // https://android.googlesource.com/platform/bionic/+/master/android-changes-for-ndk-developers.md#opening-shared-libraries-directly-from-an-apk
+                                String libraryName = zipFilePath + "!/"
+                                        + makeLibraryPathInZipFile(library, true, is64Bit);
+                                Log.i(TAG, "libraryName: " + libraryName);
+                                System.load(libraryName);
+                            }
+                        } catch (UnsatisfiedLinkError e) {
+                            Log.e(TAG, "Unable to load library: " + library);
+                            throw(e);
+                        }
+                    }
+                }
+
+                long stopTime = SystemClock.uptimeMillis();
+                mLibraryLoadTimeMs = stopTime - startTime;
+                Log.i(TAG, String.format("Time to load native libraries: %d ms (timestamps %d-%d)",
+                        mLibraryLoadTimeMs,
+                        startTime % 10000,
+                        stopTime % 10000));
+
+                mLoaded = true;
+            }
+        } catch (UnsatisfiedLinkError e) {
+            throw new ProcessInitException(LoaderErrors.LOADER_ERROR_NATIVE_LIBRARY_LOAD_FAILED, e);
+        }
+    }
+
+    /**
+     * @param library The library name that is looking for.
+     * @param crazyPrefix true iff adding crazy linker prefix to the file name.
+     * @param is64Bit true if the caller think it's run on a 64 bit device.
+     * @return the library path name in the zip file.
+     */
+    @NonNull
+    public static String makeLibraryPathInZipFile(
+            String library, boolean crazyPrefix, boolean is64Bit) {
+        // Determine the ABI string that Android uses to find native libraries. Values are described
+        // in: https://developer.android.com/ndk/guides/abis.html
+        // The 'armeabi' is omitted here because it is not supported in Chrome/WebView, while Cronet
+        // and Cast load the native library via other paths.
+        String cpuAbi;
+        switch (NativeLibraries.sCpuFamily) {
+            case NativeLibraries.CPU_FAMILY_ARM:
+                cpuAbi = is64Bit ? "arm64-v8a" : "armeabi-v7a";
+                break;
+            case NativeLibraries.CPU_FAMILY_X86:
+                cpuAbi = is64Bit ? "x86_64" : "x86";
+                break;
+            case NativeLibraries.CPU_FAMILY_MIPS:
+                cpuAbi = is64Bit ? "mips64" : "mips";
+                break;
+            default:
+                throw new RuntimeException("Unknown CPU ABI for native libraries");
+        }
+
+        // When both the Chromium linker and zip-uncompressed native libraries are used,
+        // the build system renames the native shared libraries with a 'crazy.' prefix
+        // (e.g. "/lib/armeabi-v7a/libfoo.so" -> "/lib/armeabi-v7a/crazy.libfoo.so").
+        //
+        // This prevents the package manager from extracting them at installation/update time
+        // to the /data directory. The libraries can still be accessed directly by the Chromium
+        // linker from the APK.
+        String crazyPart = crazyPrefix ? "crazy." : "";
+        return String.format("lib/%s/%s%s", cpuAbi, crazyPart, System.mapLibraryName(library));
+    }
+
+    // The WebView requires the Command Line to be switched over before
+    // initialization is done. This is okay in the WebView's case since the
+    // JNI is already loaded by this point.
+    public void switchCommandLineForWebView() {
+        synchronized (mLock) {
+            ensureCommandLineSwitchedAlreadyLocked();
+        }
+    }
+
+    // Switch the CommandLine over from Java to native if it hasn't already been done.
+    // This must happen after the code is loaded and after JNI is ready (since after the
+    // switch the Java CommandLine will delegate all calls the native CommandLine).
+    private void ensureCommandLineSwitchedAlreadyLocked() {
+        assert mLoaded;
+        if (mCommandLineSwitched) {
+            return;
+        }
+        CommandLine.enableNativeProxy();
+        mCommandLineSwitched = true;
+    }
+
+    // Invoke base::android::LibraryLoaded in library_loader_hooks.cc
+    private void initializeAlreadyLocked(@LibraryProcessType int processType)
+            throws ProcessInitException {
+        if (mInitialized) {
+            if (mLibraryProcessType != processType) {
+                throw new ProcessInitException(
+                        LoaderErrors.LOADER_ERROR_NATIVE_LIBRARY_LOAD_FAILED);
+            }
+            return;
+        }
+        mLibraryProcessType = processType;
+
+        ensureCommandLineSwitchedAlreadyLocked();
+
+        if (!nativeLibraryLoaded(mLibraryProcessType)) {
+            Log.e(TAG, "error calling nativeLibraryLoaded");
+            throw new ProcessInitException(LoaderErrors.LOADER_ERROR_FAILED_TO_REGISTER_JNI);
+        }
+
+        // Check that the version of the library we have loaded matches the version we expect
+        Log.i(TAG, String.format("Expected native library version number \"%s\", "
+                                   + "actual native library version number \"%s\"",
+                           NativeLibraries.sVersionNumber, nativeGetVersionNumber()));
+        if (!NativeLibraries.sVersionNumber.equals(nativeGetVersionNumber())) {
+            throw new ProcessInitException(LoaderErrors.LOADER_ERROR_NATIVE_LIBRARY_WRONG_VERSION);
+        }
+
+        // From now on, keep tracing in sync with native.
+        TraceEvent.registerNativeEnabledObserver();
+
+        if (processType == LibraryProcessType.PROCESS_BROWSER
+                && PLATFORM_REQUIRES_NATIVE_FALLBACK_EXTRACTION) {
+            // Perform the detection and deletion of obsolete native libraries on a background
+            // background thread.
+            AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() {
+                @Override
+                public void run() {
+                    final String suffix = BuildInfo.getInstance().extractedFileSuffix;
+                    final File[] files = getLibraryDir().listFiles();
+                    if (files == null) return;
+
+                    for (File file : files) {
+                        // NOTE: Do not simply look for <suffix> at the end of the file.
+                        //
+                        // Extracted library files have names like 'libfoo.so<suffix>', but
+                        // extractFileIfStale() will use FileUtils.copyFileStreamAtomicWithBuffer()
+                        // to create them, and this method actually uses a transient temporary file
+                        // named like 'libfoo.so<suffix>.tmp' to do that. These temporary files, if
+                        // detected here, should be preserved; hence the reason why contains() is
+                        // used below.
+                        if (!file.getName().contains(suffix)) {
+                            String fileName = file.getName();
+                            if (!file.delete()) {
+                                Log.w(TAG, "Unable to remove %s", fileName);
+                            } else {
+                                Log.i(TAG, "Removed obsolete file %s", fileName);
+                            }
+                        }
+                    }
+                }
+            });
+        }
+
+        // From this point on, native code is ready to use and checkIsReady()
+        // shouldn't complain from now on (and in fact, it's used by the
+        // following calls).
+        // Note that this flag can be accessed asynchronously, so any initialization
+        // must be performed before.
+        mInitialized = true;
+    }
+
+    // Called after all native initializations are complete.
+    public void onNativeInitializationComplete() {
+        synchronized (mLock) {
+            recordBrowserProcessHistogramAlreadyLocked();
+        }
+    }
+
+    // Record Chromium linker histogram state for the main browser process. Called from
+    // onNativeInitializationComplete().
+    private void recordBrowserProcessHistogramAlreadyLocked() {
+        assert Thread.holdsLock(mLock);
+        if (useCrazyLinker()) {
+            nativeRecordChromiumAndroidLinkerBrowserHistogram(mIsUsingBrowserSharedRelros,
+                    mLoadAtFixedAddressFailed,
+                    mLibraryWasLoadedFromApk ? LibraryLoadFromApkStatusCodes.SUCCESSFUL
+                                             : LibraryLoadFromApkStatusCodes.UNKNOWN,
+                    mLibraryLoadTimeMs);
+        }
+        if (mLibraryPreloader != null) {
+            nativeRecordLibraryPreloaderBrowserHistogram(mLibraryPreloaderStatus);
+        }
+    }
+
+    // Register pending Chromium linker histogram state for renderer processes. This cannot be
+    // recorded as a histogram immediately because histograms and IPC are not ready at the
+    // time it are captured. This function stores a pending value, so that a later call to
+    // RecordChromiumAndroidLinkerRendererHistogram() will record it correctly.
+    public void registerRendererProcessHistogram(boolean requestedSharedRelro,
+                                                 boolean loadAtFixedAddressFailed) {
+        synchronized (mLock) {
+            if (useCrazyLinker()) {
+                nativeRegisterChromiumAndroidLinkerRendererHistogram(
+                        requestedSharedRelro, loadAtFixedAddressFailed, mLibraryLoadTimeMs);
+            }
+            if (mLibraryPreloader != null) {
+                nativeRegisterLibraryPreloaderRendererHistogram(mLibraryPreloaderStatus);
+            }
+        }
+    }
+
+    /**
+     * Override the library loader (normally with a mock) for testing.
+     * @param loader the mock library loader.
+     */
+    @VisibleForTesting
+    public static void setLibraryLoaderForTesting(LibraryLoader loader) {
+        sInstance = loader;
+    }
+
+    /**
+     * Configure ubsan using $UBSAN_OPTIONS. This function needs to be called before any native
+     * libraries are loaded because ubsan reads its configuration from $UBSAN_OPTIONS when the
+     * native library is loaded.
+     */
+    public static void setEnvForNative() {
+        // The setenv API was added in L. On older versions of Android, we should still see ubsan
+        // reports, but they will not have stack traces.
+        if (BuildConfig.IS_UBSAN && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+            try {
+                // This value is duplicated in build/android/pylib/constants/__init__.py.
+                Os.setenv("UBSAN_OPTIONS",
+                        "print_stacktrace=1 stack_trace_format='#%n pc %o %m' "
+                                + "handle_segv=0 handle_sigbus=0 handle_sigfpe=0",
+                        true);
+            } catch (Exception e) {
+                Log.w(TAG, "failed to set UBSAN_OPTIONS", e);
+            }
+        }
+    }
+
+    // Android system sometimes fails to extract libraries from APK (https://crbug.com/806998).
+    // This function manually extract libraries as a fallback.
+    @SuppressLint({"SetWorldReadable"})
+    private static String extractFileIfStale(
+            Context appContext, String pathWithinApk, File destDir) {
+        assert PLATFORM_REQUIRES_NATIVE_FALLBACK_EXTRACTION;
+
+        String apkPath = appContext.getApplicationInfo().sourceDir;
+        String fileName =
+                (new File(pathWithinApk)).getName() + BuildInfo.getInstance().extractedFileSuffix;
+        File libraryFile = new File(destDir, fileName);
+
+        if (!libraryFile.exists()) {
+            try (ZipFile zipFile = new ZipFile(apkPath);
+                    InputStream inputStream =
+                            zipFile.getInputStream(zipFile.getEntry(pathWithinApk))) {
+                if (zipFile.getEntry(pathWithinApk) == null)
+                    throw new RuntimeException("Cannot find ZipEntry" + pathWithinApk);
+
+                FileUtils.copyFileStreamAtomicWithBuffer(
+                        inputStream, libraryFile, new byte[16 * 1024]);
+                libraryFile.setReadable(true, false);
+                libraryFile.setExecutable(true, false);
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+        }
+        return libraryFile.getAbsolutePath();
+    }
+
+    // Ensure the extracted native libraries is created with the right permissions.
+    private static File makeLibraryDirAndSetPermission() {
+        if (!ContextUtils.isIsolatedProcess()) {
+            File cacheDir = ContextCompat.getCodeCacheDir(ContextUtils.getApplicationContext());
+            File libDir = new File(cacheDir, LIBRARY_DIR);
+            cacheDir.mkdir();
+            cacheDir.setExecutable(true, false);
+            libDir.mkdir();
+            libDir.setExecutable(true, false);
+        }
+        return getLibraryDir();
+    }
+
+    // Return File object for the directory containing extracted native libraries.
+    private static File getLibraryDir() {
+        return new File(
+                ContextCompat.getCodeCacheDir(ContextUtils.getApplicationContext()), LIBRARY_DIR);
+    }
+
+    // Only methods needed before or during normal JNI registration are during System.OnLoad.
+    // nativeLibraryLoaded is then called to register everything else.  This process is called
+    // "initialization".  This method will be mapped (by generated code) to the LibraryLoaded
+    // definition in base/android/library_loader/library_loader_hooks.cc.
+    //
+    // Return true on success and false on failure.
+    private native boolean nativeLibraryLoaded(@LibraryProcessType int processType);
+
+    // Method called to record statistics about the Chromium linker operation for the main
+    // browser process. Indicates whether the linker attempted relro sharing for the browser,
+    // and if it did, whether the library failed to load at a fixed address. Also records
+    // support for loading a library directly from the APK file, and the number of milliseconds
+    // it took to load the libraries.
+    private native void nativeRecordChromiumAndroidLinkerBrowserHistogram(
+            boolean isUsingBrowserSharedRelros,
+            boolean loadAtFixedAddressFailed,
+            int libraryLoadFromApkStatus,
+            long libraryLoadTime);
+
+    // Method called to record the return value of NativeLibraryPreloader.loadLibrary for the main
+    // browser process.
+    private native void nativeRecordLibraryPreloaderBrowserHistogram(int status);
+
+    // Method called to register (for later recording) statistics about the Chromium linker
+    // operation for a renderer process. Indicates whether the linker attempted relro sharing,
+    // and if it did, whether the library failed to load at a fixed address. Also records the
+    // number of milliseconds it took to load the libraries.
+    private native void nativeRegisterChromiumAndroidLinkerRendererHistogram(
+            boolean requestedSharedRelro,
+            boolean loadAtFixedAddressFailed,
+            long libraryLoadTime);
+
+    // Method called to register (for later recording) the return value of
+    // NativeLibraryPreloader.loadLibrary for a renderer process.
+    private native void nativeRegisterLibraryPreloaderRendererHistogram(int status);
+
+    // Get the version of the native library. This is needed so that we can check we
+    // have the right version before initializing the (rest of the) JNI.
+    private native String nativeGetVersionNumber();
+
+    // Finds the ranges corresponding to the native library pages, forks a new
+    // process to prefetch these pages and waits for it. The new process then
+    // terminates. This is blocking.
+    private static native void nativeForkAndPrefetchNativeLibrary();
+
+    // Returns the percentage of the native library code page that are currently reseident in
+    // memory.
+    private static native int nativePercentageOfResidentNativeLibraryCode();
+
+    // Periodically logs native library residency from this thread.
+    private static native void nativePeriodicallyCollectResidency();
+}
diff --git a/base/android/java/src/org/chromium/base/library_loader/Linker.java b/base/android/java/src/org/chromium/base/library_loader/Linker.java
new file mode 100644
index 0000000..5e30cfa
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/library_loader/Linker.java
@@ -0,0 +1,1160 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base.library_loader;
+
+import android.annotation.SuppressLint;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
+import android.os.Parcelable;
+
+import org.chromium.base.ContextUtils;
+import org.chromium.base.Log;
+import org.chromium.base.StreamUtil;
+import org.chromium.base.SysUtils;
+import org.chromium.base.ThreadUtils;
+import org.chromium.base.annotations.AccessedByNative;
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.MainDex;
+
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+import javax.annotation.Nullable;
+
+/*
+ * Technical note:
+ *
+ * The point of this class is to provide an alternative to System.loadLibrary()
+ * to load native shared libraries. One specific feature that it supports is the
+ * ability to save RAM by sharing the ELF RELRO sections between renderer
+ * processes.
+ *
+ * When two processes load the same native library at the _same_ memory address,
+ * the content of their RELRO section (which includes C++ vtables or any
+ * constants that contain pointers) will be largely identical [1].
+ *
+ * By default, the RELRO section is backed by private RAM in each process,
+ * which is still significant on mobile (e.g. 1.28 MB / process on Chrome 30 for
+ * Android).
+ *
+ * However, it is possible to save RAM by creating a shared memory region,
+ * copy the RELRO content into it, then have each process swap its private,
+ * regular RELRO, with a shared, read-only, mapping of the shared one.
+ *
+ * This trick saves 98% of the RELRO section size per extra process, after the
+ * first one. On the other hand, this requires careful communication between
+ * the process where the shared RELRO is created and the one(s) where it is used.
+ *
+ * Note that swapping the regular RELRO with the shared one is not an atomic
+ * operation. Care must be taken that no other thread tries to run native code
+ * that accesses it during it. In practice, this means the swap must happen
+ * before library native code is executed.
+ *
+ * [1] The exceptions are pointers to external, randomized, symbols, like
+ * those from some system libraries, but these are very few in practice.
+ */
+
+/*
+ * Security considerations:
+ *
+ * - Whether the browser process loads its native libraries at the same
+ *   addresses as the service ones (to save RAM by sharing the RELRO too)
+ *   depends on the configuration variable BROWSER_SHARED_RELRO_CONFIG.
+ *
+ *   Not using fixed library addresses in the browser process is preferred
+ *   for regular devices since it maintains the efficacy of ASLR as an
+ *   exploit mitigation across the render <-> browser privilege boundary.
+ *
+ * - The shared RELRO memory region is always forced read-only after creation,
+ *   which means it is impossible for a compromised service process to map
+ *   it read-write (e.g. by calling mmap() or mprotect()) and modify its
+ *   content, altering values seen in other service processes.
+ *
+ * - Once the RELRO ashmem region or file is mapped into a service process's
+ *   address space, the corresponding file descriptor is immediately closed. The
+ *   file descriptor is kept opened in the browser process, because a copy needs
+ *   to be sent to each new potential service process.
+ *
+ * - The common library load addresses are randomized for each instance of
+ *   the program on the device. See getRandomBaseLoadAddress() for more
+ *   details on how this is obtained.
+ *
+ * - When loading several libraries in service processes, a simple incremental
+ *   approach from the original random base load address is used. This is
+ *   sufficient to deal correctly with component builds (which can use dozens
+ *   of shared libraries), while regular builds always embed a single shared
+ *   library per APK.
+ */
+
+/**
+ * Here's an explanation of how this class is supposed to be used:
+ *
+ *  - Native shared libraries should be loaded with Linker.loadLibrary(),
+ *    instead of System.loadLibrary(). The two functions should behave the same
+ *    (at a high level).
+ *
+ *  - Before loading any library, prepareLibraryLoad() should be called.
+ *
+ *  - After loading all libraries, finishLibraryLoad() should be called, before
+ *    running any native code from any of the libraries (except their static
+ *    constructors, which can't be avoided).
+ *
+ *  - A service process shall call either initServiceProcess() or
+ *    disableSharedRelros() early (i.e. before any loadLibrary() call).
+ *    Otherwise, the linker considers that it is running inside the browser
+ *    process. This is because various Chromium projects have vastly
+ *    different initialization paths.
+ *
+ *    disableSharedRelros() completely disables shared RELROs, and loadLibrary()
+ *    will behave exactly like System.loadLibrary().
+ *
+ *    initServiceProcess(baseLoadAddress) indicates that shared RELROs are to be
+ *    used in this process.
+ *
+ *  - The browser is in charge of deciding where in memory each library should
+ *    be loaded. This address must be passed to each service process (see
+ *    ChromiumLinkerParams.java in content for a helper class to do so).
+ *
+ *  - The browser will also generate shared RELROs for each library it loads.
+ *    More specifically, by default when in the browser process, the linker
+ *    will:
+ *
+ *       - Load libraries randomly (just like System.loadLibrary()).
+ *       - Compute the fixed address to be used to load the same library
+ *         in service processes.
+ *       - Create a shared memory region populated with the RELRO region
+ *         content pre-relocated for the specific fixed address above.
+ *
+ *    Note that these shared RELRO regions cannot be used inside the browser
+ *    process. They are also never mapped into it.
+ *
+ *    This behaviour is altered by the BROWSER_SHARED_RELRO_CONFIG configuration
+ *    variable below, which may force the browser to load the libraries at
+ *    fixed addresses too.
+ *
+ *  - Once all libraries are loaded in the browser process, one can call
+ *    getSharedRelros() which returns a Bundle instance containing a map that
+ *    links each loaded library to its shared RELRO region.
+ *
+ *    This Bundle must be passed to each service process, for example through
+ *    a Binder call (note that the Bundle includes file descriptors and cannot
+ *    be added as an Intent extra).
+ *
+ *  - In a service process, finishLibraryLoad() and/or loadLibrary() may
+ *    block until the RELRO section Bundle is received. This is typically
+ *    done by calling useSharedRelros() from another thread.
+ *
+ *    This method also ensures the process uses the shared RELROs.
+ */
+public class Linker {
+    // Log tag for this class.
+    private static final String TAG = "LibraryLoader";
+
+    // Name of the library that contains our JNI code.
+    private static final String LINKER_JNI_LIBRARY = "chromium_android_linker";
+
+    // Constants used to control the behaviour of the browser process with
+    // regards to the shared RELRO section.
+    //   NEVER        -> The browser never uses it itself.
+    //   LOW_RAM_ONLY -> It is only used on devices with low RAM.
+    //   ALWAYS       -> It is always used.
+    // NOTE: These names are known and expected by the Linker test scripts.
+    public static final int BROWSER_SHARED_RELRO_CONFIG_NEVER = 0;
+    public static final int BROWSER_SHARED_RELRO_CONFIG_LOW_RAM_ONLY = 1;
+    public static final int BROWSER_SHARED_RELRO_CONFIG_ALWAYS = 2;
+
+    // Configuration variable used to control how the browser process uses the
+    // shared RELRO. Only change this while debugging linker-related issues.
+    // NOTE: This variable's name is known and expected by the Linker test scripts.
+    public static final int BROWSER_SHARED_RELRO_CONFIG =
+            BROWSER_SHARED_RELRO_CONFIG_LOW_RAM_ONLY;
+
+    // Constants used to control the memory device config. Can be set explicitly
+    // by setMemoryDeviceConfigForTesting().
+    //   INIT         -> Value is undetermined (will check at runtime).
+    //   LOW          -> This is a low-memory device.
+    //   NORMAL       -> This is not a low-memory device.
+    public static final int MEMORY_DEVICE_CONFIG_INIT = 0;
+    public static final int MEMORY_DEVICE_CONFIG_LOW = 1;
+    public static final int MEMORY_DEVICE_CONFIG_NORMAL = 2;
+
+    // Indicates if this is a low-memory device or not. The default is to
+    // determine this by probing the system at runtime, but this can be forced
+    // for testing by calling setMemoryDeviceConfigForTesting().
+    private int mMemoryDeviceConfig = MEMORY_DEVICE_CONFIG_INIT;
+
+    // Set to true to enable debug logs.
+    protected static final boolean DEBUG = false;
+
+    // Used to pass the shared RELRO Bundle through Binder.
+    public static final String EXTRA_LINKER_SHARED_RELROS =
+            "org.chromium.base.android.linker.shared_relros";
+
+    // Guards all access to the linker.
+    protected final Object mLock = new Object();
+
+    // The name of a class that implements TestRunner.
+    private String mTestRunnerClassName;
+
+    // Size of reserved Breakpad guard region. Should match the value of
+    // kBreakpadGuardRegionBytes on the JNI side. Used when computing the load
+    // addresses of multiple loaded libraries. Set to 0 to disable the guard.
+    private static final int BREAKPAD_GUARD_REGION_BYTES = 16 * 1024 * 1024;
+
+    // Size of the area requested when using ASLR to obtain a random load address.
+    // Should match the value of kAddressSpaceReservationSize on the JNI side.
+    // Used when computing the load addresses of multiple loaded libraries to
+    // ensure that we don't try to load outside the area originally requested.
+    private static final int ADDRESS_SPACE_RESERVATION = 192 * 1024 * 1024;
+
+    // Becomes true after linker initialization.
+    private boolean mInitialized;
+
+    // Set to true if this runs in the browser process. Disabled by initServiceProcess().
+    private boolean mInBrowserProcess = true;
+
+    // Becomes true to indicate this process needs to wait for a shared RELRO in
+    // finishLibraryLoad().
+    private boolean mWaitForSharedRelros;
+
+    // Becomes true when initialization determines that the browser process can use the
+    // shared RELRO.
+    private boolean mBrowserUsesSharedRelro;
+
+    // The map of all RELRO sections either created or used in this process.
+    private Bundle mSharedRelros;
+
+    // Current common random base load address. A value of -1 indicates not yet initialized.
+    private long mBaseLoadAddress = -1;
+
+    // Current fixed-location load address for the next library called by loadLibrary().
+    // A value of -1 indicates not yet initialized.
+    private long mCurrentLoadAddress = -1;
+
+    // Becomes true once prepareLibraryLoad() has been called.
+    private boolean mPrepareLibraryLoadCalled;
+
+    // The map of libraries that are currently loaded in this process.
+    private HashMap<String, LibInfo> mLoadedLibraries;
+
+    // Singleton.
+    private static final Linker sSingleton = new Linker();
+
+    // Private singleton constructor.
+    private Linker() {
+        // Ensure this class is not referenced unless it's used.
+        assert LibraryLoader.useCrazyLinker();
+    }
+
+    /**
+     * Get singleton instance. Returns a Linker.
+     *
+     * On N+ Monochrome is selected by Play Store. With Monochrome this code is not used, instead
+     * Chrome asks the WebView to provide the library (and the shared RELRO). If the WebView fails
+     * to provide the library, the system linker is used as a fallback.
+     *
+     * Linker runs on all Android releases, but is incompatible with GVR library on N+.
+     * Linker is preferred on M- because it does not write the shared RELRO to disk at
+     * almost every cold startup.
+     *
+     * @return the Linker implementation instance.
+     */
+    public static Linker getInstance() {
+        return sSingleton;
+    }
+
+    /**
+     * Check that native library linker tests are enabled.
+     * If not enabled, calls to testing functions will fail with an assertion
+     * error.
+     *
+     * @return true if native library linker tests are enabled.
+     */
+    public static boolean areTestsEnabled() {
+        return NativeLibraries.sEnableLinkerTests;
+    }
+
+    /**
+     * Assert NativeLibraries.sEnableLinkerTests is true.
+     * Hard assertion that we are in a testing context. Cannot be disabled. The
+     * test methods in this module permit injection of runnable code by class
+     * name. To protect against both malicious and accidental use of these
+     * methods, we ensure that NativeLibraries.sEnableLinkerTests is true when
+     * any is called.
+     */
+    private static void assertLinkerTestsAreEnabled() {
+        assert NativeLibraries.sEnableLinkerTests : "Testing method called in non-testing context";
+    }
+
+    /**
+     * A public interface used to run runtime linker tests after loading
+     * libraries. Should only be used to implement the linker unit tests,
+     * which is controlled by the value of NativeLibraries.sEnableLinkerTests
+     * configured at build time.
+     */
+    public interface TestRunner {
+        /**
+         * Run runtime checks and return true if they all pass.
+         *
+         * @param memoryDeviceConfig The current memory device configuration.
+         * @param inBrowserProcess true iff this is the browser process.
+         * @return true if all checks pass.
+         */
+        public boolean runChecks(int memoryDeviceConfig, boolean inBrowserProcess);
+    }
+
+    /**
+     * Call this to retrieve the name of the current TestRunner class name
+     * if any. This can be useful to pass it from the browser process to
+     * child ones.
+     *
+     * @return null or a String holding the name of the class implementing
+     * the TestRunner set by calling setTestRunnerClassNameForTesting() previously.
+     */
+    public final String getTestRunnerClassNameForTesting() {
+        // Sanity check. This method may only be called during tests.
+        assertLinkerTestsAreEnabled();
+
+        synchronized (mLock) {
+            return mTestRunnerClassName;
+        }
+    }
+
+    /**
+     * Sets the test class name.
+     *
+     * On the first call, instantiates a Linker and sets its test runner class name. On subsequent
+     * calls, checks that the singleton produced by the first call matches the test runner class
+     * name.
+     */
+    public static final void setupForTesting(String testRunnerClassName) {
+        if (DEBUG) {
+            Log.i(TAG, "setupForTesting(" + testRunnerClassName + ") called");
+        }
+        // Sanity check. This method may only be called during tests.
+        assertLinkerTestsAreEnabled();
+
+        synchronized (sSingleton) {
+            sSingleton.mTestRunnerClassName = testRunnerClassName;
+        }
+    }
+
+    /**
+     * Instantiate and run the current TestRunner, if any. The TestRunner implementation
+     * must be instantiated _after_ all libraries are loaded to ensure that its
+     * native methods are properly registered.
+     *
+     * @param memoryDeviceConfig Linker memory config, or 0 if unused
+     * @param inBrowserProcess true if in the browser process
+     */
+    private final void runTestRunnerClassForTesting(
+            int memoryDeviceConfig, boolean inBrowserProcess) {
+        if (DEBUG) {
+            Log.i(TAG, "runTestRunnerClassForTesting called");
+        }
+        // Sanity check. This method may only be called during tests.
+        assertLinkerTestsAreEnabled();
+
+        synchronized (mLock) {
+            if (mTestRunnerClassName == null) {
+                Log.wtf(TAG, "Linker runtime tests not set up for this process");
+                assert false;
+            }
+            if (DEBUG) {
+                Log.i(TAG, "Instantiating " + mTestRunnerClassName);
+            }
+            TestRunner testRunner = null;
+            try {
+                testRunner = (TestRunner) Class.forName(mTestRunnerClassName)
+                                     .getDeclaredConstructor()
+                                     .newInstance();
+            } catch (Exception e) {
+                Log.wtf(TAG, "Could not instantiate test runner class by name", e);
+                assert false;
+            }
+
+            if (!testRunner.runChecks(memoryDeviceConfig, inBrowserProcess)) {
+                Log.wtf(TAG, "Linker runtime tests failed in this process");
+                assert false;
+            }
+
+            Log.i(TAG, "All linker tests passed");
+        }
+    }
+
+    /**
+     * Call this method before any other Linker method to force a specific
+     * memory device configuration. Should only be used for testing.
+     *
+     * @param memoryDeviceConfig MEMORY_DEVICE_CONFIG_LOW or MEMORY_DEVICE_CONFIG_NORMAL.
+     */
+    public final void setMemoryDeviceConfigForTesting(int memoryDeviceConfig) {
+        if (DEBUG) {
+            Log.i(TAG, "setMemoryDeviceConfigForTesting(" + memoryDeviceConfig + ") called");
+        }
+        // Sanity check. This method may only be called during tests.
+        assertLinkerTestsAreEnabled();
+        assert memoryDeviceConfig == MEMORY_DEVICE_CONFIG_LOW
+                || memoryDeviceConfig == MEMORY_DEVICE_CONFIG_NORMAL;
+
+        synchronized (mLock) {
+            assert mMemoryDeviceConfig == MEMORY_DEVICE_CONFIG_INIT;
+
+            mMemoryDeviceConfig = memoryDeviceConfig;
+            if (DEBUG) {
+                if (mMemoryDeviceConfig == MEMORY_DEVICE_CONFIG_LOW) {
+                    Log.i(TAG, "Simulating a low-memory device");
+                } else {
+                    Log.i(TAG, "Simulating a regular-memory device");
+                }
+            }
+        }
+    }
+
+    /**
+     * Determine whether a library is the linker library.
+     *
+     * @param library the name of the library.
+     * @return true is the library is the Linker's own JNI library.
+     */
+    boolean isChromiumLinkerLibrary(String library) {
+        return library.equals(LINKER_JNI_LIBRARY);
+    }
+
+    /**
+     * Load the Linker JNI library. Throws UnsatisfiedLinkError on error.
+     */
+    @SuppressLint({"UnsafeDynamicallyLoadedCode"})
+    private static void loadLinkerJniLibrary() {
+        LibraryLoader.setEnvForNative();
+        if (DEBUG) {
+            String libName = "lib" + LINKER_JNI_LIBRARY + ".so";
+            Log.i(TAG, "Loading " + libName);
+        }
+        try {
+            System.loadLibrary(LINKER_JNI_LIBRARY);
+            LibraryLoader.incrementRelinkerCountNotHitHistogram();
+        } catch (UnsatisfiedLinkError e) {
+            if (LibraryLoader.PLATFORM_REQUIRES_NATIVE_FALLBACK_EXTRACTION) {
+                System.load(LibraryLoader.getExtractedLibraryPath(
+                        ContextUtils.getApplicationContext(), LINKER_JNI_LIBRARY));
+                LibraryLoader.incrementRelinkerCountHitHistogram();
+            }
+        }
+    }
+
+    /**
+     * Obtain a random base load address at which to place loaded libraries.
+     *
+     * @return new base load address
+     */
+    private long getRandomBaseLoadAddress() {
+        // nativeGetRandomBaseLoadAddress() returns an address at which it has previously
+        // successfully mapped an area larger than the largest library we expect to load,
+        // on the basis that we will be able, with high probability, to map our library
+        // into it.
+        //
+        // One issue with this is that we do not yet know the size of the library that
+        // we will load is. If it is smaller than the size we used to obtain a random
+        // address the library mapping may still succeed. The other issue is that
+        // although highly unlikely, there is no guarantee that something else does not
+        // map into the area we are going to use between here and when we try to map into it.
+        //
+        // The above notes mean that all of this is probablistic. It is however okay to do
+        // because if, worst case and unlikely, we get unlucky in our choice of address,
+        // the back-out and retry without the shared RELRO in the ChildProcessService will
+        // keep things running.
+        final long address = nativeGetRandomBaseLoadAddress();
+        if (DEBUG) {
+            Log.i(TAG, String.format(Locale.US, "Random native base load address: 0x%x", address));
+        }
+        return address;
+    }
+
+    /**
+     * Load a native shared library with the Chromium linker. Note the crazy linker treats
+     * libraries and files as equivalent, so you can only open one library in a given zip
+     * file. The library must not be the Chromium linker library.
+     *
+     * @param libFilePath The path of the library (possibly in the zip file).
+     */
+    void loadLibrary(String libFilePath) {
+        if (DEBUG) {
+            Log.i(TAG, "loadLibrary: " + libFilePath);
+        }
+        final boolean isFixedAddressPermitted = true;
+        loadLibraryImpl(libFilePath, isFixedAddressPermitted);
+    }
+
+    /**
+     * Load a native shared library with the Chromium linker, ignoring any
+     * requested fixed address for RELRO sharing. Note the crazy linker treats libraries and
+     * files as equivalent, so you can only open one library in a given zip file. The
+     * library must not be the Chromium linker library.
+     *
+     * @param libFilePath The path of the library (possibly in the zip file).
+     */
+    void loadLibraryNoFixedAddress(String libFilePath) {
+        if (DEBUG) {
+            Log.i(TAG, "loadLibraryAtAnyAddress: " + libFilePath);
+        }
+        final boolean isFixedAddressPermitted = false;
+        loadLibraryImpl(libFilePath, isFixedAddressPermitted);
+    }
+
+    // Used internally to initialize the linker's data. Assumes lock is held.
+    // Loads JNI, and sets mMemoryDeviceConfig and mBrowserUsesSharedRelro.
+    private void ensureInitializedLocked() {
+        assert Thread.holdsLock(mLock);
+
+        if (mInitialized) {
+            return;
+        }
+
+        // On first call, load libchromium_android_linker.so. Cannot be done in the
+        // constructor because instantiation occurs on the UI thread.
+        loadLinkerJniLibrary();
+
+        if (mMemoryDeviceConfig == MEMORY_DEVICE_CONFIG_INIT) {
+            if (SysUtils.isLowEndDevice()) {
+                mMemoryDeviceConfig = MEMORY_DEVICE_CONFIG_LOW;
+            } else {
+                mMemoryDeviceConfig = MEMORY_DEVICE_CONFIG_NORMAL;
+            }
+        }
+
+        // Cannot run in the constructor because SysUtils.isLowEndDevice() relies
+        // on CommandLine, which may not be available at instantiation.
+        switch (BROWSER_SHARED_RELRO_CONFIG) {
+            case BROWSER_SHARED_RELRO_CONFIG_NEVER:
+                mBrowserUsesSharedRelro = false;
+                break;
+            case BROWSER_SHARED_RELRO_CONFIG_LOW_RAM_ONLY:
+                if (mMemoryDeviceConfig == MEMORY_DEVICE_CONFIG_LOW) {
+                    mBrowserUsesSharedRelro = true;
+                    Log.w(TAG, "Low-memory device: shared RELROs used in all processes");
+                } else {
+                    mBrowserUsesSharedRelro = false;
+                }
+                break;
+            case BROWSER_SHARED_RELRO_CONFIG_ALWAYS:
+                Log.w(TAG, "Beware: shared RELROs used in all processes!");
+                mBrowserUsesSharedRelro = true;
+                break;
+            default:
+                Log.wtf(TAG, "FATAL: illegal shared RELRO config");
+                throw new AssertionError();
+        }
+
+        mInitialized = true;
+    }
+
+    /**
+     * Call this method to determine if the linker will try to use shared RELROs
+     * for the browser process.
+     */
+    public boolean isUsingBrowserSharedRelros() {
+        synchronized (mLock) {
+            ensureInitializedLocked();
+            return mInBrowserProcess && mBrowserUsesSharedRelro;
+        }
+    }
+
+    /**
+     * Call this method just before loading any native shared libraries in this process.
+     *
+     * @param apkFilePath Optional current APK file path. If provided, the linker
+     * will try to load libraries directly from it.
+     */
+    public void prepareLibraryLoad(@Nullable String apkFilePath) {
+        if (DEBUG) {
+            Log.i(TAG, "prepareLibraryLoad() called");
+        }
+        synchronized (mLock) {
+            ensureInitializedLocked();
+            if (apkFilePath != null) {
+                nativeAddZipArchivePath(apkFilePath);
+            }
+            mPrepareLibraryLoadCalled = true;
+
+            if (mInBrowserProcess) {
+                // Force generation of random base load address, as well
+                // as creation of shared RELRO sections in this process.
+                setupBaseLoadAddressLocked();
+            }
+        }
+    }
+
+    /**
+     * Call this method just after loading all native shared libraries in this process.
+     * Note that when in a service process, this will block until the RELRO bundle is
+     * received, i.e. when another thread calls useSharedRelros().
+     */
+    void finishLibraryLoad() {
+        if (DEBUG) {
+            Log.i(TAG, "finishLibraryLoad() called");
+        }
+        synchronized (mLock) {
+            ensureInitializedLocked();
+            if (DEBUG) {
+                Log.i(TAG,
+                        String.format(Locale.US,
+                                "mInBrowserProcess=%b mBrowserUsesSharedRelro=%b mWaitForSharedRelros=%b",
+                                mInBrowserProcess, mBrowserUsesSharedRelro, mWaitForSharedRelros));
+            }
+
+            if (mLoadedLibraries == null) {
+                if (DEBUG) {
+                    Log.i(TAG, "No libraries loaded");
+                }
+            } else {
+                if (mInBrowserProcess) {
+                    // Create new Bundle containing RELRO section information
+                    // for all loaded libraries. Make it available to getSharedRelros().
+                    mSharedRelros = createBundleFromLibInfoMap(mLoadedLibraries);
+                    if (DEBUG) {
+                        Log.i(TAG, "Shared RELRO created");
+                        dumpBundle(mSharedRelros);
+                    }
+
+                    if (mBrowserUsesSharedRelro) {
+                        useSharedRelrosLocked(mSharedRelros);
+                    }
+                }
+
+                if (mWaitForSharedRelros) {
+                    assert !mInBrowserProcess;
+
+                    // Wait until the shared relro bundle is received from useSharedRelros().
+                    while (mSharedRelros == null) {
+                        try {
+                            mLock.wait();
+                        } catch (InterruptedException ie) {
+                            // Restore the thread's interrupt status.
+                            Thread.currentThread().interrupt();
+                        }
+                    }
+                    useSharedRelrosLocked(mSharedRelros);
+                    // Clear the Bundle to ensure its file descriptor references can't be reused.
+                    mSharedRelros.clear();
+                    mSharedRelros = null;
+                }
+            }
+
+            // If testing, run tests now that all libraries are loaded and initialized.
+            if (NativeLibraries.sEnableLinkerTests) {
+                runTestRunnerClassForTesting(mMemoryDeviceConfig, mInBrowserProcess);
+            }
+        }
+        if (DEBUG) {
+            Log.i(TAG, "finishLibraryLoad() exiting");
+        }
+    }
+
+    /**
+     * Call this to send a Bundle containing the shared RELRO sections to be
+     * used in this process. If initServiceProcess() was previously called,
+     * finishLibraryLoad() will not exit until this method is called in another
+     * thread with a non-null value.
+     *
+     * @param bundle The Bundle instance containing a map of shared RELRO sections
+     * to use in this process.
+     */
+    public void useSharedRelros(Bundle bundle) {
+        // Ensure the bundle uses the application's class loader, not the framework
+        // one which doesn't know anything about LibInfo.
+        // Also, hold a fresh copy of it so the caller can't recycle it.
+        Bundle clonedBundle = null;
+        if (bundle != null) {
+            bundle.setClassLoader(LibInfo.class.getClassLoader());
+            clonedBundle = new Bundle(LibInfo.class.getClassLoader());
+            Parcel parcel = Parcel.obtain();
+            bundle.writeToParcel(parcel, 0);
+            parcel.setDataPosition(0);
+            clonedBundle.readFromParcel(parcel);
+            parcel.recycle();
+        }
+        if (DEBUG) {
+            Log.i(TAG, "useSharedRelros() called with " + bundle + ", cloned " + clonedBundle);
+        }
+        synchronized (mLock) {
+            // Note that in certain cases, this can be called before
+            // initServiceProcess() in service processes.
+            mSharedRelros = clonedBundle;
+            // Tell any listener blocked in finishLibraryLoad() about it.
+            mLock.notifyAll();
+        }
+    }
+
+    /**
+     * Call this to retrieve the shared RELRO sections created in this process,
+     * after loading all libraries.
+     *
+     * @return a new Bundle instance, or null if RELRO sharing is disabled on
+     * this system, or if initServiceProcess() was called previously.
+     */
+    public Bundle getSharedRelros() {
+        if (DEBUG) {
+            Log.i(TAG, "getSharedRelros() called");
+        }
+        synchronized (mLock) {
+            if (!mInBrowserProcess) {
+                if (DEBUG) {
+                    Log.i(TAG, "... returning null Bundle");
+                }
+                return null;
+            }
+
+            // Return the Bundle created in finishLibraryLoad().
+            if (DEBUG) {
+                Log.i(TAG, "... returning " + mSharedRelros);
+            }
+            return mSharedRelros;
+        }
+    }
+
+    /**
+     * Call this method before loading any libraries to indicate that this
+     * process shall neither create or reuse shared RELRO sections.
+     */
+    public void disableSharedRelros() {
+        if (DEBUG) {
+            Log.i(TAG, "disableSharedRelros() called");
+        }
+        synchronized (mLock) {
+            ensureInitializedLocked();
+            mInBrowserProcess = false;
+            mWaitForSharedRelros = false;
+            mBrowserUsesSharedRelro = false;
+        }
+    }
+
+    /**
+     * Call this method before loading any libraries to indicate that this
+     * process is ready to reuse shared RELRO sections from another one.
+     * Typically used when starting service processes.
+     *
+     * @param baseLoadAddress the base library load address to use.
+     */
+    public void initServiceProcess(long baseLoadAddress) {
+        if (DEBUG) {
+            Log.i(TAG,
+                    String.format(Locale.US, "initServiceProcess(0x%x) called", baseLoadAddress));
+        }
+        synchronized (mLock) {
+            ensureInitializedLocked();
+            mInBrowserProcess = false;
+            mBrowserUsesSharedRelro = false;
+            mWaitForSharedRelros = true;
+            mBaseLoadAddress = baseLoadAddress;
+            mCurrentLoadAddress = baseLoadAddress;
+        }
+    }
+
+    /**
+     * Retrieve the base load address of all shared RELRO sections.
+     * This also enforces the creation of shared RELRO sections in
+     * prepareLibraryLoad(), which can later be retrieved with getSharedRelros().
+     *
+     * @return a common, random base load address, or 0 if RELRO sharing is
+     * disabled.
+     */
+    public long getBaseLoadAddress() {
+        synchronized (mLock) {
+            ensureInitializedLocked();
+            if (!mInBrowserProcess) {
+                Log.w(TAG, "Shared RELRO sections are disabled in this process!");
+                return 0;
+            }
+
+            setupBaseLoadAddressLocked();
+            if (DEBUG) {
+                Log.i(TAG,
+                        String.format(
+                                Locale.US, "getBaseLoadAddress() returns 0x%x", mBaseLoadAddress));
+            }
+            return mBaseLoadAddress;
+        }
+    }
+
+    // Used internally to lazily setup the common random base load address.
+    private void setupBaseLoadAddressLocked() {
+        assert Thread.holdsLock(mLock);
+        if (mBaseLoadAddress == -1) {
+            mBaseLoadAddress = getRandomBaseLoadAddress();
+            mCurrentLoadAddress = mBaseLoadAddress;
+            if (mBaseLoadAddress == 0) {
+                // If the random address is 0 there are issues with finding enough
+                // free address space, so disable RELRO shared / fixed load addresses.
+                Log.w(TAG, "Disabling shared RELROs due address space pressure");
+                mBrowserUsesSharedRelro = false;
+                mWaitForSharedRelros = false;
+            }
+        }
+    }
+
+    // Used for debugging only.
+    private void dumpBundle(Bundle bundle) {
+        if (DEBUG) {
+            Log.i(TAG, "Bundle has " + bundle.size() + " items: " + bundle);
+        }
+    }
+
+    /**
+     * Use the shared RELRO section from a Bundle received form another process.
+     * Call this after calling setBaseLoadAddress() then loading all libraries
+     * with loadLibrary().
+     *
+     * @param bundle Bundle instance generated with createSharedRelroBundle() in
+     * another process.
+     */
+    private void useSharedRelrosLocked(Bundle bundle) {
+        assert Thread.holdsLock(mLock);
+
+        if (DEBUG) {
+            Log.i(TAG, "Linker.useSharedRelrosLocked() called");
+        }
+
+        if (bundle == null) {
+            if (DEBUG) {
+                Log.i(TAG, "null bundle!");
+            }
+            return;
+        }
+
+        if (mLoadedLibraries == null) {
+            if (DEBUG) {
+                Log.i(TAG, "No libraries loaded!");
+            }
+            return;
+        }
+
+        if (DEBUG) {
+            dumpBundle(bundle);
+        }
+        HashMap<String, LibInfo> relroMap = createLibInfoMapFromBundle(bundle);
+
+        // Apply the RELRO section to all libraries that were already loaded.
+        for (Map.Entry<String, LibInfo> entry : relroMap.entrySet()) {
+            String libName = entry.getKey();
+            LibInfo libInfo = entry.getValue();
+            if (!nativeUseSharedRelro(libName, libInfo)) {
+                Log.w(TAG, "Could not use shared RELRO section for " + libName);
+            } else {
+                if (DEBUG) {
+                    Log.i(TAG, "Using shared RELRO section for " + libName);
+                }
+            }
+        }
+
+        // In service processes, close all file descriptors from the map now.
+        if (!mInBrowserProcess) {
+            closeLibInfoMap(relroMap);
+        }
+
+        if (DEBUG) {
+            Log.i(TAG, "Linker.useSharedRelrosLocked() exiting");
+        }
+    }
+
+    /**
+     * Implements loading a native shared library with the Chromium linker.
+     *
+     * Load a native shared library with the Chromium linker. If the zip file
+     * is not null, the shared library must be uncompressed and page aligned
+     * inside the zipfile. Note the crazy linker treats libraries and files as
+     * equivalent, so you can only open one library in a given zip file. The
+     * library must not be the Chromium linker library.
+     *
+     * @param libFilePath The path of the library (possibly in the zip file).
+     * @param isFixedAddressPermitted If true, uses a fixed load address if one was
+     * supplied, otherwise ignores the fixed address and loads wherever available.
+     */
+    void loadLibraryImpl(String libFilePath, boolean isFixedAddressPermitted) {
+        if (DEBUG) {
+            Log.i(TAG, "loadLibraryImpl: " + libFilePath + ", " + isFixedAddressPermitted);
+        }
+        synchronized (mLock) {
+            ensureInitializedLocked();
+
+            // Security: Ensure prepareLibraryLoad() was called before.
+            // In theory, this can be done lazily here, but it's more consistent
+            // to use a pair of functions (i.e. prepareLibraryLoad() + finishLibraryLoad())
+            // that wrap all calls to loadLibrary() in the library loader.
+            assert mPrepareLibraryLoadCalled;
+
+            if (mLoadedLibraries == null) {
+                mLoadedLibraries = new HashMap<String, LibInfo>();
+            }
+
+            if (mLoadedLibraries.containsKey(libFilePath)) {
+                if (DEBUG) {
+                    Log.i(TAG, "Not loading " + libFilePath + " twice");
+                }
+                return;
+            }
+
+            LibInfo libInfo = new LibInfo();
+            long loadAddress = 0;
+            if (isFixedAddressPermitted) {
+                if ((mInBrowserProcess && mBrowserUsesSharedRelro) || mWaitForSharedRelros) {
+                    // Load the library at a fixed address.
+                    loadAddress = mCurrentLoadAddress;
+
+                    // For multiple libraries, ensure we stay within reservation range.
+                    if (loadAddress > mBaseLoadAddress + ADDRESS_SPACE_RESERVATION) {
+                        String errorMessage =
+                                "Load address outside reservation, for: " + libFilePath;
+                        Log.e(TAG, errorMessage);
+                        throw new UnsatisfiedLinkError(errorMessage);
+                    }
+                }
+            }
+
+            final String sharedRelRoName = libFilePath;
+            if (!nativeLoadLibrary(libFilePath, loadAddress, libInfo)) {
+                String errorMessage = "Unable to load library: " + libFilePath;
+                Log.e(TAG, errorMessage);
+                throw new UnsatisfiedLinkError(errorMessage);
+            }
+
+            // Print the load address to the logcat when testing the linker. The format
+            // of the string is expected by the Python test_runner script as one of:
+            //    BROWSER_LIBRARY_ADDRESS: <library-name> <address>
+            //    RENDERER_LIBRARY_ADDRESS: <library-name> <address>
+            // Where <library-name> is the library name, and <address> is the hexadecimal load
+            // address.
+            if (NativeLibraries.sEnableLinkerTests) {
+                String tag =
+                        mInBrowserProcess ? "BROWSER_LIBRARY_ADDRESS" : "RENDERER_LIBRARY_ADDRESS";
+                Log.i(TAG,
+                        String.format(
+                                Locale.US, "%s: %s %x", tag, libFilePath, libInfo.mLoadAddress));
+            }
+
+            if (mInBrowserProcess) {
+                // Create a new shared RELRO section at the 'current' fixed load address.
+                if (!nativeCreateSharedRelro(sharedRelRoName, mCurrentLoadAddress, libInfo)) {
+                    Log.w(TAG,
+                            String.format(Locale.US, "Could not create shared RELRO for %s at %x",
+                                    libFilePath, mCurrentLoadAddress));
+                } else {
+                    if (DEBUG) {
+                        Log.i(TAG,
+                                String.format(Locale.US, "Created shared RELRO for %s at %x: %s",
+                                        sharedRelRoName, mCurrentLoadAddress, libInfo.toString()));
+                    }
+                }
+            }
+
+            if (loadAddress != 0 && mCurrentLoadAddress != 0) {
+                // Compute the next current load address. If mCurrentLoadAddress
+                // is not 0, this is an explicit library load address. Otherwise,
+                // this is an explicit load address for relocated RELRO sections
+                // only.
+                mCurrentLoadAddress =
+                        libInfo.mLoadAddress + libInfo.mLoadSize + BREAKPAD_GUARD_REGION_BYTES;
+            }
+
+            mLoadedLibraries.put(sharedRelRoName, libInfo);
+            if (DEBUG) {
+                Log.i(TAG, "Library details " + libInfo.toString());
+            }
+        }
+    }
+
+    /**
+     * Record information for a given library.
+     * IMPORTANT: Native code knows about this class's fields, so
+     * don't change them without modifying the corresponding C++ sources.
+     * Also, the LibInfo instance owns the shared RELRO file descriptor.
+     */
+    private static class LibInfo implements Parcelable {
+        LibInfo() {}
+
+        // from Parcelable
+        LibInfo(Parcel in) {
+            mLoadAddress = in.readLong();
+            mLoadSize = in.readLong();
+            mRelroStart = in.readLong();
+            mRelroSize = in.readLong();
+            ParcelFileDescriptor fd = ParcelFileDescriptor.CREATOR.createFromParcel(in);
+            // If CreateSharedRelro fails, the OS file descriptor will be -1 and |fd| will be null.
+            if (fd != null) {
+                mRelroFd = fd.detachFd();
+            }
+        }
+
+        public void close() {
+            if (mRelroFd >= 0) {
+                StreamUtil.closeQuietly(ParcelFileDescriptor.adoptFd(mRelroFd));
+                mRelroFd = -1;
+            }
+        }
+
+        // from Parcelable
+        @Override
+        public void writeToParcel(Parcel out, int flags) {
+            if (mRelroFd >= 0) {
+                out.writeLong(mLoadAddress);
+                out.writeLong(mLoadSize);
+                out.writeLong(mRelroStart);
+                out.writeLong(mRelroSize);
+                try {
+                    ParcelFileDescriptor fd = ParcelFileDescriptor.fromFd(mRelroFd);
+                    fd.writeToParcel(out, 0);
+                    fd.close();
+                } catch (java.io.IOException e) {
+                    Log.e(TAG, "Can't write LibInfo file descriptor to parcel", e);
+                }
+            }
+        }
+
+        // from Parcelable
+        @Override
+        public int describeContents() {
+            return Parcelable.CONTENTS_FILE_DESCRIPTOR;
+        }
+
+        // from Parcelable
+        public static final Parcelable.Creator<LibInfo> CREATOR =
+                new Parcelable.Creator<LibInfo>() {
+                    @Override
+                    public LibInfo createFromParcel(Parcel in) {
+                        return new LibInfo(in);
+                    }
+
+                    @Override
+                    public LibInfo[] newArray(int size) {
+                        return new LibInfo[size];
+                    }
+                };
+
+        // IMPORTANT: Don't change these fields without modifying the
+        // native code that accesses them directly!
+        @AccessedByNative
+        public long mLoadAddress; // page-aligned library load address.
+        @AccessedByNative
+        public long mLoadSize;    // page-aligned library load size.
+        @AccessedByNative
+        public long mRelroStart;  // page-aligned address in memory, or 0 if none.
+        @AccessedByNative
+        public long mRelroSize;   // page-aligned size in memory, or 0.
+        @AccessedByNative
+        public int mRelroFd = -1; // shared RELRO file descriptor, or -1
+    }
+
+    // Create a Bundle from a map of LibInfo objects.
+    private Bundle createBundleFromLibInfoMap(HashMap<String, LibInfo> map) {
+        Bundle bundle = new Bundle(map.size());
+        for (Map.Entry<String, LibInfo> entry : map.entrySet()) {
+            bundle.putParcelable(entry.getKey(), entry.getValue());
+        }
+        return bundle;
+    }
+
+    // Create a new LibInfo map from a Bundle.
+    private HashMap<String, LibInfo> createLibInfoMapFromBundle(Bundle bundle) {
+        HashMap<String, LibInfo> map = new HashMap<String, LibInfo>();
+        for (String library : bundle.keySet()) {
+            LibInfo libInfo = bundle.getParcelable(library);
+            map.put(library, libInfo);
+        }
+        return map;
+    }
+
+    // Call the close() method on all values of a LibInfo map.
+    private void closeLibInfoMap(HashMap<String, LibInfo> map) {
+        for (Map.Entry<String, LibInfo> entry : map.entrySet()) {
+            entry.getValue().close();
+        }
+    }
+
+    /**
+     * Move activity from the native thread to the main UI thread.
+     * Called from native code on its own thread. Posts a callback from
+     * the UI thread back to native code.
+     *
+     * @param opaque Opaque argument.
+     */
+    @CalledByNative
+    @MainDex
+    private static void postCallbackOnMainThread(final long opaque) {
+        ThreadUtils.postOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                nativeRunCallbackOnUiThread(opaque);
+            }
+        });
+    }
+
+    /**
+     * Native method to run callbacks on the main UI thread.
+     * Supplied by the crazy linker and called by postCallbackOnMainThread.
+     *
+     * @param opaque Opaque crazy linker arguments.
+     */
+    private static native void nativeRunCallbackOnUiThread(long opaque);
+
+    /**
+     * Native method used to load a library.
+     *
+     * @param library Platform specific library name (e.g. libfoo.so)
+     * @param loadAddress Explicit load address, or 0 for randomized one.
+     * @param libInfo If not null, the mLoadAddress and mLoadSize fields
+     * of this LibInfo instance will set on success.
+     * @return true for success, false otherwise.
+     */
+    private static native boolean nativeLoadLibrary(
+            String library, long loadAddress, LibInfo libInfo);
+
+    /**
+     * Native method used to add a zip archive or APK to the search path
+     * for native libraries. Allows loading directly from it.
+     *
+     * @param zipfilePath Path of the zip file containing the libraries.
+     * @return true for success, false otherwise.
+     */
+    private static native boolean nativeAddZipArchivePath(String zipFilePath);
+
+    /**
+     * Native method used to create a shared RELRO section.
+     * If the library was already loaded at the same address using
+     * nativeLoadLibrary(), this creates the RELRO for it. Otherwise,
+     * this loads a new temporary library at the specified address,
+     * creates and extracts the RELRO section from it, then unloads it.
+     *
+     * @param library Library name.
+     * @param loadAddress load address, which can be different from the one
+     * used to load the library in the current process!
+     * @param libInfo libInfo instance. On success, the mRelroStart, mRelroSize
+     * and mRelroFd will be set.
+     * @return true on success, false otherwise.
+     */
+    private static native boolean nativeCreateSharedRelro(
+            String library, long loadAddress, LibInfo libInfo);
+
+    /**
+     * Native method used to use a shared RELRO section.
+     *
+     * @param library Library name.
+     * @param libInfo A LibInfo instance containing valid RELRO information
+     * @return true on success.
+     */
+    private static native boolean nativeUseSharedRelro(String library, LibInfo libInfo);
+
+    /**
+     * Return a random address that should be free to be mapped with the given size.
+     * Maps an area large enough for the largest library we might attempt to load,
+     * and if successful then unmaps it and returns the address of the area allocated
+     * by the system (with ASLR). The idea is that this area should remain free of
+     * other mappings until we map our library into it.
+     *
+     * @return address to pass to future mmap, or 0 on error.
+     */
+    private static native long nativeGetRandomBaseLoadAddress();
+}
diff --git a/base/android/java/src/org/chromium/base/library_loader/LoaderErrors.java b/base/android/java/src/org/chromium/base/library_loader/LoaderErrors.java
new file mode 100644
index 0000000..2b94370
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/library_loader/LoaderErrors.java
@@ -0,0 +1,16 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base.library_loader;
+
+/**
+ * These are the possible failures from the LibraryLoader
+ */
+public class LoaderErrors {
+    public static final int LOADER_ERROR_NORMAL_COMPLETION = 0;
+    public static final int LOADER_ERROR_FAILED_TO_REGISTER_JNI = 1;
+    public static final int LOADER_ERROR_NATIVE_LIBRARY_LOAD_FAILED = 2;
+    public static final int LOADER_ERROR_NATIVE_LIBRARY_WRONG_VERSION = 3;
+    public static final int LOADER_ERROR_NATIVE_STARTUP_FAILED = 4;
+}
diff --git a/base/android/java/src/org/chromium/base/library_loader/NativeLibraryPreloader.java b/base/android/java/src/org/chromium/base/library_loader/NativeLibraryPreloader.java
new file mode 100644
index 0000000..6f8008d
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/library_loader/NativeLibraryPreloader.java
@@ -0,0 +1,20 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base.library_loader;
+
+import android.content.Context;
+
+/**
+ * This is interface to preload the native library before calling System.loadLibrary.
+ *
+ * Preloading shouldn't call System.loadLibrary() or otherwise cause any Chromium
+ * code to be run, because it can be called before Chromium command line is known.
+ * It can however open the library via dlopen() or android_dlopen_ext() so that
+ * dlopen() later called by System.loadLibrary() becomes a noop. This is what the
+ * only subclass (MonochromeLibraryPreloader) is doing.
+ */
+public abstract class NativeLibraryPreloader {
+    public abstract int loadLibrary(Context context);
+}
diff --git a/base/android/java/src/org/chromium/base/library_loader/ProcessInitException.java b/base/android/java/src/org/chromium/base/library_loader/ProcessInitException.java
new file mode 100644
index 0000000..1066675
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/library_loader/ProcessInitException.java
@@ -0,0 +1,35 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base.library_loader;
+
+/**
+ * The exception that is thrown when the intialization of a process was failed.
+ */
+public class ProcessInitException extends Exception {
+    private int mErrorCode = LoaderErrors.LOADER_ERROR_NORMAL_COMPLETION;
+
+    /**
+     * @param errorCode This will be one of the LoaderErrors error codes.
+     */
+    public ProcessInitException(int errorCode) {
+        mErrorCode = errorCode;
+    }
+
+    /**
+     * @param errorCode This will be one of the LoaderErrors error codes.
+     * @param throwable The wrapped throwable obj.
+     */
+    public ProcessInitException(int errorCode, Throwable throwable) {
+        super(null, throwable);
+        mErrorCode = errorCode;
+    }
+
+    /**
+     * Return the error code.
+     */
+    public int getErrorCode() {
+        return mErrorCode;
+    }
+}
diff --git a/base/android/java/src/org/chromium/base/memory/MemoryPressureCallback.java b/base/android/java/src/org/chromium/base/memory/MemoryPressureCallback.java
new file mode 100644
index 0000000..258aa0b
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/memory/MemoryPressureCallback.java
@@ -0,0 +1,15 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base.memory;
+
+import org.chromium.base.MemoryPressureLevel;
+
+/**
+ * Memory pressure callback interface.
+ */
+@FunctionalInterface
+public interface MemoryPressureCallback {
+    public void onPressure(@MemoryPressureLevel int pressure);
+}
diff --git a/base/android/java/src/org/chromium/base/memory/MemoryPressureMonitor.java b/base/android/java/src/org/chromium/base/memory/MemoryPressureMonitor.java
new file mode 100644
index 0000000..c8af484
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/memory/MemoryPressureMonitor.java
@@ -0,0 +1,301 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base.memory;
+
+import android.app.ActivityManager;
+import android.content.ComponentCallbacks2;
+import android.content.res.Configuration;
+import android.os.Build;
+import android.os.SystemClock;
+
+import org.chromium.base.ContextUtils;
+import org.chromium.base.MemoryPressureLevel;
+import org.chromium.base.MemoryPressureListener;
+import org.chromium.base.Supplier;
+import org.chromium.base.ThreadUtils;
+import org.chromium.base.VisibleForTesting;
+import org.chromium.base.annotations.MainDex;
+import org.chromium.base.metrics.CachedMetrics;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * This class monitors memory pressure and reports it to the native side.
+ * Even though there can be other callbacks besides MemoryPressureListener (which reports
+ * pressure to the native side, and is added implicitly), the class is designed to suite
+ * needs of native MemoryPressureListeners.
+ *
+ * There are two groups of MemoryPressureListeners:
+ *
+ * 1. Stateless, i.e. ones that simply free memory (caches, etc.) in response to memory
+ *    pressure. These listeners need to be called periodically (to have effect), but not
+ *    too frequently (to avoid regressing performance too much).
+ *
+ * 2. Stateful, i.e. ones that change their behavior based on the last received memory
+ *    pressure (in addition to freeing memory). These listeners need to know when the
+ *    pressure subsides, i.e. they need to be notified about CRITICAL->MODERATE changes.
+ *
+ * Android notifies about memory pressure through onTrimMemory() / onLowMemory() callbacks
+ * from ComponentCallbacks2, but these are unreliable (e.g. called too early, called just
+ * once, not called when memory pressure subsides, etc., see https://crbug.com/813909 for
+ * more examples).
+ *
+ * There is also ActivityManager.getMyMemoryState() API which returns current pressure for
+ * the calling process. It has its caveats, for example it can't be called from isolated
+ * processes (renderers). Plus we don't want to poll getMyMemoryState() unnecessarily, for
+ * example there is no reason to poll it when Chrome is in the background.
+ *
+ * This class implements the following principles:
+ *
+ * 1. Throttle pressure signals sent to callbacks.
+ *    Callbacks are called at most once during throttling interval. If same pressure is
+ *    reported several times during the interval, all reports except the first one are
+ *    ignored.
+ *
+ * 2. Always report changes in pressure.
+ *    If pressure changes during the interval, the change is not ignored, but delayed
+ *    until the end of the interval.
+ *
+ * 3. Poll on CRITICAL memory pressure.
+ *    Once CRITICAL pressure is reported, getMyMemoryState API is used to periodically
+ *    query pressure until it subsides (becomes non-CRITICAL).
+ *
+ * Zooming out, the class is used as follows:
+ *
+ * 1. Only the browser process / WebView process poll, and it only polls when it makes
+ *    sense to do so (when Chrome is in the foreground / there are WebView instances
+ *    around).
+ *
+ * 2. Services (GPU, renderers) don't poll, instead they get additional pressure signals
+ *    from the main process.
+ *
+ * NOTE: This class should only be used on UiThread as defined by ThreadUtils (which is
+ *       Android main thread for Chrome, but can be some other thread for WebView).
+ */
+@MainDex
+public class MemoryPressureMonitor {
+    private static final int DEFAULT_THROTTLING_INTERVAL_MS = 60 * 1000;
+
+    private final int mThrottlingIntervalMs;
+
+    // Pressure reported to callbacks in the current throttling interval.
+    private @MemoryPressureLevel int mLastReportedPressure = MemoryPressureLevel.NONE;
+
+    // Pressure received (but not reported) during the current throttling interval,
+    // or null if no pressure was received.
+    private @MemoryPressureLevel Integer mThrottledPressure;
+
+    // Whether we need to throttle pressure signals.
+    private boolean mIsInsideThrottlingInterval;
+
+    private boolean mPollingEnabled;
+
+    // Changed by tests.
+    private Supplier<Integer> mCurrentPressureSupplier =
+            MemoryPressureMonitor::getCurrentMemoryPressure;
+
+    // Changed by tests.
+    private MemoryPressureCallback mReportingCallback =
+            MemoryPressureListener::notifyMemoryPressure;
+
+    private final Runnable mThrottlingIntervalTask = this ::onThrottlingIntervalFinished;
+
+    // ActivityManager.getMyMemoryState() time histograms, recorded by getCurrentMemoryPressure().
+    // Using Count1MHistogramSample because TimesHistogramSample doesn't support microsecond
+    // precision.
+    private static final CachedMetrics.Count1MHistogramSample sGetMyMemoryStateSucceededTime =
+            new CachedMetrics.Count1MHistogramSample(
+                    "Android.MemoryPressureMonitor.GetMyMemoryState.Succeeded.Time");
+    private static final CachedMetrics.Count1MHistogramSample sGetMyMemoryStateFailedTime =
+            new CachedMetrics.Count1MHistogramSample(
+                    "Android.MemoryPressureMonitor.GetMyMemoryState.Failed.Time");
+
+    // The only instance.
+    public static final MemoryPressureMonitor INSTANCE =
+            new MemoryPressureMonitor(DEFAULT_THROTTLING_INTERVAL_MS);
+
+    @VisibleForTesting
+    protected MemoryPressureMonitor(int throttlingIntervalMs) {
+        mThrottlingIntervalMs = throttlingIntervalMs;
+    }
+
+    /**
+     * Starts listening to ComponentCallbacks2.
+     */
+    public void registerComponentCallbacks() {
+        ThreadUtils.assertOnUiThread();
+
+        ContextUtils.getApplicationContext().registerComponentCallbacks(new ComponentCallbacks2() {
+            @Override
+            public void onTrimMemory(int level) {
+                Integer pressure = memoryPressureFromTrimLevel(level);
+                if (pressure != null) {
+                    notifyPressure(pressure);
+                }
+            }
+
+            @Override
+            public void onLowMemory() {
+                notifyPressure(MemoryPressureLevel.CRITICAL);
+            }
+
+            @Override
+            public void onConfigurationChanged(Configuration configuration) {}
+        });
+    }
+
+    /**
+     * Enables memory pressure polling.
+     * See class comment for specifics. This method also does a single pressure check to get
+     * the current pressure.
+     */
+    public void enablePolling() {
+        ThreadUtils.assertOnUiThread();
+        if (mPollingEnabled) return;
+
+        mPollingEnabled = true;
+        if (!mIsInsideThrottlingInterval) {
+            reportCurrentPressure();
+        }
+    }
+
+    /**
+     * Disables memory pressure polling.
+     */
+    public void disablePolling() {
+        ThreadUtils.assertOnUiThread();
+        if (!mPollingEnabled) return;
+
+        mPollingEnabled = false;
+    }
+
+    /**
+     * Notifies the class about change in memory pressure.
+     * Note that |pressure| might get throttled or delayed, i.e. calling this method doesn't
+     * necessarily call the callbacks. See the class comment.
+     */
+    public void notifyPressure(@MemoryPressureLevel int pressure) {
+        ThreadUtils.assertOnUiThread();
+
+        if (mIsInsideThrottlingInterval) {
+            // We've already reported during this interval. Save |pressure| and act on
+            // it later, when the interval finishes.
+            mThrottledPressure = pressure;
+            return;
+        }
+
+        reportPressure(pressure);
+    }
+
+    /**
+     * Last pressure that was reported to MemoryPressureListener.
+     * Returns MemoryPressureLevel.NONE if nothing was reported yet.
+     */
+    public @MemoryPressureLevel int getLastReportedPressure() {
+        ThreadUtils.assertOnUiThread();
+        return mLastReportedPressure;
+    }
+
+    private void reportPressure(@MemoryPressureLevel int pressure) {
+        assert !mIsInsideThrottlingInterval : "Can't report pressure when throttling.";
+
+        startThrottlingInterval();
+
+        mLastReportedPressure = pressure;
+        mReportingCallback.onPressure(pressure);
+    }
+
+    private void onThrottlingIntervalFinished() {
+        mIsInsideThrottlingInterval = false;
+
+        // If there was a pressure change during the interval, report it.
+        if (mThrottledPressure != null && mLastReportedPressure != mThrottledPressure) {
+            int throttledPressure = mThrottledPressure;
+            mThrottledPressure = null;
+            reportPressure(throttledPressure);
+            return;
+        }
+
+        // The pressure didn't change during the interval. Report current pressure
+        // (starting a new interval) if we need to.
+        if (mPollingEnabled && mLastReportedPressure == MemoryPressureLevel.CRITICAL) {
+            reportCurrentPressure();
+        }
+    }
+
+    private void reportCurrentPressure() {
+        Integer pressure = mCurrentPressureSupplier.get();
+        if (pressure != null) {
+            reportPressure(pressure);
+        }
+    }
+
+    private void startThrottlingInterval() {
+        ThreadUtils.postOnUiThreadDelayed(mThrottlingIntervalTask, mThrottlingIntervalMs);
+        mIsInsideThrottlingInterval = true;
+    }
+
+    @VisibleForTesting
+    public void setCurrentPressureSupplierForTesting(Supplier<Integer> supplier) {
+        mCurrentPressureSupplier = supplier;
+    }
+
+    @VisibleForTesting
+    public void setReportingCallbackForTesting(MemoryPressureCallback callback) {
+        mReportingCallback = callback;
+    }
+
+    /**
+     * Queries current memory pressure.
+     * Returns null if the pressure couldn't be determined.
+     */
+    private static @MemoryPressureLevel Integer getCurrentMemoryPressure() {
+        long startNanos = elapsedRealtimeNanos();
+        try {
+            ActivityManager.RunningAppProcessInfo processInfo =
+                    new ActivityManager.RunningAppProcessInfo();
+            ActivityManager.getMyMemoryState(processInfo);
+            recordRealtimeNanosDuration(sGetMyMemoryStateSucceededTime, startNanos);
+            return memoryPressureFromTrimLevel(processInfo.lastTrimLevel);
+        } catch (Exception e) {
+            // Defensively catch all exceptions, just in case.
+            recordRealtimeNanosDuration(sGetMyMemoryStateFailedTime, startNanos);
+            return null;
+        }
+    }
+
+    private static void recordRealtimeNanosDuration(
+            CachedMetrics.Count1MHistogramSample histogram, long startNanos) {
+        // We're using Count1MHistogram, so we need to calculate duration in microseconds
+        long durationUs = TimeUnit.NANOSECONDS.toMicros(elapsedRealtimeNanos() - startNanos);
+        // record() takes int, so we need to clamp.
+        histogram.record((int) Math.min(durationUs, Integer.MAX_VALUE));
+    }
+
+    private static long elapsedRealtimeNanos() {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+            return SystemClock.elapsedRealtimeNanos();
+        } else {
+            return SystemClock.elapsedRealtime() * 1000000;
+        }
+    }
+
+    /**
+     * Maps ComponentCallbacks2.TRIM_* value to MemoryPressureLevel.
+     * Returns null if |level| couldn't be mapped and should be ignored.
+     */
+    @VisibleForTesting
+    public static @MemoryPressureLevel Integer memoryPressureFromTrimLevel(int level) {
+        if (level >= ComponentCallbacks2.TRIM_MEMORY_COMPLETE
+                || level == ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL) {
+            return MemoryPressureLevel.CRITICAL;
+        } else if (level >= ComponentCallbacks2.TRIM_MEMORY_BACKGROUND) {
+            // Don't notify on TRIM_MEMORY_UI_HIDDEN, since this class only
+            // dispatches actionable memory pressure signals to native.
+            return MemoryPressureLevel.MODERATE;
+        }
+        return null;
+    }
+}
diff --git a/base/android/java/src/org/chromium/base/memory/MemoryPressureUma.java b/base/android/java/src/org/chromium/base/memory/MemoryPressureUma.java
new file mode 100644
index 0000000..dc90f57
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/memory/MemoryPressureUma.java
@@ -0,0 +1,113 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base.memory;
+
+import android.content.ComponentCallbacks2;
+import android.content.res.Configuration;
+import android.support.annotation.IntDef;
+
+import org.chromium.base.ContextUtils;
+import org.chromium.base.ThreadUtils;
+import org.chromium.base.metrics.RecordHistogram;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Centralizes UMA data collection for Android-specific memory conditions.
+ */
+public class MemoryPressureUma implements ComponentCallbacks2 {
+    @IntDef({
+            Notification.UNKNOWN_TRIM_LEVEL, Notification.TRIM_MEMORY_COMPLETE,
+            Notification.TRIM_MEMORY_MODERATE, Notification.TRIM_MEMORY_BACKGROUND,
+            Notification.TRIM_MEMORY_UI_HIDDEN, Notification.TRIM_MEMORY_RUNNING_CRITICAL,
+            Notification.TRIM_MEMORY_RUNNING_LOW, Notification.TRIM_MEMORY_RUNNING_MODERATE,
+            Notification.ON_LOW_MEMORY, Notification.NOTIFICATION_MAX,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    private @interface Notification {
+        // WARNING: These values are persisted to logs. Entries should not be
+        //          renumbered and numeric values should never be reused.
+        // Keep in sync with "Android.MemoryPressureNotification" UMA enum.
+        int UNKNOWN_TRIM_LEVEL = 0;
+        int TRIM_MEMORY_COMPLETE = 1;
+        int TRIM_MEMORY_MODERATE = 2;
+        int TRIM_MEMORY_BACKGROUND = 3;
+        int TRIM_MEMORY_UI_HIDDEN = 4;
+        int TRIM_MEMORY_RUNNING_CRITICAL = 5;
+        int TRIM_MEMORY_RUNNING_LOW = 6;
+        int TRIM_MEMORY_RUNNING_MODERATE = 7;
+        int ON_LOW_MEMORY = 8;
+
+        // Must be the last one.
+        int NOTIFICATION_MAX = 9;
+    }
+
+    private final String mHistogramName;
+
+    private static MemoryPressureUma sInstance;
+
+    public static void initializeForBrowser() {
+        initializeInstance("Browser");
+    }
+
+    public static void initializeForChildService() {
+        initializeInstance("ChildService");
+    }
+
+    private static void initializeInstance(String processType) {
+        ThreadUtils.assertOnUiThread();
+        assert sInstance == null;
+        sInstance = new MemoryPressureUma(processType);
+        ContextUtils.getApplicationContext().registerComponentCallbacks(sInstance);
+    }
+
+    private MemoryPressureUma(String processType) {
+        mHistogramName = "Android.MemoryPressureNotification." + processType;
+    }
+
+    @Override
+    public void onLowMemory() {
+        record(Notification.ON_LOW_MEMORY);
+    }
+
+    @Override
+    public void onTrimMemory(int level) {
+        switch (level) {
+            case TRIM_MEMORY_COMPLETE:
+                record(Notification.TRIM_MEMORY_COMPLETE);
+                break;
+            case TRIM_MEMORY_MODERATE:
+                record(Notification.TRIM_MEMORY_MODERATE);
+                break;
+            case TRIM_MEMORY_BACKGROUND:
+                record(Notification.TRIM_MEMORY_BACKGROUND);
+                break;
+            case TRIM_MEMORY_UI_HIDDEN:
+                record(Notification.TRIM_MEMORY_UI_HIDDEN);
+                break;
+            case TRIM_MEMORY_RUNNING_CRITICAL:
+                record(Notification.TRIM_MEMORY_RUNNING_CRITICAL);
+                break;
+            case TRIM_MEMORY_RUNNING_LOW:
+                record(Notification.TRIM_MEMORY_RUNNING_LOW);
+                break;
+            case TRIM_MEMORY_RUNNING_MODERATE:
+                record(Notification.TRIM_MEMORY_RUNNING_MODERATE);
+                break;
+            default:
+                record(Notification.UNKNOWN_TRIM_LEVEL);
+                break;
+        }
+    }
+
+    @Override
+    public void onConfigurationChanged(Configuration configuration) {}
+
+    private void record(@Notification int notification) {
+        RecordHistogram.recordEnumeratedHistogram(
+                mHistogramName, notification, Notification.NOTIFICATION_MAX);
+    }
+}
diff --git a/base/android/java/src/org/chromium/base/metrics/CachedMetrics.java b/base/android/java/src/org/chromium/base/metrics/CachedMetrics.java
new file mode 100644
index 0000000..ba03e51
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/metrics/CachedMetrics.java
@@ -0,0 +1,307 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base.metrics;
+
+import org.chromium.base.library_loader.LibraryLoader;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Utility classes for recording UMA metrics before the native library
+ * may have been loaded.  Metrics are cached until the library is known
+ * to be loaded, then committed to the MetricsService all at once.
+ */
+public class CachedMetrics {
+    /**
+     * Base class for cached metric objects. Subclasses are expected to call
+     * addToCache() when some metric state gets recorded that requires a later
+     * commit operation when the native library is loaded.
+     */
+    private abstract static class CachedMetric {
+        private static final List<CachedMetric> sMetrics = new ArrayList<CachedMetric>();
+
+        protected final String mName;
+        protected boolean mCached;
+
+        /**
+         * @param name Name of the metric to record.
+         */
+        protected CachedMetric(String name) {
+            mName = name;
+        }
+
+        /**
+         * Adds this object to the sMetrics cache, if it hasn't been added already.
+         * Must be called while holding the synchronized(sMetrics) lock.
+         * Note: The synchronization is not done inside this function because subclasses
+         * need to increment their held values under lock to ensure thread-safety.
+         */
+        protected final void addToCache() {
+            assert Thread.holdsLock(sMetrics);
+
+            if (mCached) return;
+            sMetrics.add(this);
+            mCached = true;
+        }
+
+        /**
+         * Commits the metric. Expects the native library to be loaded.
+         * Must be called while holding the synchronized(sMetrics) lock.
+         */
+        protected abstract void commitAndClear();
+    }
+
+    /**
+     * Caches an action that will be recorded after native side is loaded.
+     */
+    public static class ActionEvent extends CachedMetric {
+        private int mCount;
+
+        public ActionEvent(String actionName) {
+            super(actionName);
+        }
+
+        public void record() {
+            synchronized (CachedMetric.sMetrics) {
+                if (LibraryLoader.getInstance().isInitialized()) {
+                    recordWithNative();
+                } else {
+                    mCount++;
+                    addToCache();
+                }
+            }
+        }
+
+        private void recordWithNative() {
+            RecordUserAction.record(mName);
+        }
+
+        @Override
+        protected void commitAndClear() {
+            while (mCount > 0) {
+                recordWithNative();
+                mCount--;
+            }
+        }
+    }
+
+    /** Caches a set of integer histogram samples. */
+    public static class SparseHistogramSample extends CachedMetric {
+        private final List<Integer> mSamples = new ArrayList<Integer>();
+
+        public SparseHistogramSample(String histogramName) {
+            super(histogramName);
+        }
+
+        public void record(int sample) {
+            synchronized (CachedMetric.sMetrics) {
+                if (LibraryLoader.getInstance().isInitialized()) {
+                    recordWithNative(sample);
+                } else {
+                    mSamples.add(sample);
+                    addToCache();
+                }
+            }
+        }
+
+        private void recordWithNative(int sample) {
+            RecordHistogram.recordSparseSlowlyHistogram(mName, sample);
+        }
+
+        @Override
+        protected void commitAndClear() {
+            for (Integer sample : mSamples) {
+                recordWithNative(sample);
+            }
+            mSamples.clear();
+        }
+    }
+
+    /** Caches a set of enumerated histogram samples. */
+    public static class EnumeratedHistogramSample extends CachedMetric {
+        private final List<Integer> mSamples = new ArrayList<Integer>();
+        private final int mMaxValue;
+
+        public EnumeratedHistogramSample(String histogramName, int maxValue) {
+            super(histogramName);
+            mMaxValue = maxValue;
+        }
+
+        public void record(int sample) {
+            synchronized (CachedMetric.sMetrics) {
+                if (LibraryLoader.getInstance().isInitialized()) {
+                    recordWithNative(sample);
+                } else {
+                    mSamples.add(sample);
+                    addToCache();
+                }
+            }
+        }
+
+        private void recordWithNative(int sample) {
+            RecordHistogram.recordEnumeratedHistogram(mName, sample, mMaxValue);
+        }
+
+        @Override
+        protected void commitAndClear() {
+            for (Integer sample : mSamples) {
+                recordWithNative(sample);
+            }
+            mSamples.clear();
+        }
+    }
+
+    /** Caches a set of times histogram samples. */
+    public static class TimesHistogramSample extends CachedMetric {
+        private final List<Long> mSamples = new ArrayList<Long>();
+        private final TimeUnit mTimeUnit;
+
+        public TimesHistogramSample(String histogramName, TimeUnit timeUnit) {
+            super(histogramName);
+            RecordHistogram.assertTimesHistogramSupportsUnit(timeUnit);
+            mTimeUnit = timeUnit;
+        }
+
+        public void record(long sample) {
+            synchronized (CachedMetric.sMetrics) {
+                if (LibraryLoader.getInstance().isInitialized()) {
+                    recordWithNative(sample);
+                } else {
+                    mSamples.add(sample);
+                    addToCache();
+                }
+            }
+        }
+
+        private void recordWithNative(long sample) {
+            RecordHistogram.recordTimesHistogram(mName, sample, mTimeUnit);
+        }
+
+        @Override
+        protected void commitAndClear() {
+            for (Long sample : mSamples) {
+                recordWithNative(sample);
+            }
+            mSamples.clear();
+        }
+    }
+
+    /** Caches a set of boolean histogram samples. */
+    public static class BooleanHistogramSample extends CachedMetric {
+        private final List<Boolean> mSamples = new ArrayList<Boolean>();
+
+        public BooleanHistogramSample(String histogramName) {
+            super(histogramName);
+        }
+
+        public void record(boolean sample) {
+            synchronized (CachedMetric.sMetrics) {
+                if (LibraryLoader.getInstance().isInitialized()) {
+                    recordWithNative(sample);
+                } else {
+                    mSamples.add(sample);
+                    addToCache();
+                }
+            }
+        }
+
+        private void recordWithNative(boolean sample) {
+            RecordHistogram.recordBooleanHistogram(mName, sample);
+        }
+
+        @Override
+        protected void commitAndClear() {
+            for (Boolean sample : mSamples) {
+                recordWithNative(sample);
+            }
+            mSamples.clear();
+        }
+    }
+
+    /**
+     * Caches a set of custom count histogram samples.
+     * Corresponds to UMA_HISTOGRAM_CUSTOM_COUNTS C++ macro.
+     */
+    public static class CustomCountHistogramSample extends CachedMetric {
+        private final List<Integer> mSamples = new ArrayList<Integer>();
+        private final int mMin;
+        private final int mMax;
+        private final int mNumBuckets;
+
+        public CustomCountHistogramSample(String histogramName, int min, int max, int numBuckets) {
+            super(histogramName);
+            mMin = min;
+            mMax = max;
+            mNumBuckets = numBuckets;
+        }
+
+        public void record(int sample) {
+            synchronized (CachedMetric.sMetrics) {
+                if (LibraryLoader.getInstance().isInitialized()) {
+                    recordWithNative(sample);
+                } else {
+                    mSamples.add(sample);
+                    addToCache();
+                }
+            }
+        }
+
+        private void recordWithNative(int sample) {
+            RecordHistogram.recordCustomCountHistogram(mName, sample, mMin, mMax, mNumBuckets);
+        }
+
+        @Override
+        protected void commitAndClear() {
+            for (Integer sample : mSamples) {
+                recordWithNative(sample);
+            }
+            mSamples.clear();
+        }
+    }
+
+    /**
+     * Caches a set of count histogram samples in range [1, 100).
+     * Corresponds to UMA_HISTOGRAM_COUNTS_100 C++ macro.
+     */
+    public static class Count100HistogramSample extends CustomCountHistogramSample {
+        public Count100HistogramSample(String histogramName) {
+            super(histogramName, 1, 100, 50);
+        }
+    }
+
+    /**
+     * Caches a set of count histogram samples in range [1, 1000).
+     * Corresponds to UMA_HISTOGRAM_COUNTS_1000 C++ macro.
+     */
+    public static class Count1000HistogramSample extends CustomCountHistogramSample {
+        public Count1000HistogramSample(String histogramName) {
+            super(histogramName, 1, 1000, 50);
+        }
+    }
+
+    /**
+     * Caches a set of count histogram samples in range [1, 1000000).
+     * Corresponds to UMA_HISTOGRAM_COUNTS_1M C++ macro.
+     */
+    public static class Count1MHistogramSample extends CustomCountHistogramSample {
+        public Count1MHistogramSample(String histogramName) {
+            super(histogramName, 1, 1000000, 50);
+        }
+    }
+
+    /**
+     * Calls out to native code to commit any cached histograms and events.
+     * Should be called once the native library has been loaded.
+     */
+    public static void commitCachedMetrics() {
+        synchronized (CachedMetric.sMetrics) {
+            for (CachedMetric metric : CachedMetric.sMetrics) {
+                metric.commitAndClear();
+            }
+        }
+    }
+}
diff --git a/base/android/java/src/org/chromium/base/metrics/RecordHistogram.java b/base/android/java/src/org/chromium/base/metrics/RecordHistogram.java
new file mode 100644
index 0000000..898f009
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/metrics/RecordHistogram.java
@@ -0,0 +1,331 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base.metrics;
+
+import org.chromium.base.VisibleForTesting;
+import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.MainDex;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Java API for recording UMA histograms.
+ *
+ * Internally, histograms objects are cached on the Java side by their pointer
+ * values (converted to long). This is safe to do because C++ Histogram objects
+ * are never freed. Caching them on the Java side prevents needing to do costly
+ * Java String to C++ string conversions on the C++ side during lookup.
+ *
+ * Note: the JNI calls are relatively costly - avoid calling these methods in performance-critical
+ * code.
+ */
+@JNINamespace("base::android")
+@MainDex
+public class RecordHistogram {
+    private static Throwable sDisabledBy;
+    private static Map<String, Long> sCache =
+            Collections.synchronizedMap(new HashMap<String, Long>());
+
+    /**
+     * Tests may not have native initialized, so they may need to disable metrics. The value should
+     * be reset after the test done, to avoid carrying over state to unrelated tests.
+     *
+     * In JUnit tests this can be done automatically using
+     * {@link org.chromium.chrome.browser.DisableHistogramsRule}
+     */
+    @VisibleForTesting
+    public static void setDisabledForTests(boolean disabled) {
+        if (disabled && sDisabledBy != null) {
+            throw new IllegalStateException("Histograms are already disabled.", sDisabledBy);
+        }
+        sDisabledBy = disabled ? new Throwable() : null;
+    }
+
+    private static long getCachedHistogramKey(String name) {
+        Long key = sCache.get(name);
+        // Note: If key is null, we don't have it cached. In that case, pass 0
+        // to the native code, which gets converted to a null histogram pointer
+        // which will cause the native code to look up the object on the native
+        // side.
+        return (key == null ? 0 : key);
+    }
+
+    /**
+     * Records a sample in a boolean UMA histogram of the given name. Boolean histogram has two
+     * buckets, corresponding to success (true) and failure (false). This is the Java equivalent of
+     * the UMA_HISTOGRAM_BOOLEAN C++ macro.
+     * @param name name of the histogram
+     * @param sample sample to be recorded, either true or false
+     */
+    public static void recordBooleanHistogram(String name, boolean sample) {
+        if (sDisabledBy != null) return;
+        long key = getCachedHistogramKey(name);
+        long result = nativeRecordBooleanHistogram(name, key, sample);
+        if (result != key) sCache.put(name, result);
+    }
+
+    /**
+     * Records a sample in an enumerated histogram of the given name and boundary. Note that
+     * |boundary| identifies the histogram - it should be the same at every invocation. This is the
+     * Java equivalent of the UMA_HISTOGRAM_ENUMERATION C++ macro.
+     * @param name name of the histogram
+     * @param sample sample to be recorded, at least 0 and at most |boundary| - 1
+     * @param boundary upper bound for legal sample values - all sample values have to be strictly
+     *        lower than |boundary|
+     */
+    public static void recordEnumeratedHistogram(String name, int sample, int boundary) {
+        if (sDisabledBy != null) return;
+        long key = getCachedHistogramKey(name);
+        long result = nativeRecordEnumeratedHistogram(name, key, sample, boundary);
+        if (result != key) sCache.put(name, result);
+    }
+
+    /**
+     * Records a sample in a count histogram. This is the Java equivalent of the
+     * UMA_HISTOGRAM_COUNTS C++ macro.
+     * @param name name of the histogram
+     * @param sample sample to be recorded, at least 1 and at most 999999
+     */
+    public static void recordCountHistogram(String name, int sample) {
+        recordCustomCountHistogram(name, sample, 1, 1000000, 50);
+    }
+
+    /**
+     * Records a sample in a count histogram. This is the Java equivalent of the
+     * UMA_HISTOGRAM_COUNTS_100 C++ macro.
+     * @param name name of the histogram
+     * @param sample sample to be recorded, at least 1 and at most 99
+     */
+    public static void recordCount100Histogram(String name, int sample) {
+        recordCustomCountHistogram(name, sample, 1, 100, 50);
+    }
+
+    /**
+     * Records a sample in a count histogram. This is the Java equivalent of the
+     * UMA_HISTOGRAM_COUNTS_1000 C++ macro.
+     * @param name name of the histogram
+     * @param sample sample to be recorded, at least 1 and at most 999
+     */
+    public static void recordCount1000Histogram(String name, int sample) {
+        recordCustomCountHistogram(name, sample, 1, 1000, 50);
+    }
+
+    /**
+     * Records a sample in a count histogram. This is the Java equivalent of the
+     * UMA_HISTOGRAM_CUSTOM_COUNTS C++ macro.
+     * @param name name of the histogram
+     * @param sample sample to be recorded, at least |min| and at most |max| - 1
+     * @param min lower bound for expected sample values. It must be >= 1
+     * @param max upper bounds for expected sample values
+     * @param numBuckets the number of buckets
+     */
+    public static void recordCustomCountHistogram(
+            String name, int sample, int min, int max, int numBuckets) {
+        if (sDisabledBy != null) return;
+        long key = getCachedHistogramKey(name);
+        long result = nativeRecordCustomCountHistogram(name, key, sample, min, max, numBuckets);
+        if (result != key) sCache.put(name, result);
+    }
+
+    /**
+     * Records a sample in a linear histogram. This is the Java equivalent for using
+     * base::LinearHistogram.
+     * @param name name of the histogram
+     * @param sample sample to be recorded, at least |min| and at most |max| - 1.
+     * @param min lower bound for expected sample values, should be at least 1.
+     * @param max upper bounds for expected sample values
+     * @param numBuckets the number of buckets
+     */
+    public static void recordLinearCountHistogram(
+            String name, int sample, int min, int max, int numBuckets) {
+        if (sDisabledBy != null) return;
+        long key = getCachedHistogramKey(name);
+        long result = nativeRecordLinearCountHistogram(name, key, sample, min, max, numBuckets);
+        if (result != key) sCache.put(name, result);
+    }
+
+    /**
+     * Records a sample in a percentage histogram. This is the Java equivalent of the
+     * UMA_HISTOGRAM_PERCENTAGE C++ macro.
+     * @param name name of the histogram
+     * @param sample sample to be recorded, at least 0 and at most 100.
+     */
+    public static void recordPercentageHistogram(String name, int sample) {
+        if (sDisabledBy != null) return;
+        long key = getCachedHistogramKey(name);
+        long result = nativeRecordEnumeratedHistogram(name, key, sample, 101);
+        if (result != key) sCache.put(name, result);
+    }
+
+    /**
+     * Records a sparse histogram. This is the Java equivalent of UmaHistogramSparse.
+     * @param name name of the histogram
+     * @param sample sample to be recorded. All values of |sample| are valid, including negative
+     *        values.
+     */
+    public static void recordSparseSlowlyHistogram(String name, int sample) {
+        if (sDisabledBy != null) return;
+        long key = getCachedHistogramKey(name);
+        long result = nativeRecordSparseHistogram(name, key, sample);
+        if (result != key) sCache.put(name, result);
+    }
+
+    /**
+     * Records a sample in a histogram of times. Useful for recording short durations. This is the
+     * Java equivalent of the UMA_HISTOGRAM_TIMES C++ macro.
+     * Note that histogram samples will always be converted to milliseconds when logged.
+     * @param name name of the histogram
+     * @param duration duration to be recorded
+     * @param timeUnit the unit of the duration argument (must be >= MILLISECONDS)
+     */
+    public static void recordTimesHistogram(String name, long duration, TimeUnit timeUnit) {
+        assertTimesHistogramSupportsUnit(timeUnit);
+        recordCustomTimesHistogramMilliseconds(
+                name, timeUnit.toMillis(duration), 1, TimeUnit.SECONDS.toMillis(10), 50);
+    }
+
+    /**
+     * Records a sample in a histogram of times. Useful for recording medium durations. This is the
+     * Java equivalent of the UMA_HISTOGRAM_MEDIUM_TIMES C++ macro.
+     * Note that histogram samples will always be converted to milliseconds when logged.
+     * @param name name of the histogram
+     * @param duration duration to be recorded
+     * @param timeUnit the unit of the duration argument (must be >= MILLISECONDS)
+     */
+    public static void recordMediumTimesHistogram(String name, long duration, TimeUnit timeUnit) {
+        assertTimesHistogramSupportsUnit(timeUnit);
+        recordCustomTimesHistogramMilliseconds(
+                name, timeUnit.toMillis(duration), 10, TimeUnit.MINUTES.toMillis(3), 50);
+    }
+
+    /**
+     * Records a sample in a histogram of times. Useful for recording long durations. This is the
+     * Java equivalent of the UMA_HISTOGRAM_LONG_TIMES C++ macro.
+     * Note that histogram samples will always be converted to milliseconds when logged.
+     * @param name name of the histogram
+     * @param duration duration to be recorded
+     * @param timeUnit the unit of the duration argument (must be >= MILLISECONDS)
+     */
+    public static void recordLongTimesHistogram(String name, long duration, TimeUnit timeUnit) {
+        assertTimesHistogramSupportsUnit(timeUnit);
+        recordCustomTimesHistogramMilliseconds(
+                name, timeUnit.toMillis(duration), 1, TimeUnit.HOURS.toMillis(1), 50);
+    }
+
+    /**
+     * Records a sample in a histogram of times. Useful for recording long durations. This is the
+     * Java equivalent of the UMA_HISTOGRAM_LONG_TIMES_100 C++ macro.
+     * Note that histogram samples will always be converted to milliseconds when logged.
+     * @param name name of the histogram
+     * @param duration duration to be recorded
+     * @param timeUnit the unit of the duration argument (must be >= MILLISECONDS)
+     */
+    public static void recordLongTimesHistogram100(String name, long duration, TimeUnit timeUnit) {
+        assertTimesHistogramSupportsUnit(timeUnit);
+        recordCustomTimesHistogramMilliseconds(
+                name, timeUnit.toMillis(duration), 1, TimeUnit.HOURS.toMillis(1), 100);
+    }
+
+    /**
+     * Records a sample in a histogram of times with custom buckets. This is the Java equivalent of
+     * the UMA_HISTOGRAM_CUSTOM_TIMES C++ macro.
+     * Note that histogram samples will always be converted to milliseconds when logged.
+     * @param name name of the histogram
+     * @param duration duration to be recorded
+     * @param min the minimum bucket value
+     * @param max the maximum bucket value
+     * @param timeUnit the unit of the duration, min, and max arguments (must be >= MILLISECONDS)
+     * @param numBuckets the number of buckets
+     */
+    public static void recordCustomTimesHistogram(
+            String name, long duration, long min, long max, TimeUnit timeUnit, int numBuckets) {
+        assertTimesHistogramSupportsUnit(timeUnit);
+        recordCustomTimesHistogramMilliseconds(name, timeUnit.toMillis(duration),
+                timeUnit.toMillis(min), timeUnit.toMillis(max), numBuckets);
+    }
+
+    /**
+     * Records a sample in a histogram of sizes in KB. This is the Java equivalent of the
+     * UMA_HISTOGRAM_MEMORY_KB C++ macro.
+     *
+     * Good for sizes up to about 500MB.
+     *
+     * @param name name of the histogram.
+     * @param sizeInkB Sample to record in KB.
+     */
+    public static void recordMemoryKBHistogram(String name, int sizeInKB) {
+        recordCustomCountHistogram(name, sizeInKB, 1000, 500000, 50);
+    }
+
+    /**
+     * Asserts that the time unit is supported by TimesHistogram.
+     * @param timeUnit the unit, must be >= MILLISECONDS
+     */
+    /* package */ static void assertTimesHistogramSupportsUnit(TimeUnit timeUnit) {
+        // Use extra variable, or else 'git cl format' produces weird results.
+        boolean supported = timeUnit != TimeUnit.NANOSECONDS && timeUnit != TimeUnit.MICROSECONDS;
+        assert supported : "TimesHistogram doesn't support MICROSECOND and NANOSECONDS time units. "
+                           + "Consider using CountHistogram instead.";
+    }
+
+    private static int clampToInt(long value) {
+        if (value > Integer.MAX_VALUE) return Integer.MAX_VALUE;
+        // Note: Clamping to MIN_VALUE rather than 0, to let base/ histograms code
+        // do its own handling of negative values in the future.
+        if (value < Integer.MIN_VALUE) return Integer.MIN_VALUE;
+        return (int) value;
+    }
+
+    private static void recordCustomTimesHistogramMilliseconds(
+            String name, long duration, long min, long max, int numBuckets) {
+        if (sDisabledBy != null) return;
+        long key = getCachedHistogramKey(name);
+        // Note: Duration, min and max are clamped to int here because that's what's expected by
+        // the native histograms API. Callers of these functions still pass longs because that's
+        // the types returned by TimeUnit and System.currentTimeMillis() APIs, from which these
+        // values come.
+        assert max == clampToInt(max);
+        long result = nativeRecordCustomTimesHistogramMilliseconds(
+                name, key, clampToInt(duration), clampToInt(min), clampToInt(max), numBuckets);
+        if (result != key) sCache.put(name, result);
+    }
+
+    /**
+     * Returns the number of samples recorded in the given bucket of the given histogram.
+     * @param name name of the histogram to look up
+     * @param sample the bucket containing this sample value will be looked up
+     */
+    @VisibleForTesting
+    public static int getHistogramValueCountForTesting(String name, int sample) {
+        return nativeGetHistogramValueCountForTesting(name, sample);
+    }
+
+    /**
+     * Returns the number of samples recorded for the given histogram.
+     * @param name name of the histogram to look up.
+     */
+    @VisibleForTesting
+    public static int getHistogramTotalCountForTesting(String name) {
+        return nativeGetHistogramTotalCountForTesting(name);
+    }
+
+    private static native long nativeRecordCustomTimesHistogramMilliseconds(
+            String name, long key, int duration, int min, int max, int numBuckets);
+
+    private static native long nativeRecordBooleanHistogram(String name, long key, boolean sample);
+    private static native long nativeRecordEnumeratedHistogram(
+            String name, long key, int sample, int boundary);
+    private static native long nativeRecordCustomCountHistogram(
+            String name, long key, int sample, int min, int max, int numBuckets);
+    private static native long nativeRecordLinearCountHistogram(
+            String name, long key, int sample, int min, int max, int numBuckets);
+    private static native long nativeRecordSparseHistogram(String name, long key, int sample);
+
+    private static native int nativeGetHistogramValueCountForTesting(String name, int sample);
+    private static native int nativeGetHistogramTotalCountForTesting(String name);
+}
diff --git a/base/android/java/src/org/chromium/base/metrics/RecordUserAction.java b/base/android/java/src/org/chromium/base/metrics/RecordUserAction.java
new file mode 100644
index 0000000..0d2ba54
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/metrics/RecordUserAction.java
@@ -0,0 +1,85 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base.metrics;
+
+import org.chromium.base.ThreadUtils;
+import org.chromium.base.VisibleForTesting;
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.JNINamespace;
+
+/**
+ * Java API for recording UMA actions.
+ *
+ * WARNINGS:
+ * JNI calls are relatively costly - avoid using in performance-critical code.
+ *
+ * We use a script (extract_actions.py) to scan the source code and extract actions. A string
+ * literal (not a variable) must be passed to record().
+ */
+@JNINamespace("base::android")
+public class RecordUserAction {
+    private static Throwable sDisabledBy;
+
+    /**
+     * Tests may not have native initialized, so they may need to disable metrics. The value should
+     * be reset after the test done, to avoid carrying over state to unrelated tests.
+     */
+    @VisibleForTesting
+    public static void setDisabledForTests(boolean disabled) {
+        if (disabled && sDisabledBy != null) {
+            throw new IllegalStateException("UserActions are already disabled.", sDisabledBy);
+        }
+        sDisabledBy = disabled ? new Throwable() : null;
+    }
+
+    public static void record(final String action) {
+        if (sDisabledBy != null) return;
+
+        if (ThreadUtils.runningOnUiThread()) {
+            nativeRecordUserAction(action);
+            return;
+        }
+
+        ThreadUtils.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                nativeRecordUserAction(action);
+            }
+        });
+    }
+
+    /**
+     * Interface to a class that receives a callback for each UserAction that is recorded.
+     */
+    public interface UserActionCallback {
+        @CalledByNative("UserActionCallback")
+        void onActionRecorded(String action);
+    }
+
+    private static long sNativeActionCallback;
+
+    /**
+     * Register a callback that is executed for each recorded UserAction.
+     * Only one callback can be registered at a time.
+     * The callback has to be unregistered using removeActionCallbackForTesting().
+     */
+    public static void setActionCallbackForTesting(UserActionCallback callback) {
+        assert sNativeActionCallback == 0;
+        sNativeActionCallback = nativeAddActionCallbackForTesting(callback);
+    }
+
+    /**
+     * Unregister the UserActionCallback.
+     */
+    public static void removeActionCallbackForTesting() {
+        assert sNativeActionCallback != 0;
+        nativeRemoveActionCallbackForTesting(sNativeActionCallback);
+        sNativeActionCallback = 0;
+    }
+
+    private static native void nativeRecordUserAction(String action);
+    private static native long nativeAddActionCallbackForTesting(UserActionCallback callback);
+    private static native void nativeRemoveActionCallbackForTesting(long callbackId);
+}
diff --git a/base/android/java/src/org/chromium/base/metrics/StatisticsRecorderAndroid.java b/base/android/java/src/org/chromium/base/metrics/StatisticsRecorderAndroid.java
new file mode 100644
index 0000000..bff3fae
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/metrics/StatisticsRecorderAndroid.java
@@ -0,0 +1,27 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base.metrics;
+
+import org.chromium.base.annotations.JNINamespace;
+
+/**
+ * Java API which exposes the registered histograms on the native side as
+ * JSON test.
+ */
+@JNINamespace("base::android")
+public final class StatisticsRecorderAndroid {
+    private StatisticsRecorderAndroid() {}
+
+    /**
+     * @param verbosityLevel controls the information that should be included when dumping each of
+     * the histogram.
+     * @return All the registered histograms as JSON text.
+     */
+    public static String toJson(@JSONVerbosityLevel int verbosityLevel) {
+        return nativeToJson(verbosityLevel);
+    }
+
+    private static native String nativeToJson(@JSONVerbosityLevel int verbosityLevel);
+}
\ No newline at end of file
diff --git a/base/android/java/src/org/chromium/base/multidex/ChromiumMultiDexInstaller.java b/base/android/java/src/org/chromium/base/multidex/ChromiumMultiDexInstaller.java
new file mode 100644
index 0000000..5588ec5
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/multidex/ChromiumMultiDexInstaller.java
@@ -0,0 +1,78 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base.multidex;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.Build;
+import android.support.multidex.MultiDex;
+
+import org.chromium.base.ContextUtils;
+import org.chromium.base.Log;
+import org.chromium.base.VisibleForTesting;
+import org.chromium.base.annotations.MainDex;
+
+/**
+ *  Performs multidex installation for non-isolated processes.
+ */
+@MainDex
+public class ChromiumMultiDexInstaller {
+    private static final String TAG = "base_multidex";
+
+    /**
+     * Suffix for the meta-data tag in the AndroidManifext.xml that determines whether loading
+     * secondary dexes should be skipped for a given process name.
+     */
+    private static final String IGNORE_MULTIDEX_KEY = ".ignore_multidex";
+
+    /**
+     *  Installs secondary dexes if possible/necessary.
+     *
+     *  Isolated processes (e.g. renderer processes) can't load secondary dex files on
+     *  K and below, so we don't even try in that case.
+     *
+     *  In release builds of app apks (as opposed to test apks), this is a no-op because:
+     *    - multidex isn't necessary in release builds because we run proguard there and
+     *      thus aren't threatening to hit the dex limit; and
+     *    - calling MultiDex.install, even in the absence of secondary dexes, causes a
+     *      significant regression in start-up time (crbug.com/525695).
+     *
+     *  @param context The application context.
+     */
+    @VisibleForTesting
+    public static void install(Context context) {
+        // TODO(jbudorick): Back out this version check once support for K & below works.
+        // http://crbug.com/512357
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP
+                && !shouldInstallMultiDex(context)) {
+            Log.i(TAG, "Skipping multidex installation: not needed for process.");
+        } else {
+            MultiDex.install(context);
+            Log.i(TAG, "Completed multidex installation.");
+        }
+    }
+
+    // Determines whether MultiDex should be installed for the current process.  Isolated
+    // Processes should skip MultiDex as they can not actually access the files on disk.
+    // Privileged processes need ot have all of their dependencies in the MainDex for
+    // performance reasons.
+    private static boolean shouldInstallMultiDex(Context context) {
+        if (ContextUtils.isIsolatedProcess()) {
+            return false;
+        }
+        String currentProcessName = ContextUtils.getProcessName();
+        PackageManager packageManager = context.getPackageManager();
+        try {
+            ApplicationInfo appInfo = packageManager.getApplicationInfo(context.getPackageName(),
+                    PackageManager.GET_META_DATA);
+            if (appInfo == null || appInfo.metaData == null) return true;
+            return !appInfo.metaData.getBoolean(currentProcessName + IGNORE_MULTIDEX_KEY, false);
+        } catch (PackageManager.NameNotFoundException e) {
+            return true;
+        }
+    }
+
+}
diff --git a/base/android/java/src/org/chromium/base/process_launcher/ChildConnectionAllocator.java b/base/android/java/src/org/chromium/base/process_launcher/ChildConnectionAllocator.java
new file mode 100644
index 0000000..43ae259
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/process_launcher/ChildConnectionAllocator.java
@@ -0,0 +1,305 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base.process_launcher;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+
+import org.chromium.base.Log;
+import org.chromium.base.VisibleForTesting;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Queue;
+
+/**
+ * This class is responsible for allocating and managing connections to child
+ * process services. These connections are in a pool (the services are defined
+ * in the AndroidManifest.xml).
+ */
+public class ChildConnectionAllocator {
+    private static final String TAG = "ChildConnAllocator";
+
+    /** Factory interface. Used by tests to specialize created connections. */
+    @VisibleForTesting
+    public interface ConnectionFactory {
+        ChildProcessConnection createConnection(Context context, ComponentName serviceName,
+                boolean bindToCaller, boolean bindAsExternalService, Bundle serviceBundle);
+    }
+
+    /** Default implementation of the ConnectionFactory that creates actual connections. */
+    private static class ConnectionFactoryImpl implements ConnectionFactory {
+        @Override
+        public ChildProcessConnection createConnection(Context context, ComponentName serviceName,
+                boolean bindToCaller, boolean bindAsExternalService, Bundle serviceBundle) {
+            return new ChildProcessConnection(
+                    context, serviceName, bindToCaller, bindAsExternalService, serviceBundle);
+        }
+    }
+
+    // Delay between the call to freeConnection and the connection actually beeing   freed.
+    private static final long FREE_CONNECTION_DELAY_MILLIS = 1;
+
+    // The handler of the thread on which all interations should happen.
+    private final Handler mLauncherHandler;
+
+    // Connections to services. Indices of the array correspond to the service numbers.
+    private final ChildProcessConnection[] mChildProcessConnections;
+
+    // Runnable which will be called when allocator wants to allocate a new connection, but does
+    // not have any more free slots. May be null.
+    private final Runnable mFreeSlotCallback;
+    private final String mPackageName;
+    private final String mServiceClassName;
+    private final boolean mBindToCaller;
+    private final boolean mBindAsExternalService;
+    private final boolean mUseStrongBinding;
+
+    // The list of free (not bound) service indices.
+    private final ArrayList<Integer> mFreeConnectionIndices;
+
+    private final Queue<Runnable> mPendingAllocations = new ArrayDeque<>();
+
+    private ConnectionFactory mConnectionFactory = new ConnectionFactoryImpl();
+
+    /**
+     * Factory method that retrieves the service name and number of service from the
+     * AndroidManifest.xml.
+     */
+    public static ChildConnectionAllocator create(Context context, Handler launcherHandler,
+            Runnable freeSlotCallback, String packageName, String serviceClassName,
+            String numChildServicesManifestKey, boolean bindToCaller, boolean bindAsExternalService,
+            boolean useStrongBinding) {
+        int numServices = -1;
+        PackageManager packageManager = context.getPackageManager();
+        try {
+            ApplicationInfo appInfo =
+                    packageManager.getApplicationInfo(packageName, PackageManager.GET_META_DATA);
+            if (appInfo.metaData != null) {
+                numServices = appInfo.metaData.getInt(numChildServicesManifestKey, -1);
+            }
+        } catch (PackageManager.NameNotFoundException e) {
+            throw new RuntimeException("Could not get application info.");
+        }
+
+        if (numServices < 0) {
+            throw new RuntimeException("Illegal meta data value for number of child services");
+        }
+
+        // Check that the service exists.
+        try {
+            // PackageManager#getServiceInfo() throws an exception if the service does not exist.
+            packageManager.getServiceInfo(
+                    new ComponentName(packageName, serviceClassName + "0"), 0);
+        } catch (PackageManager.NameNotFoundException e) {
+            throw new RuntimeException("Illegal meta data value: the child service doesn't exist");
+        }
+
+        return new ChildConnectionAllocator(launcherHandler, freeSlotCallback, packageName,
+                serviceClassName, bindToCaller, bindAsExternalService, useStrongBinding,
+                numServices);
+    }
+
+    /**
+     * Factory method used with some tests to create an allocator with values passed in directly
+     * instead of being retrieved from the AndroidManifest.xml.
+     */
+    @VisibleForTesting
+    public static ChildConnectionAllocator createForTest(Runnable freeSlotCallback,
+            String packageName, String serviceClassName, int serviceCount, boolean bindToCaller,
+            boolean bindAsExternalService, boolean useStrongBinding) {
+        return new ChildConnectionAllocator(new Handler(), freeSlotCallback, packageName,
+                serviceClassName, bindToCaller, bindAsExternalService, useStrongBinding,
+                serviceCount);
+    }
+
+    private ChildConnectionAllocator(Handler launcherHandler, Runnable freeSlotCallback,
+            String packageName, String serviceClassName, boolean bindToCaller,
+            boolean bindAsExternalService, boolean useStrongBinding, int numChildServices) {
+        mFreeSlotCallback = freeSlotCallback;
+        mLauncherHandler = launcherHandler;
+        assert isRunningOnLauncherThread();
+        mPackageName = packageName;
+        mServiceClassName = serviceClassName;
+        mBindToCaller = bindToCaller;
+        mBindAsExternalService = bindAsExternalService;
+        mUseStrongBinding = useStrongBinding;
+        mChildProcessConnections = new ChildProcessConnection[numChildServices];
+        mFreeConnectionIndices = new ArrayList<Integer>(numChildServices);
+        for (int i = 0; i < numChildServices; i++) {
+            mFreeConnectionIndices.add(i);
+        }
+    }
+
+    /** @return a bound connection, or null if there are no free slots. */
+    public ChildProcessConnection allocate(Context context, Bundle serviceBundle,
+            final ChildProcessConnection.ServiceCallback serviceCallback) {
+        assert isRunningOnLauncherThread();
+        if (mFreeConnectionIndices.isEmpty()) {
+            Log.d(TAG, "Ran out of services to allocate.");
+            return null;
+        }
+        int slot = mFreeConnectionIndices.remove(0);
+        assert mChildProcessConnections[slot] == null;
+        ComponentName serviceName = new ComponentName(mPackageName, mServiceClassName + slot);
+
+        // Wrap the service callbacks so that:
+        // - we can intercept onChildProcessDied and clean-up connections
+        // - the callbacks are actually posted so that this method will return before the callbacks
+        //   are called (so that the caller may set any reference to the returned connection before
+        //   any callback logic potentially tries to access that connection).
+        ChildProcessConnection.ServiceCallback serviceCallbackWrapper =
+                new ChildProcessConnection.ServiceCallback() {
+                    @Override
+                    public void onChildStarted() {
+                        assert isRunningOnLauncherThread();
+                        if (serviceCallback != null) {
+                            mLauncherHandler.post(new Runnable() {
+                                @Override
+                                public void run() {
+                                    serviceCallback.onChildStarted();
+                                }
+                            });
+                        }
+                    }
+
+                    @Override
+                    public void onChildStartFailed(final ChildProcessConnection connection) {
+                        assert isRunningOnLauncherThread();
+                        if (serviceCallback != null) {
+                            mLauncherHandler.post(new Runnable() {
+                                @Override
+                                public void run() {
+                                    serviceCallback.onChildStartFailed(connection);
+                                }
+                            });
+                        }
+                        freeConnectionWithDelay(connection);
+                    }
+
+                    @Override
+                    public void onChildProcessDied(final ChildProcessConnection connection) {
+                        assert isRunningOnLauncherThread();
+                        if (serviceCallback != null) {
+                            mLauncherHandler.post(new Runnable() {
+                                @Override
+                                public void run() {
+                                    serviceCallback.onChildProcessDied(connection);
+                                }
+                            });
+                        }
+                        freeConnectionWithDelay(connection);
+                    }
+
+                    private void freeConnectionWithDelay(final ChildProcessConnection connection) {
+                        // Freeing a service should be delayed. This is so that we avoid immediately
+                        // reusing the freed service (see http://crbug.com/164069): the framework
+                        // might keep a service process alive when it's been unbound for a short
+                        // time. If a new connection to the same service is bound at that point, the
+                        // process is reused and bad things happen (mostly static variables are set
+                        // when we don't expect them to).
+                        mLauncherHandler.postDelayed(new Runnable() {
+                            @Override
+                            public void run() {
+                                free(connection);
+                            }
+                        }, FREE_CONNECTION_DELAY_MILLIS);
+                    }
+                };
+
+        ChildProcessConnection connection = mConnectionFactory.createConnection(
+                context, serviceName, mBindToCaller, mBindAsExternalService, serviceBundle);
+        mChildProcessConnections[slot] = connection;
+
+        connection.start(mUseStrongBinding, serviceCallbackWrapper);
+        Log.d(TAG, "Allocator allocated and bound a connection, name: %s, slot: %d",
+                mServiceClassName, slot);
+        return connection;
+    }
+
+    /** Frees a connection and notifies listeners. */
+    private void free(ChildProcessConnection connection) {
+        assert isRunningOnLauncherThread();
+
+        // mChildProcessConnections is relatively short (20 items at max at this point).
+        // We are better of iterating than caching in a map.
+        int slot = Arrays.asList(mChildProcessConnections).indexOf(connection);
+        if (slot == -1) {
+            Log.e(TAG, "Unable to find connection to free.");
+            assert false;
+        } else {
+            mChildProcessConnections[slot] = null;
+            assert !mFreeConnectionIndices.contains(slot);
+            mFreeConnectionIndices.add(slot);
+            Log.d(TAG, "Allocator freed a connection, name: %s, slot: %d", mServiceClassName, slot);
+        }
+
+        if (mPendingAllocations.isEmpty()) return;
+        mPendingAllocations.remove().run();
+        assert mFreeConnectionIndices.isEmpty();
+        if (!mPendingAllocations.isEmpty() && mFreeSlotCallback != null) mFreeSlotCallback.run();
+    }
+
+    // Can only be called once all slots are full, ie when allocate returns null.
+    // The callback will be called when a slot becomes free, and should synchronous call
+    // allocate to take the slot.
+    public void queueAllocation(Runnable runnable) {
+        assert mFreeConnectionIndices.isEmpty();
+        boolean wasEmpty = mPendingAllocations.isEmpty();
+        mPendingAllocations.add(runnable);
+        if (wasEmpty && mFreeSlotCallback != null) mFreeSlotCallback.run();
+    }
+
+    public String getPackageName() {
+        return mPackageName;
+    }
+
+    public boolean anyConnectionAllocated() {
+        return mFreeConnectionIndices.size() < mChildProcessConnections.length;
+    }
+
+    public boolean isFreeConnectionAvailable() {
+        assert isRunningOnLauncherThread();
+        return !mFreeConnectionIndices.isEmpty();
+    }
+
+    public int getNumberOfServices() {
+        return mChildProcessConnections.length;
+    }
+
+    public boolean isConnectionFromAllocator(ChildProcessConnection connection) {
+        for (ChildProcessConnection existingConnection : mChildProcessConnections) {
+            if (existingConnection == connection) return true;
+        }
+        return false;
+    }
+
+    @VisibleForTesting
+    public void setConnectionFactoryForTesting(ConnectionFactory connectionFactory) {
+        mConnectionFactory = connectionFactory;
+    }
+
+    /** @return the count of connections managed by the allocator */
+    @VisibleForTesting
+    public int allocatedConnectionsCountForTesting() {
+        assert isRunningOnLauncherThread();
+        return mChildProcessConnections.length - mFreeConnectionIndices.size();
+    }
+
+    @VisibleForTesting
+    public ChildProcessConnection getChildProcessConnectionAtSlotForTesting(int slotNumber) {
+        return mChildProcessConnections[slotNumber];
+    }
+
+    private boolean isRunningOnLauncherThread() {
+        return mLauncherHandler.getLooper() == Looper.myLooper();
+    }
+}
diff --git a/base/android/java/src/org/chromium/base/process_launcher/ChildProcessConnection.java b/base/android/java/src/org/chromium/base/process_launcher/ChildProcessConnection.java
new file mode 100644
index 0000000..bfa5d5c
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/process_launcher/ChildProcessConnection.java
@@ -0,0 +1,766 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base.process_launcher;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+
+import org.chromium.base.ChildBindingState;
+import org.chromium.base.Log;
+import org.chromium.base.MemoryPressureLevel;
+import org.chromium.base.MemoryPressureListener;
+import org.chromium.base.ThreadUtils;
+import org.chromium.base.TraceEvent;
+import org.chromium.base.VisibleForTesting;
+import org.chromium.base.memory.MemoryPressureCallback;
+
+import java.util.Arrays;
+import java.util.List;
+
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.GuardedBy;
+
+/**
+ * Manages a connection between the browser activity and a child service.
+ */
+public class ChildProcessConnection {
+    private static final String TAG = "ChildProcessConn";
+    private static final int NUM_BINDING_STATES = ChildBindingState.MAX_VALUE + 1;
+
+    /**
+     * Used to notify the consumer about the process start. These callbacks will be invoked before
+     * the ConnectionCallbacks.
+     */
+    public interface ServiceCallback {
+        /**
+         * Called when the child process has successfully started and is ready for connection
+         * setup.
+         */
+        void onChildStarted();
+
+        /**
+         * Called when the child process failed to start. This can happen if the process is already
+         * in use by another client. The client will not receive any other callbacks after this one.
+         */
+        void onChildStartFailed(ChildProcessConnection connection);
+
+        /**
+         * Called when the service has been disconnected. whether it was stopped by the client or
+         * if it stopped unexpectedly (process crash).
+         * This is the last callback from this interface that a client will receive for a specific
+         * connection.
+         */
+        void onChildProcessDied(ChildProcessConnection connection);
+    }
+
+    /**
+     * Used to notify the consumer about the connection being established.
+     */
+    public interface ConnectionCallback {
+        /**
+         * Called when the connection to the service is established.
+         * @param connection the connection object to the child process
+         */
+        void onConnected(ChildProcessConnection connection);
+    }
+
+    /**
+     * Delegate that ChildServiceConnection should call when the service connects/disconnects.
+     * These callbacks are expected to happen on a background thread.
+     */
+    @VisibleForTesting
+    protected interface ChildServiceConnectionDelegate {
+        void onServiceConnected(IBinder service);
+        void onServiceDisconnected();
+    }
+
+    @VisibleForTesting
+    protected interface ChildServiceConnectionFactory {
+        ChildServiceConnection createConnection(
+                Intent bindIntent, int bindFlags, ChildServiceConnectionDelegate delegate);
+    }
+
+    /** Interface representing a connection to the Android service. Can be mocked in unit-tests. */
+    @VisibleForTesting
+    protected interface ChildServiceConnection {
+        boolean bind();
+        void unbind();
+        boolean isBound();
+    }
+
+    /** Implementation of ChildServiceConnection that does connect to a service. */
+    private static class ChildServiceConnectionImpl
+            implements ChildServiceConnection, ServiceConnection {
+        private final Context mContext;
+        private final Intent mBindIntent;
+        private final int mBindFlags;
+        private final ChildServiceConnectionDelegate mDelegate;
+        private boolean mBound;
+
+        private ChildServiceConnectionImpl(Context context, Intent bindIntent, int bindFlags,
+                ChildServiceConnectionDelegate delegate) {
+            mContext = context;
+            mBindIntent = bindIntent;
+            mBindFlags = bindFlags;
+            mDelegate = delegate;
+        }
+
+        @Override
+        public boolean bind() {
+            if (!mBound) {
+                try {
+                    TraceEvent.begin("ChildProcessConnection.ChildServiceConnectionImpl.bind");
+                    mBound = mContext.bindService(mBindIntent, this, mBindFlags);
+                } finally {
+                    TraceEvent.end("ChildProcessConnection.ChildServiceConnectionImpl.bind");
+                }
+            }
+            return mBound;
+        }
+
+        @Override
+        public void unbind() {
+            if (mBound) {
+                mContext.unbindService(this);
+                mBound = false;
+            }
+        }
+
+        @Override
+        public boolean isBound() {
+            return mBound;
+        }
+
+        @Override
+        public void onServiceConnected(ComponentName className, final IBinder service) {
+            mDelegate.onServiceConnected(service);
+        }
+
+        // Called on the main thread to notify that the child service did not disconnect gracefully.
+        @Override
+        public void onServiceDisconnected(ComponentName className) {
+            mDelegate.onServiceDisconnected();
+        }
+    }
+
+    // Synchronize on this for access.
+    @GuardedBy("sAllBindingStateCounts")
+    private static final int[] sAllBindingStateCounts = new int[NUM_BINDING_STATES];
+
+    @VisibleForTesting
+    static void resetBindingStateCountsForTesting() {
+        synchronized (sAllBindingStateCounts) {
+            for (int i = 0; i < NUM_BINDING_STATES; ++i) {
+                sAllBindingStateCounts[i] = 0;
+            }
+        }
+    }
+
+    private final Handler mLauncherHandler;
+    private final ComponentName mServiceName;
+
+    // Parameters passed to the child process through the service binding intent.
+    // If the service gets recreated by the framework the intent will be reused, so these parameters
+    // should be common to all processes of that type.
+    private final Bundle mServiceBundle;
+
+    // Whether bindToCaller should be called on the service after setup to check that only one
+    // process is bound to the service.
+    private final boolean mBindToCaller;
+
+    private static class ConnectionParams {
+        final Bundle mConnectionBundle;
+        final List<IBinder> mClientInterfaces;
+
+        ConnectionParams(Bundle connectionBundle, List<IBinder> clientInterfaces) {
+            mConnectionBundle = connectionBundle;
+            mClientInterfaces = clientInterfaces;
+        }
+    }
+
+    // This is set in start() and is used in onServiceConnected().
+    private ServiceCallback mServiceCallback;
+
+    // This is set in setupConnection() and is later used in doConnectionSetup(), after which the
+    // variable is cleared. Therefore this is only valid while the connection is being set up.
+    private ConnectionParams mConnectionParams;
+
+    // Callback provided in setupConnection() that will communicate the result to the caller. This
+    // has to be called exactly once after setupConnection(), even if setup fails, so that the
+    // caller can free up resources associated with the setup attempt. This is set to null after the
+    // call.
+    private ConnectionCallback mConnectionCallback;
+
+    private IChildProcessService mService;
+
+    // Set to true when the service connection callback runs. This differs from
+    // mServiceConnectComplete, which tracks that the connection completed successfully.
+    private boolean mDidOnServiceConnected;
+
+    // Set to true when the service connected successfully.
+    private boolean mServiceConnectComplete;
+
+    // Set to true when the service disconnects, as opposed to being properly closed. This happens
+    // when the process crashes or gets killed by the system out-of-memory killer.
+    private boolean mServiceDisconnected;
+
+    // Process ID of the corresponding child process.
+    private int mPid;
+
+    // Strong binding will make the service priority equal to the priority of the activity.
+    private final ChildServiceConnection mStrongBinding;
+
+    // Moderate binding will make the service priority equal to the priority of a visible process
+    // while the app is in the foreground.
+    // This is also used as the initial binding before any priorities are set.
+    private final ChildServiceConnection mModerateBinding;
+
+    // Low priority binding maintained in the entire lifetime of the connection, i.e. between calls
+    // to start() and stop().
+    private final ChildServiceConnection mWaivedBinding;
+
+    // Refcount of bindings.
+    private int mStrongBindingCount;
+    private int mModerateBindingCount;
+
+    // Set to true once unbind() was called.
+    private boolean mUnbound;
+
+    // Binding state of this connection.
+    private @ChildBindingState int mBindingState;
+
+    // Protects access to instance variables that are also accessed on the client thread.
+    private final Object mClientThreadLock = new Object();
+
+    // Same as above except it no longer updates after |unbind()|.
+    @GuardedBy("mClientThreadLock")
+    private @ChildBindingState int mBindingStateCurrentOrWhenDied;
+
+    // Indicate |kill()| was called to intentionally kill this process.
+    @GuardedBy("mClientThreadLock")
+    private boolean mKilledByUs;
+
+    // Copy of |sAllBindingStateCounts| at the time this is unbound.
+    @GuardedBy("mClientThreadLock")
+    private int[] mAllBindingStateCountsWhenDied;
+
+    private MemoryPressureCallback mMemoryPressureCallback;
+
+    public ChildProcessConnection(Context context, ComponentName serviceName, boolean bindToCaller,
+            boolean bindAsExternalService, Bundle serviceBundle) {
+        this(context, serviceName, bindToCaller, bindAsExternalService, serviceBundle,
+                null /* connectionFactory */);
+    }
+
+    @VisibleForTesting
+    public ChildProcessConnection(final Context context, ComponentName serviceName,
+            boolean bindToCaller, boolean bindAsExternalService, Bundle serviceBundle,
+            ChildServiceConnectionFactory connectionFactory) {
+        mLauncherHandler = new Handler();
+        assert isRunningOnLauncherThread();
+        mServiceName = serviceName;
+        mServiceBundle = serviceBundle != null ? serviceBundle : new Bundle();
+        mServiceBundle.putBoolean(ChildProcessConstants.EXTRA_BIND_TO_CALLER, bindToCaller);
+        mBindToCaller = bindToCaller;
+
+        if (connectionFactory == null) {
+            connectionFactory = new ChildServiceConnectionFactory() {
+                @Override
+                public ChildServiceConnection createConnection(
+                        Intent bindIntent, int bindFlags, ChildServiceConnectionDelegate delegate) {
+                    return new ChildServiceConnectionImpl(context, bindIntent, bindFlags, delegate);
+                }
+            };
+        }
+
+        ChildServiceConnectionDelegate delegate = new ChildServiceConnectionDelegate() {
+            @Override
+            public void onServiceConnected(final IBinder service) {
+                mLauncherHandler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        onServiceConnectedOnLauncherThread(service);
+                    }
+                });
+            }
+
+            @Override
+            public void onServiceDisconnected() {
+                mLauncherHandler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        onServiceDisconnectedOnLauncherThread();
+                    }
+                });
+            }
+        };
+
+        Intent intent = new Intent();
+        intent.setComponent(serviceName);
+        if (serviceBundle != null) {
+            intent.putExtras(serviceBundle);
+        }
+
+        int defaultFlags = Context.BIND_AUTO_CREATE
+                | (bindAsExternalService ? Context.BIND_EXTERNAL_SERVICE : 0);
+
+        mModerateBinding = connectionFactory.createConnection(intent, defaultFlags, delegate);
+        mStrongBinding = connectionFactory.createConnection(
+                intent, defaultFlags | Context.BIND_IMPORTANT, delegate);
+        mWaivedBinding = connectionFactory.createConnection(
+                intent, defaultFlags | Context.BIND_WAIVE_PRIORITY, delegate);
+    }
+
+    public final IChildProcessService getService() {
+        assert isRunningOnLauncherThread();
+        return mService;
+    }
+
+    public final ComponentName getServiceName() {
+        assert isRunningOnLauncherThread();
+        return mServiceName;
+    }
+
+    public boolean isConnected() {
+        return mService != null;
+    }
+
+    /**
+     * @return the connection pid, or 0 if not yet connected
+     */
+    public int getPid() {
+        assert isRunningOnLauncherThread();
+        return mPid;
+    }
+
+    /**
+     * Starts a connection to an IChildProcessService. This must be followed by a call to
+     * setupConnection() to setup the connection parameters. start() and setupConnection() are
+     * separate to allow to pass whatever parameters are available in start(), and complete the
+     * remainder addStrongBinding while reducing the connection setup latency.
+     * @param useStrongBinding whether a strong binding should be bound by default. If false, an
+     * initial moderate binding is used.
+     * @param serviceCallback (optional) callbacks invoked when the child process starts or fails to
+     * start and when the service stops.
+     */
+    public void start(boolean useStrongBinding, ServiceCallback serviceCallback) {
+        try {
+            TraceEvent.begin("ChildProcessConnection.start");
+            assert isRunningOnLauncherThread();
+            assert mConnectionParams
+                    == null : "setupConnection() called before start() in ChildProcessConnection.";
+
+            mServiceCallback = serviceCallback;
+
+            if (!bind(useStrongBinding)) {
+                Log.e(TAG, "Failed to establish the service connection.");
+                // We have to notify the caller so that they can free-up associated resources.
+                // TODO(ppi): Can we hard-fail here?
+                notifyChildProcessDied();
+            }
+        } finally {
+            TraceEvent.end("ChildProcessConnection.start");
+        }
+    }
+
+    /**
+     * Sets-up the connection after it was started with start().
+     * @param connectionBundle a bundle passed to the service that can be used to pass various
+     *         parameters to the service
+     * @param clientInterfaces optional client specified interfaces that the child can use to
+     *         communicate with the parent process
+     * @param connectionCallback will be called exactly once after the connection is set up or the
+     *                           setup fails
+     */
+    public void setupConnection(Bundle connectionBundle, @Nullable List<IBinder> clientInterfaces,
+            ConnectionCallback connectionCallback) {
+        assert isRunningOnLauncherThread();
+        assert mConnectionParams == null;
+        if (mServiceDisconnected) {
+            Log.w(TAG, "Tried to setup a connection that already disconnected.");
+            connectionCallback.onConnected(null);
+            return;
+        }
+        try {
+            TraceEvent.begin("ChildProcessConnection.setupConnection");
+            mConnectionCallback = connectionCallback;
+            mConnectionParams = new ConnectionParams(connectionBundle, clientInterfaces);
+            // Run the setup if the service is already connected. If not, doConnectionSetup() will
+            // be called from onServiceConnected().
+            if (mServiceConnectComplete) {
+                doConnectionSetup();
+            }
+        } finally {
+            TraceEvent.end("ChildProcessConnection.setupConnection");
+        }
+    }
+
+    /**
+     * Terminates the connection to IChildProcessService, closing all bindings. It is safe to call
+     * this multiple times.
+     */
+    public void stop() {
+        assert isRunningOnLauncherThread();
+        unbind();
+        notifyChildProcessDied();
+    }
+
+    public void kill() {
+        assert isRunningOnLauncherThread();
+        IChildProcessService service = mService;
+        unbind();
+        try {
+            if (service != null) service.forceKill();
+        } catch (RemoteException e) {
+            // Intentionally ignore since we are killing it anyway.
+        }
+        synchronized (mClientThreadLock) {
+            mKilledByUs = true;
+        }
+        notifyChildProcessDied();
+    }
+
+    private void onServiceConnectedOnLauncherThread(IBinder service) {
+        assert isRunningOnLauncherThread();
+        // A flag from the parent class ensures we run the post-connection logic only once
+        // (instead of once per each ChildServiceConnection).
+        if (mDidOnServiceConnected) {
+            return;
+        }
+        try {
+            TraceEvent.begin("ChildProcessConnection.ChildServiceConnection.onServiceConnected");
+            mDidOnServiceConnected = true;
+            mService = IChildProcessService.Stub.asInterface(service);
+
+            if (mBindToCaller) {
+                try {
+                    if (!mService.bindToCaller()) {
+                        if (mServiceCallback != null) {
+                            mServiceCallback.onChildStartFailed(this);
+                        }
+                        unbind();
+                        return;
+                    }
+                } catch (RemoteException ex) {
+                    // Do not trigger the StartCallback here, since the service is already
+                    // dead and the onChildStopped callback will run from onServiceDisconnected().
+                    Log.e(TAG, "Failed to bind service to connection.", ex);
+                    return;
+                }
+            }
+
+            if (mServiceCallback != null) {
+                mServiceCallback.onChildStarted();
+            }
+
+            mServiceConnectComplete = true;
+
+            if (mMemoryPressureCallback == null) {
+                final MemoryPressureCallback callback = this ::onMemoryPressure;
+                ThreadUtils.postOnUiThread(() -> MemoryPressureListener.addCallback(callback));
+                mMemoryPressureCallback = callback;
+            }
+
+            // Run the setup if the connection parameters have already been provided. If
+            // not, doConnectionSetup() will be called from setupConnection().
+            if (mConnectionParams != null) {
+                doConnectionSetup();
+            }
+        } finally {
+            TraceEvent.end("ChildProcessConnection.ChildServiceConnection.onServiceConnected");
+        }
+    }
+
+    private void onServiceDisconnectedOnLauncherThread() {
+        assert isRunningOnLauncherThread();
+        // Ensure that the disconnection logic runs only once (instead of once per each
+        // ChildServiceConnection).
+        if (mServiceDisconnected) {
+            return;
+        }
+        mServiceDisconnected = true;
+        Log.w(TAG, "onServiceDisconnected (crash or killed by oom): pid=%d", mPid);
+        stop(); // We don't want to auto-restart on crash. Let the browser do that.
+
+        // If we have a pending connection callback, we need to communicate the failure to
+        // the caller.
+        if (mConnectionCallback != null) {
+            mConnectionCallback.onConnected(null);
+            mConnectionCallback = null;
+        }
+    }
+
+    private void onSetupConnectionResult(int pid) {
+        mPid = pid;
+        assert mPid != 0 : "Child service claims to be run by a process of pid=0.";
+
+        if (mConnectionCallback != null) {
+            mConnectionCallback.onConnected(this);
+        }
+        mConnectionCallback = null;
+    }
+
+    /**
+     * Called after the connection parameters have been set (in setupConnection()) *and* a
+     * connection has been established (as signaled by onServiceConnected()). These two events can
+     * happen in any order.
+     */
+    private void doConnectionSetup() {
+        try {
+            TraceEvent.begin("ChildProcessConnection.doConnectionSetup");
+            assert mServiceConnectComplete && mService != null;
+            assert mConnectionParams != null;
+
+            ICallbackInt pidCallback = new ICallbackInt.Stub() {
+                @Override
+                public void call(final int pid) {
+                    mLauncherHandler.post(new Runnable() {
+                        @Override
+                        public void run() {
+                            onSetupConnectionResult(pid);
+                        }
+                    });
+                }
+            };
+            try {
+                mService.setupConnection(mConnectionParams.mConnectionBundle, pidCallback,
+                        mConnectionParams.mClientInterfaces);
+            } catch (RemoteException re) {
+                Log.e(TAG, "Failed to setup connection.", re);
+            }
+            mConnectionParams = null;
+        } finally {
+            TraceEvent.end("ChildProcessConnection.doConnectionSetup");
+        }
+    }
+
+    private boolean bind(boolean useStrongBinding) {
+        assert isRunningOnLauncherThread();
+        assert !mUnbound;
+
+        boolean success;
+        if (useStrongBinding) {
+            success = mStrongBinding.bind();
+        } else {
+            mModerateBindingCount++;
+            success = mModerateBinding.bind();
+        }
+        if (!success) return false;
+
+        mWaivedBinding.bind();
+        updateBindingState();
+        return true;
+    }
+
+    @VisibleForTesting
+    protected void unbind() {
+        assert isRunningOnLauncherThread();
+        mService = null;
+        mConnectionParams = null;
+        mUnbound = true;
+        mStrongBinding.unbind();
+        mWaivedBinding.unbind();
+        mModerateBinding.unbind();
+        updateBindingState();
+
+        int[] bindingStateCounts;
+        synchronized (sAllBindingStateCounts) {
+            bindingStateCounts = Arrays.copyOf(sAllBindingStateCounts, NUM_BINDING_STATES);
+        }
+        synchronized (mClientThreadLock) {
+            mAllBindingStateCountsWhenDied = bindingStateCounts;
+        }
+
+        if (mMemoryPressureCallback != null) {
+            final MemoryPressureCallback callback = mMemoryPressureCallback;
+            ThreadUtils.postOnUiThread(() -> MemoryPressureListener.removeCallback(callback));
+            mMemoryPressureCallback = null;
+        }
+    }
+
+    public boolean isStrongBindingBound() {
+        assert isRunningOnLauncherThread();
+        return mStrongBinding.isBound();
+    }
+
+    public void addStrongBinding() {
+        assert isRunningOnLauncherThread();
+        if (!isConnected()) {
+            Log.w(TAG, "The connection is not bound for %d", getPid());
+            return;
+        }
+        if (mStrongBindingCount == 0) {
+            mStrongBinding.bind();
+            updateBindingState();
+        }
+        mStrongBindingCount++;
+    }
+
+    public void removeStrongBinding() {
+        assert isRunningOnLauncherThread();
+        if (!isConnected()) {
+            Log.w(TAG, "The connection is not bound for %d", getPid());
+            return;
+        }
+        assert mStrongBindingCount > 0;
+        mStrongBindingCount--;
+        if (mStrongBindingCount == 0) {
+            mStrongBinding.unbind();
+            updateBindingState();
+        }
+    }
+
+    public boolean isModerateBindingBound() {
+        assert isRunningOnLauncherThread();
+        return mModerateBinding.isBound();
+    }
+
+    public void addModerateBinding() {
+        assert isRunningOnLauncherThread();
+        if (!isConnected()) {
+            Log.w(TAG, "The connection is not bound for %d", getPid());
+            return;
+        }
+        if (mModerateBindingCount == 0) {
+            mModerateBinding.bind();
+            updateBindingState();
+        }
+        mModerateBindingCount++;
+    }
+
+    public void removeModerateBinding() {
+        assert isRunningOnLauncherThread();
+        if (!isConnected()) {
+            Log.w(TAG, "The connection is not bound for %d", getPid());
+            return;
+        }
+        assert mModerateBindingCount > 0;
+        mModerateBindingCount--;
+        if (mModerateBindingCount == 0) {
+            mModerateBinding.unbind();
+            updateBindingState();
+        }
+    }
+
+    /**
+     * @return true if the connection is bound and only bound with the waived binding or if the
+     * connection is unbound and was only bound with the waived binding when it disconnected.
+     */
+    public @ChildBindingState int bindingStateCurrentOrWhenDied() {
+        // WARNING: this method can be called from a thread other than the launcher thread.
+        // Note that it returns the current waived bound only state and is racy. This not really
+        // preventable without changing the caller's API, short of blocking.
+        synchronized (mClientThreadLock) {
+            return mBindingStateCurrentOrWhenDied;
+        }
+    }
+
+    /**
+     * @return true if the connection is intentionally killed by calling kill().
+     */
+    public boolean isKilledByUs() {
+        // WARNING: this method can be called from a thread other than the launcher thread.
+        // Note that it returns the current waived bound only state and is racy. This not really
+        // preventable without changing the caller's API, short of blocking.
+        synchronized (mClientThreadLock) {
+            return mKilledByUs;
+        }
+    }
+
+    public int[] bindingStateCountsCurrentOrWhenDied() {
+        // WARNING: this method can be called from a thread other than the launcher thread.
+        // Note that it returns the current waived bound only state and is racy. This not really
+        // preventable without changing the caller's API, short of blocking.
+        synchronized (mClientThreadLock) {
+            if (mAllBindingStateCountsWhenDied != null) {
+                return Arrays.copyOf(mAllBindingStateCountsWhenDied, NUM_BINDING_STATES);
+            }
+        }
+        synchronized (sAllBindingStateCounts) {
+            return Arrays.copyOf(sAllBindingStateCounts, NUM_BINDING_STATES);
+        }
+    }
+
+    // Should be called any binding is bound or unbound.
+    private void updateBindingState() {
+        int oldBindingState = mBindingState;
+        if (mUnbound) {
+            mBindingState = ChildBindingState.UNBOUND;
+        } else if (mStrongBinding.isBound()) {
+            mBindingState = ChildBindingState.STRONG;
+        } else if (mModerateBinding.isBound()) {
+            mBindingState = ChildBindingState.MODERATE;
+        } else {
+            assert mWaivedBinding.isBound();
+            mBindingState = ChildBindingState.WAIVED;
+        }
+
+        if (mBindingState != oldBindingState) {
+            synchronized (sAllBindingStateCounts) {
+                if (oldBindingState != ChildBindingState.UNBOUND) {
+                    assert sAllBindingStateCounts[oldBindingState] > 0;
+                    sAllBindingStateCounts[oldBindingState]--;
+                }
+                if (mBindingState != ChildBindingState.UNBOUND) {
+                    sAllBindingStateCounts[mBindingState]++;
+                }
+            }
+        }
+
+        if (!mUnbound) {
+            synchronized (mClientThreadLock) {
+                mBindingStateCurrentOrWhenDied = mBindingState;
+            }
+        }
+    }
+
+    private void notifyChildProcessDied() {
+        if (mServiceCallback != null) {
+            // Guard against nested calls to this method.
+            ServiceCallback serviceCallback = mServiceCallback;
+            mServiceCallback = null;
+            serviceCallback.onChildProcessDied(this);
+        }
+    }
+
+    private boolean isRunningOnLauncherThread() {
+        return mLauncherHandler.getLooper() == Looper.myLooper();
+    }
+
+    @VisibleForTesting
+    public void crashServiceForTesting() throws RemoteException {
+        mService.forceKill();
+    }
+
+    @VisibleForTesting
+    public boolean didOnServiceConnectedForTesting() {
+        return mDidOnServiceConnected;
+    }
+
+    @VisibleForTesting
+    protected Handler getLauncherHandler() {
+        return mLauncherHandler;
+    }
+
+    private void onMemoryPressure(@MemoryPressureLevel int pressure) {
+        mLauncherHandler.post(() -> onMemoryPressureOnLauncherThread(pressure));
+    }
+
+    private void onMemoryPressureOnLauncherThread(@MemoryPressureLevel int pressure) {
+        if (mService == null) return;
+        try {
+            mService.onMemoryPressure(pressure);
+        } catch (RemoteException ex) {
+            // Ignore
+        }
+    }
+}
diff --git a/base/android/java/src/org/chromium/base/process_launcher/ChildProcessConstants.java b/base/android/java/src/org/chromium/base/process_launcher/ChildProcessConstants.java
new file mode 100644
index 0000000..ec232d7
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/process_launcher/ChildProcessConstants.java
@@ -0,0 +1,27 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base.process_launcher;
+
+/**
+ * Constants to be used by child processes.
+ */
+public interface ChildProcessConstants {
+    // Below are the names for the items placed in the bind or start command intent.
+    // Note that because that intent maybe reused if a service is restarted, none should be process
+    // specific.
+
+    public static final String EXTRA_BIND_TO_CALLER =
+            "org.chromium.base.process_launcher.extra.bind_to_caller";
+
+    // Below are the names for the items placed in the Bundle passed in the
+    // IChildProcessService.setupConnection call, once the connection has been established.
+
+    // Key for the command line.
+    public static final String EXTRA_COMMAND_LINE =
+            "org.chromium.base.process_launcher.extra.command_line";
+
+    // Key for the file descriptors that should be mapped in the child process.
+    public static final String EXTRA_FILES = "org.chromium.base.process_launcher.extra.extraFiles";
+}
diff --git a/base/android/java/src/org/chromium/base/process_launcher/ChildProcessLauncher.java b/base/android/java/src/org/chromium/base/process_launcher/ChildProcessLauncher.java
new file mode 100644
index 0000000..7cdc852
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/process_launcher/ChildProcessLauncher.java
@@ -0,0 +1,278 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base.process_launcher;
+
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+
+import org.chromium.base.ContextUtils;
+import org.chromium.base.Log;
+import org.chromium.base.TraceEvent;
+
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * This class is used to start a child process by connecting to a ChildProcessService.
+ */
+public class ChildProcessLauncher {
+    private static final String TAG = "ChildProcLauncher";
+
+    /** Delegate that client should use to customize the process launching. */
+    public abstract static class Delegate {
+        /**
+         * Called when the launcher is about to start. Gives the embedder a chance to provide an
+         * already bound connection if it has one. (allowing for warm-up connections: connections
+         * that are already bound in advance to speed up child process start-up time).
+         * Note that onBeforeConnectionAllocated will not be called if this method returns a
+         * connection.
+         * @param connectionAllocator the allocator the returned connection should have been
+         * allocated of.
+         * @param serviceCallback the service callback that the connection should use.
+         * @return a bound connection to use to connect to the child process service, or null if a
+         * connection should be allocated and bound by the launcher.
+         */
+        public ChildProcessConnection getBoundConnection(
+                ChildConnectionAllocator connectionAllocator,
+                ChildProcessConnection.ServiceCallback serviceCallback) {
+            return null;
+        }
+
+        /**
+         * Called before a connection is allocated.
+         * Note that this is only called if the ChildProcessLauncher is created with
+         * {@link #createWithConnectionAllocator}.
+         * @param serviceBundle the bundle passed in the service intent. Clients can add their own
+         * extras to the bundle.
+         */
+        public void onBeforeConnectionAllocated(Bundle serviceBundle) {}
+
+        /**
+         * Called before setup is called on the connection.
+         * @param connectionBundle the bundle passed to the {@link ChildProcessService} in the
+         * setup call. Clients can add their own extras to the bundle.
+         */
+        public void onBeforeConnectionSetup(Bundle connectionBundle) {}
+
+        /**
+         * Called when the connection was successfully established, meaning the setup call on the
+         * service was successful.
+         * @param connection the connection over which the setup call was made.
+         */
+        public void onConnectionEstablished(ChildProcessConnection connection) {}
+
+        /**
+         * Called when a connection has been disconnected. Only invoked if onConnectionEstablished
+         * was called, meaning the connection was already established.
+         * @param connection the connection that got disconnected.
+         */
+        public void onConnectionLost(ChildProcessConnection connection) {}
+    }
+
+    // Represents an invalid process handle; same as base/process/process.h kNullProcessHandle.
+    private static final int NULL_PROCESS_HANDLE = 0;
+
+    // The handle for the thread we were created on and on which all methods should be called.
+    private final Handler mLauncherHandler;
+
+    private final Delegate mDelegate;
+
+    private final String[] mCommandLine;
+    private final FileDescriptorInfo[] mFilesToBeMapped;
+
+    // The allocator used to create the connection.
+    private final ChildConnectionAllocator mConnectionAllocator;
+
+    // The IBinder interfaces provided to the created service.
+    private final List<IBinder> mClientInterfaces;
+
+    // The actual service connection. Set once we have connected to the service.
+    private ChildProcessConnection mConnection;
+
+    /**
+     * Constructor.
+     *
+     * @param launcherHandler the handler for the thread where all operations should happen.
+     * @param delegate the delagate that gets notified of the launch progress.
+     * @param commandLine the command line that should be passed to the started process.
+     * @param filesToBeMapped the files that should be passed to the started process.
+     * @param connectionAllocator the allocator used to create connections to the service.
+     * @param clientInterfaces the interfaces that should be passed to the started process so it can
+     * communicate with the parent process.
+     */
+    public ChildProcessLauncher(Handler launcherHandler, Delegate delegate, String[] commandLine,
+            FileDescriptorInfo[] filesToBeMapped, ChildConnectionAllocator connectionAllocator,
+            List<IBinder> clientInterfaces) {
+        assert connectionAllocator != null;
+        mLauncherHandler = launcherHandler;
+        isRunningOnLauncherThread();
+        mCommandLine = commandLine;
+        mConnectionAllocator = connectionAllocator;
+        mDelegate = delegate;
+        mFilesToBeMapped = filesToBeMapped;
+        mClientInterfaces = clientInterfaces;
+    }
+
+    /**
+     * Starts the child process and calls setup on it if {@param setupConnection} is true.
+     * @param setupConnection whether the setup should be performed on the connection once
+     * established
+     * @param queueIfNoFreeConnection whether to queue that request if no service connection is
+     * available. If the launcher was created with a connection provider, this parameter has no
+     * effect.
+     * @return true if the connection was started or was queued.
+     */
+    public boolean start(final boolean setupConnection, final boolean queueIfNoFreeConnection) {
+        assert isRunningOnLauncherThread();
+        try {
+            TraceEvent.begin("ChildProcessLauncher.start");
+            ChildProcessConnection.ServiceCallback serviceCallback =
+                    new ChildProcessConnection.ServiceCallback() {
+                        @Override
+                        public void onChildStarted() {}
+
+                        @Override
+                        public void onChildStartFailed(ChildProcessConnection connection) {
+                            assert isRunningOnLauncherThread();
+                            assert mConnection == connection;
+                            Log.e(TAG, "ChildProcessConnection.start failed, trying again");
+                            mLauncherHandler.post(new Runnable() {
+                                @Override
+                                public void run() {
+                                    // The child process may already be bound to another client
+                                    // (this can happen if multi-process WebView is used in more
+                                    // than one process), so try starting the process again.
+                                    // This connection that failed to start has not been freed,
+                                    // so a new bound connection will be allocated.
+                                    mConnection = null;
+                                    start(setupConnection, queueIfNoFreeConnection);
+                                }
+                            });
+                        }
+
+                        @Override
+                        public void onChildProcessDied(ChildProcessConnection connection) {
+                            assert isRunningOnLauncherThread();
+                            assert mConnection == connection;
+                            ChildProcessLauncher.this.onChildProcessDied();
+                        }
+                    };
+            mConnection = mDelegate.getBoundConnection(mConnectionAllocator, serviceCallback);
+            if (mConnection != null) {
+                assert mConnectionAllocator.isConnectionFromAllocator(mConnection);
+                setupConnection();
+                return true;
+            }
+            if (!allocateAndSetupConnection(
+                        serviceCallback, setupConnection, queueIfNoFreeConnection)
+                    && !queueIfNoFreeConnection) {
+                return false;
+            }
+            return true;
+        } finally {
+            TraceEvent.end("ChildProcessLauncher.start");
+        }
+    }
+
+    public ChildProcessConnection getConnection() {
+        return mConnection;
+    }
+
+    public ChildConnectionAllocator getConnectionAllocator() {
+        return mConnectionAllocator;
+    }
+
+    private boolean allocateAndSetupConnection(
+            final ChildProcessConnection.ServiceCallback serviceCallback,
+            final boolean setupConnection, final boolean queueIfNoFreeConnection) {
+        assert mConnection == null;
+        Bundle serviceBundle = new Bundle();
+        mDelegate.onBeforeConnectionAllocated(serviceBundle);
+
+        mConnection = mConnectionAllocator.allocate(
+                ContextUtils.getApplicationContext(), serviceBundle, serviceCallback);
+        if (mConnection == null) {
+            if (!queueIfNoFreeConnection) {
+                Log.d(TAG, "Failed to allocate a child connection (no queuing).");
+                return false;
+            }
+            mConnectionAllocator.queueAllocation(
+                    () -> allocateAndSetupConnection(
+                                    serviceCallback, setupConnection, queueIfNoFreeConnection));
+            return false;
+        }
+
+        if (setupConnection) {
+            setupConnection();
+        }
+        return true;
+    }
+
+    private void setupConnection() {
+        ChildProcessConnection.ConnectionCallback connectionCallback =
+                new ChildProcessConnection.ConnectionCallback() {
+                    @Override
+                    public void onConnected(ChildProcessConnection connection) {
+                        assert mConnection == connection;
+                        onServiceConnected();
+                    }
+                };
+        Bundle connectionBundle = createConnectionBundle();
+        mDelegate.onBeforeConnectionSetup(connectionBundle);
+        mConnection.setupConnection(connectionBundle, getClientInterfaces(), connectionCallback);
+    }
+
+    private void onServiceConnected() {
+        assert isRunningOnLauncherThread();
+
+        Log.d(TAG, "on connect callback, pid=%d", mConnection.getPid());
+
+        mDelegate.onConnectionEstablished(mConnection);
+
+        // Proactively close the FDs rather than waiting for the GC to do it.
+        try {
+            for (FileDescriptorInfo fileInfo : mFilesToBeMapped) {
+                fileInfo.fd.close();
+            }
+        } catch (IOException ioe) {
+            Log.w(TAG, "Failed to close FD.", ioe);
+        }
+    }
+
+    public int getPid() {
+        assert isRunningOnLauncherThread();
+        return mConnection == null ? NULL_PROCESS_HANDLE : mConnection.getPid();
+    }
+
+    public List<IBinder> getClientInterfaces() {
+        return mClientInterfaces;
+    }
+
+    private boolean isRunningOnLauncherThread() {
+        return mLauncherHandler.getLooper() == Looper.myLooper();
+    }
+
+    private Bundle createConnectionBundle() {
+        Bundle bundle = new Bundle();
+        bundle.putStringArray(ChildProcessConstants.EXTRA_COMMAND_LINE, mCommandLine);
+        bundle.putParcelableArray(ChildProcessConstants.EXTRA_FILES, mFilesToBeMapped);
+        return bundle;
+    }
+
+    private void onChildProcessDied() {
+        assert isRunningOnLauncherThread();
+        if (getPid() != 0) {
+            mDelegate.onConnectionLost(mConnection);
+        }
+    }
+
+    public void stop() {
+        assert isRunningOnLauncherThread();
+        Log.d(TAG, "stopping child connection: pid=%d", mConnection.getPid());
+        mConnection.stop();
+    }
+}
diff --git a/base/android/java/src/org/chromium/base/process_launcher/ChildProcessService.java b/base/android/java/src/org/chromium/base/process_launcher/ChildProcessService.java
new file mode 100644
index 0000000..876ebbb
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/process_launcher/ChildProcessService.java
@@ -0,0 +1,346 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base.process_launcher;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Parcelable;
+import android.os.Process;
+import android.os.RemoteException;
+import android.util.SparseArray;
+
+import org.chromium.base.BaseSwitches;
+import org.chromium.base.CommandLine;
+import org.chromium.base.ContextUtils;
+import org.chromium.base.Log;
+import org.chromium.base.MemoryPressureLevel;
+import org.chromium.base.ThreadUtils;
+import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.MainDex;
+import org.chromium.base.memory.MemoryPressureMonitor;
+
+import java.util.List;
+import java.util.concurrent.Semaphore;
+
+import javax.annotation.concurrent.GuardedBy;
+
+/**
+ * This is the base class for child services; the embedding application should contain
+ * ProcessService0, 1.. etc subclasses that provide the concrete service entry points, so it can
+ * connect to more than one distinct process (i.e. one process per service number, up to limit of
+ * N).
+ * The embedding application must declare these service instances in the application section
+ * of its AndroidManifest.xml, first with some meta-data describing the services:
+ *     <meta-data android:name="org.chromium.test_app.SERVICES_NAME"
+ *           android:value="org.chromium.test_app.ProcessService"/>
+ * and then N entries of the form:
+ *     <service android:name="org.chromium.test_app.ProcessServiceX"
+ *              android:process=":processX" />
+ *
+ * Subclasses must also provide a delegate in this class constructor. That delegate is responsible
+ * for loading native libraries and running the main entry point of the service.
+ */
+@JNINamespace("base::android")
+@MainDex
+public abstract class ChildProcessService extends Service {
+    private static final String MAIN_THREAD_NAME = "ChildProcessMain";
+    private static final String TAG = "ChildProcessService";
+
+    // Only for a check that create is only called once.
+    private static boolean sCreateCalled;
+
+    private final ChildProcessServiceDelegate mDelegate;
+
+    private final Object mBinderLock = new Object();
+    private final Object mLibraryInitializedLock = new Object();
+
+    // True if we should enforce that bindToCaller() is called before setupConnection().
+    // Only set once in bind(), does not require synchronization.
+    private boolean mBindToCallerCheck;
+
+    // PID of the client of this service, set in bindToCaller(), if mBindToCallerCheck is true.
+    @GuardedBy("mBinderLock")
+    private int mBoundCallingPid;
+
+    // This is the native "Main" thread for the renderer / utility process.
+    private Thread mMainThread;
+
+    // Parameters received via IPC, only accessed while holding the mMainThread monitor.
+    private String[] mCommandLineParams;
+
+    // File descriptors that should be registered natively.
+    private FileDescriptorInfo[] mFdInfos;
+
+    @GuardedBy("mLibraryInitializedLock")
+    private boolean mLibraryInitialized;
+
+    // Called once the service is bound and all service related member variables have been set.
+    // Only set once in bind(), does not require synchronization.
+    private boolean mServiceBound;
+
+    private final Semaphore mActivitySemaphore = new Semaphore(1);
+
+    public ChildProcessService(ChildProcessServiceDelegate delegate) {
+        mDelegate = delegate;
+    }
+
+    // Binder object used by clients for this service.
+    private final IChildProcessService.Stub mBinder = new IChildProcessService.Stub() {
+        // NOTE: Implement any IChildProcessService methods here.
+        @Override
+        public boolean bindToCaller() {
+            assert mBindToCallerCheck;
+            assert mServiceBound;
+            synchronized (mBinderLock) {
+                int callingPid = Binder.getCallingPid();
+                if (mBoundCallingPid == 0) {
+                    mBoundCallingPid = callingPid;
+                } else if (mBoundCallingPid != callingPid) {
+                    Log.e(TAG, "Service is already bound by pid %d, cannot bind for pid %d",
+                            mBoundCallingPid, callingPid);
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        @Override
+        public void setupConnection(Bundle args, ICallbackInt pidCallback, List<IBinder> callbacks)
+                throws RemoteException {
+            assert mServiceBound;
+            synchronized (mBinderLock) {
+                if (mBindToCallerCheck && mBoundCallingPid == 0) {
+                    Log.e(TAG, "Service has not been bound with bindToCaller()");
+                    pidCallback.call(-1);
+                    return;
+                }
+            }
+
+            pidCallback.call(Process.myPid());
+            processConnectionBundle(args, callbacks);
+        }
+
+        @Override
+        public void forceKill() {
+            assert mServiceBound;
+            Process.killProcess(Process.myPid());
+        }
+
+        @Override
+        public void onMemoryPressure(@MemoryPressureLevel int pressure) {
+            // This method is called by the host process when the host process reports pressure
+            // to its native side. The key difference between the host process and its services is
+            // that the host process polls memory pressure when it gets CRITICAL, and periodically
+            // invokes pressure listeners until pressure subsides. (See MemoryPressureMonitor for
+            // more info.)
+            //
+            // Services don't poll, so this side-channel is used to notify services about memory
+            // pressure from the host process's POV.
+            //
+            // However, since both host process and services listen to ComponentCallbacks2, we
+            // can't be sure that the host process won't get better signals than their services.
+            // I.e. we need to watch out for a situation where a service gets CRITICAL, but the
+            // host process gets MODERATE - in this case we need to ignore MODERATE.
+            //
+            // So we're ignoring pressure from the host process if it's better than the last
+            // reported pressure. I.e. the host process can drive pressure up, but it'll go
+            // down only when we the service get a signal through ComponentCallbacks2.
+            ThreadUtils.postOnUiThread(() -> {
+                if (pressure >= MemoryPressureMonitor.INSTANCE.getLastReportedPressure()) {
+                    MemoryPressureMonitor.INSTANCE.notifyPressure(pressure);
+                }
+            });
+        }
+    };
+
+    /**
+     * Loads Chrome's native libraries and initializes a ChildProcessService.
+     */
+    // For sCreateCalled check.
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        Log.i(TAG, "Creating new ChildProcessService pid=%d", Process.myPid());
+        if (sCreateCalled) {
+            throw new RuntimeException("Illegal child process reuse.");
+        }
+        sCreateCalled = true;
+
+        // Initialize the context for the application that owns this ChildProcessService object.
+        ContextUtils.initApplicationContext(getApplicationContext());
+
+        mDelegate.onServiceCreated();
+
+        mMainThread = new Thread(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    // CommandLine must be initialized before everything else.
+                    synchronized (mMainThread) {
+                        while (mCommandLineParams == null) {
+                            mMainThread.wait();
+                        }
+                    }
+                    assert mServiceBound;
+                    CommandLine.init(mCommandLineParams);
+
+                    if (CommandLine.getInstance().hasSwitch(
+                                BaseSwitches.RENDERER_WAIT_FOR_JAVA_DEBUGGER)) {
+                        android.os.Debug.waitForDebugger();
+                    }
+
+                    boolean nativeLibraryLoaded = false;
+                    try {
+                        nativeLibraryLoaded = mDelegate.loadNativeLibrary(getApplicationContext());
+                    } catch (Exception e) {
+                        Log.e(TAG, "Failed to load native library.", e);
+                    }
+                    if (!nativeLibraryLoaded) {
+                        System.exit(-1);
+                    }
+
+                    synchronized (mLibraryInitializedLock) {
+                        mLibraryInitialized = true;
+                        mLibraryInitializedLock.notifyAll();
+                    }
+                    synchronized (mMainThread) {
+                        mMainThread.notifyAll();
+                        while (mFdInfos == null) {
+                            mMainThread.wait();
+                        }
+                    }
+
+                    SparseArray<String> idsToKeys = mDelegate.getFileDescriptorsIdsToKeys();
+
+                    int[] fileIds = new int[mFdInfos.length];
+                    String[] keys = new String[mFdInfos.length];
+                    int[] fds = new int[mFdInfos.length];
+                    long[] regionOffsets = new long[mFdInfos.length];
+                    long[] regionSizes = new long[mFdInfos.length];
+                    for (int i = 0; i < mFdInfos.length; i++) {
+                        FileDescriptorInfo fdInfo = mFdInfos[i];
+                        String key = idsToKeys != null ? idsToKeys.get(fdInfo.id) : null;
+                        if (key != null) {
+                            keys[i] = key;
+                        } else {
+                            fileIds[i] = fdInfo.id;
+                        }
+                        fds[i] = fdInfo.fd.detachFd();
+                        regionOffsets[i] = fdInfo.offset;
+                        regionSizes[i] = fdInfo.size;
+                    }
+                    nativeRegisterFileDescriptors(keys, fileIds, fds, regionOffsets, regionSizes);
+
+                    mDelegate.onBeforeMain();
+                    if (mActivitySemaphore.tryAcquire()) {
+                        mDelegate.runMain();
+                        nativeExitChildProcess();
+                    }
+                } catch (InterruptedException e) {
+                    Log.w(TAG, "%s startup failed: %s", MAIN_THREAD_NAME, e);
+                }
+            }
+        }, MAIN_THREAD_NAME);
+        mMainThread.start();
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        Log.i(TAG, "Destroying ChildProcessService pid=%d", Process.myPid());
+        if (mActivitySemaphore.tryAcquire()) {
+            // TODO(crbug.com/457406): This is a bit hacky, but there is no known better solution
+            // as this service will get reused (at least if not sandboxed).
+            // In fact, we might really want to always exit() from onDestroy(), not just from
+            // the early return here.
+            System.exit(0);
+            return;
+        }
+        synchronized (mLibraryInitializedLock) {
+            try {
+                while (!mLibraryInitialized) {
+                    // Avoid a potential race in calling through to native code before the library
+                    // has loaded.
+                    mLibraryInitializedLock.wait();
+                }
+            } catch (InterruptedException e) {
+                // Ignore
+            }
+        }
+        mDelegate.onDestroy();
+    }
+
+    /*
+     * Returns the communication channel to the service. Note that even if multiple clients were to
+     * connect, we should only get one call to this method. So there is no need to synchronize
+     * member variables that are only set in this method and accessed from binder methods, as binder
+     * methods can't be called until this method returns.
+     * @param intent The intent that was used to bind to the service.
+     * @return the binder used by the client to setup the connection.
+     */
+    @Override
+    public IBinder onBind(Intent intent) {
+        assert !mServiceBound;
+
+        // We call stopSelf() to request that this service be stopped as soon as the client unbinds.
+        // Otherwise the system may keep it around and available for a reconnect. The child
+        // processes do not currently support reconnect; they must be initialized from scratch every
+        // time.
+        stopSelf();
+
+        mBindToCallerCheck =
+                intent.getBooleanExtra(ChildProcessConstants.EXTRA_BIND_TO_CALLER, false);
+        mServiceBound = true;
+        mDelegate.onServiceBound(intent);
+        // Don't block bind() with any extra work, post it to the application thread instead.
+        new Handler(Looper.getMainLooper())
+                .post(() -> mDelegate.preloadNativeLibrary(getApplicationContext()));
+        return mBinder;
+    }
+
+    private void processConnectionBundle(Bundle bundle, List<IBinder> clientInterfaces) {
+        // Required to unparcel FileDescriptorInfo.
+        ClassLoader classLoader = getApplicationContext().getClassLoader();
+        bundle.setClassLoader(classLoader);
+        synchronized (mMainThread) {
+            if (mCommandLineParams == null) {
+                mCommandLineParams =
+                        bundle.getStringArray(ChildProcessConstants.EXTRA_COMMAND_LINE);
+                mMainThread.notifyAll();
+            }
+            // We must have received the command line by now
+            assert mCommandLineParams != null;
+            Parcelable[] fdInfosAsParcelable =
+                    bundle.getParcelableArray(ChildProcessConstants.EXTRA_FILES);
+            if (fdInfosAsParcelable != null) {
+                // For why this arraycopy is necessary:
+                // http://stackoverflow.com/questions/8745893/i-dont-get-why-this-classcastexception-occurs
+                mFdInfos = new FileDescriptorInfo[fdInfosAsParcelable.length];
+                System.arraycopy(fdInfosAsParcelable, 0, mFdInfos, 0, fdInfosAsParcelable.length);
+            }
+            mDelegate.onConnectionSetup(bundle, clientInterfaces);
+            mMainThread.notifyAll();
+        }
+    }
+
+    /**
+     * Helper for registering FileDescriptorInfo objects with GlobalFileDescriptors or
+     * FileDescriptorStore.
+     * This includes the IPC channel, the crash dump signals and resource related
+     * files.
+     */
+    private static native void nativeRegisterFileDescriptors(
+            String[] keys, int[] id, int[] fd, long[] offset, long[] size);
+
+    /**
+     * Force the child process to exit.
+     */
+    private static native void nativeExitChildProcess();
+}
diff --git a/base/android/java/src/org/chromium/base/process_launcher/ChildProcessServiceDelegate.java b/base/android/java/src/org/chromium/base/process_launcher/ChildProcessServiceDelegate.java
new file mode 100644
index 0000000..7beffef
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/process_launcher/ChildProcessServiceDelegate.java
@@ -0,0 +1,76 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base.process_launcher;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.util.SparseArray;
+
+import java.util.List;
+
+/**
+ * The interface that embedders should implement to specialize child service creation.
+ */
+public interface ChildProcessServiceDelegate {
+    /** Invoked when the service was created. This is the first method invoked on the delegate. */
+    void onServiceCreated();
+
+    /**
+     * Called when the service is bound. Invoked on a background thread.
+     * @param intent the intent that started the service.
+     */
+    void onServiceBound(Intent intent);
+
+    /**
+     * Called once the connection has been setup. Invoked on a background thread.
+     * @param connectionBundle the bundle pass to the setupConnection call
+     * @param clientInterfaces the IBinders interfaces provided by the client
+     */
+    void onConnectionSetup(Bundle connectionBundle, List<IBinder> clientInterfaces);
+
+    /**
+     * Called when the service gets destroyed.
+     * Note that the system might kill the process hosting the service without this method being
+     * called.
+     */
+    void onDestroy();
+
+    /**
+     * Called when the delegate should load the native library.
+     * @param hostContext The host context the library should be loaded with (i.e. Chrome).
+     * @return true if the library was loaded successfully, false otherwise in which case the
+     * service stops.
+     */
+    boolean loadNativeLibrary(Context hostContext);
+
+    /**
+     * Called when the delegate should preload the native library.
+     * Preloading is automatically done during library loading, but can also be called explicitly
+     * to speed up the loading. See {@link LibraryLoader.preloadNow}.
+     * @param hostContext The host context the library should be preloaded with (i.e. Chrome).
+     */
+    void preloadNativeLibrary(Context hostContext);
+
+    /**
+     * Should return a map that associatesfile descriptors' IDs to keys.
+     * This is needed as at the moment we use 2 different stores for the FDs in native code:
+     * base::FileDescriptorStore which associates FDs with string identifiers (the key), and
+     * base::GlobalDescriptors which associates FDs with int ids.
+     * FDs for which the returned map contains a mapping are added to base::FileDescriptorStore with
+     * the associated key, all others are added to base::GlobalDescriptors.
+     */
+    SparseArray<String> getFileDescriptorsIdsToKeys();
+
+    /** Called before the main method is invoked. */
+    void onBeforeMain();
+
+    /**
+     * The main entry point for the service. This method should block as long as the service should
+     * be running.
+     */
+    void runMain();
+}
diff --git a/base/android/java/src/org/chromium/base/process_launcher/FileDescriptorInfo.aidl b/base/android/java/src/org/chromium/base/process_launcher/FileDescriptorInfo.aidl
new file mode 100644
index 0000000..e37d8c7
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/process_launcher/FileDescriptorInfo.aidl
@@ -0,0 +1,7 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base.process_launcher;
+
+parcelable FileDescriptorInfo;
diff --git a/base/android/java/src/org/chromium/base/process_launcher/FileDescriptorInfo.java b/base/android/java/src/org/chromium/base/process_launcher/FileDescriptorInfo.java
new file mode 100644
index 0000000..3dc3663
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/process_launcher/FileDescriptorInfo.java
@@ -0,0 +1,68 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base.process_launcher;
+
+import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
+import android.os.Parcelable;
+
+import org.chromium.base.annotations.MainDex;
+import org.chromium.base.annotations.UsedByReflection;
+
+import javax.annotation.concurrent.Immutable;
+
+/**
+ * Parcelable class that contains file descriptor and file region information to
+ * be passed to child processes.
+ */
+@Immutable
+@MainDex
+@UsedByReflection("child_process_launcher_helper_android.cc")
+public final class FileDescriptorInfo implements Parcelable {
+    public final int id;
+    public final ParcelFileDescriptor fd;
+    public final long offset;
+    public final long size;
+
+    public FileDescriptorInfo(int id, ParcelFileDescriptor fd, long offset, long size) {
+        this.id = id;
+        this.fd = fd;
+        this.offset = offset;
+        this.size = size;
+    }
+
+    FileDescriptorInfo(Parcel in) {
+        id = in.readInt();
+        fd = in.readParcelable(ParcelFileDescriptor.class.getClassLoader());
+        offset = in.readLong();
+        size = in.readLong();
+    }
+
+    @Override
+    public int describeContents() {
+        return CONTENTS_FILE_DESCRIPTOR;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(id);
+        dest.writeParcelable(fd, CONTENTS_FILE_DESCRIPTOR);
+        dest.writeLong(offset);
+        dest.writeLong(size);
+    }
+
+    public static final Parcelable.Creator<FileDescriptorInfo> CREATOR =
+            new Parcelable.Creator<FileDescriptorInfo>() {
+                @Override
+                public FileDescriptorInfo createFromParcel(Parcel in) {
+                    return new FileDescriptorInfo(in);
+                }
+
+                @Override
+                public FileDescriptorInfo[] newArray(int size) {
+                    return new FileDescriptorInfo[size];
+                }
+            };
+}
diff --git a/base/android/java/src/org/chromium/base/process_launcher/ICallbackInt.aidl b/base/android/java/src/org/chromium/base/process_launcher/ICallbackInt.aidl
new file mode 100644
index 0000000..db93cb0
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/process_launcher/ICallbackInt.aidl
@@ -0,0 +1,9 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base.process_launcher;
+
+oneway interface ICallbackInt {
+    void call(int value);
+}
diff --git a/base/android/java/src/org/chromium/base/process_launcher/IChildProcessService.aidl b/base/android/java/src/org/chromium/base/process_launcher/IChildProcessService.aidl
new file mode 100644
index 0000000..298e0bf
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/process_launcher/IChildProcessService.aidl
@@ -0,0 +1,26 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base.process_launcher;
+
+import android.os.Bundle;
+
+import org.chromium.base.process_launcher.ICallbackInt;
+
+interface IChildProcessService {
+  // On the first call to this method, the service will record the calling PID
+  // and return true. Subsequent calls will only return true if the calling PID
+  // is the same as the recorded one.
+  boolean bindToCaller();
+
+  // Sets up the initial IPC channel.
+  oneway void setupConnection(in Bundle args, ICallbackInt pidCallback,
+          in List<IBinder>  clientInterfaces);
+
+  // Forcefully kills the child process.
+  oneway void forceKill();
+
+  // Notifies about memory pressure. The argument is MemoryPressureLevel enum.
+  oneway void onMemoryPressure(int pressure);
+}
diff --git a/base/android/java/templates/BuildConfig.template b/base/android/java/templates/BuildConfig.template
new file mode 100644
index 0000000..1006d12
--- /dev/null
+++ b/base/android/java/templates/BuildConfig.template
@@ -0,0 +1,70 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base;
+
+#define Q(x) #x
+#define QUOTE(x) Q(x)
+
+#if defined(USE_FINAL)
+#define MAYBE_FINAL final
+#else
+#define MAYBE_FINAL
+#endif
+
+/**
+ *  Build configuration. Generated on a per-target basis.
+ */
+public class BuildConfig {
+
+#if defined(ENABLE_MULTIDEX)
+    public static MAYBE_FINAL boolean IS_MULTIDEX_ENABLED = true;
+#else
+    public static MAYBE_FINAL boolean IS_MULTIDEX_ENABLED = false;
+#endif
+
+#if defined(_FIREBASE_APP_ID)
+    public static MAYBE_FINAL String FIREBASE_APP_ID = QUOTE(_FIREBASE_APP_ID);
+#else
+    public static MAYBE_FINAL String FIREBASE_APP_ID = "";
+#endif
+
+#if defined(_DCHECK_IS_ON)
+    public static MAYBE_FINAL boolean DCHECK_IS_ON = true;
+#else
+    public static MAYBE_FINAL boolean DCHECK_IS_ON = false;
+#endif
+
+#if defined(_IS_UBSAN)
+    public static MAYBE_FINAL boolean IS_UBSAN = true;
+#else
+    public static MAYBE_FINAL boolean IS_UBSAN = false;
+#endif
+
+    // Sorted list of locales that have a compressed .pak within assets.
+    // Stored as an array because AssetManager.list() is slow.
+#if defined(COMPRESSED_LOCALE_LIST)
+    public static MAYBE_FINAL String[] COMPRESSED_LOCALES = COMPRESSED_LOCALE_LIST;
+#else
+    public static MAYBE_FINAL String[] COMPRESSED_LOCALES = {};
+#endif
+
+    // Sorted list of locales that have an uncompressed .pak within assets.
+    // Stored as an array because AssetManager.list() is slow.
+#if defined(UNCOMPRESSED_LOCALE_LIST)
+    public static MAYBE_FINAL String[] UNCOMPRESSED_LOCALES = UNCOMPRESSED_LOCALE_LIST;
+#else
+    public static MAYBE_FINAL String[] UNCOMPRESSED_LOCALES = {};
+#endif
+
+    // The ID of the android string resource that stores the product version.
+    // This layer of indirection is necessary to make the resource dependency
+    // optional for android_apk targets/base_java (ex. for cronet).
+#if defined(_RESOURCES_VERSION_VARIABLE)
+    public static MAYBE_FINAL int R_STRING_PRODUCT_VERSION = _RESOURCES_VERSION_VARIABLE;
+#else
+    // Default value, do not use.
+    public static MAYBE_FINAL int R_STRING_PRODUCT_VERSION = 0;
+#endif
+}
diff --git a/base/android/java/templates/NativeLibraries.template b/base/android/java/templates/NativeLibraries.template
new file mode 100644
index 0000000..68277df
--- /dev/null
+++ b/base/android/java/templates/NativeLibraries.template
@@ -0,0 +1,109 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base.library_loader;
+
+public class NativeLibraries {
+    /**
+     * IMPORTANT NOTE: The variables defined here must _not_ be 'final'.
+     *
+     * The reason for this is very subtle:
+     *
+     * - This template is used to generate several distinct, but similar
+     *   files used in different contexts:
+     *
+     *   o .../gen/templates/org/chromium/base/library_loader/NativeLibraries.java
+     *
+     *     This file is used to build base.jar, which is the library
+     *     jar used by chromium projects. However, the
+     *     corresponding NativeLibraries.class file will _not_ be part
+     *     of the final base.jar.
+     *
+     *   o .../$PROJECT/native_libraries_java/NativeLibraries.java
+     *
+     *     This file is used to build an APK (e.g. $PROJECT
+     *     could be 'content_shell_apk'). Its content will depend on
+     *     this target's specific build configuration, and differ from
+     *     the source file above.
+     *
+     * - During the final link, all .jar files are linked together into
+     *   a single .dex file, and the second version of NativeLibraries.class
+     *   will be put into the final output file, and used at runtime.
+     *
+     * - If the variables were defined as 'final', their value would be
+     *   optimized out inside of 'base.jar', and could not be specialized
+     *   for every chromium program. This, however, doesn't apply to arrays of
+     *   strings, which can be defined as final.
+     *
+     * This exotic scheme is used to avoid injecting project-specific, or
+     * even build-specific, values into the base layer. E.g. this is
+     * how the component build is supported on Android without modifying
+     * the sources of each and every Chromium-based target.
+     */
+
+    public static final int CPU_FAMILY_UNKNOWN = 0;
+    public static final int CPU_FAMILY_ARM = 1;
+    public static final int CPU_FAMILY_MIPS = 2;
+    public static final int CPU_FAMILY_X86 = 3;
+
+#if defined(ENABLE_CHROMIUM_LINKER_LIBRARY_IN_ZIP_FILE) && \
+    !defined(ENABLE_CHROMIUM_LINKER)
+#error "Must have ENABLE_CHROMIUM_LINKER to enable library in zip file"
+#endif
+
+    // Set to true to enable the use of the Chromium Linker.
+#if defined(ENABLE_CHROMIUM_LINKER)
+    public static boolean sUseLinker = true;
+#else
+    public static boolean sUseLinker = false;
+#endif
+
+#if defined(ENABLE_CHROMIUM_LINKER_LIBRARY_IN_ZIP_FILE)
+    public static boolean sUseLibraryInZipFile = true;
+#else
+    public static boolean sUseLibraryInZipFile = false;
+#endif
+
+#if defined(ENABLE_CHROMIUM_LINKER_TESTS)
+    public static boolean sEnableLinkerTests = true;
+#else
+    public static boolean sEnableLinkerTests = false;
+#endif
+
+    // This is the list of native libraries to be loaded (in the correct order)
+    // by LibraryLoader.java.  The base java library is compiled with no
+    // array defined, and then the build system creates a version of the file
+    // with the real list of libraries required (which changes based upon which
+    // .apk is being built).
+    // TODO(cjhopman): This is public since it is referenced by NativeTestActivity.java
+    // directly. The two ways of library loading should be refactored into one.
+    public static final String[] LIBRARIES =
+#if defined(NATIVE_LIBRARIES_LIST)
+      NATIVE_LIBRARIES_LIST;
+#else
+      {};
+#endif
+
+    // This is the expected version of the 'main' native library, which is the one that
+    // implements the initial set of base JNI functions including
+    // base::android::nativeGetVersionName()
+    static String sVersionNumber =
+#if defined(NATIVE_LIBRARIES_VERSION_NUMBER)
+      NATIVE_LIBRARIES_VERSION_NUMBER;
+#else
+      "";
+#endif
+
+    public static int sCpuFamily =
+#if defined(ANDROID_APP_CPU_FAMILY_ARM)
+        CPU_FAMILY_ARM;
+#elif defined(ANDROID_APP_CPU_FAMILY_X86)
+        CPU_FAMILY_X86;
+#elif defined(ANDROID_APP_CPU_FAMILY_MIPS)
+        CPU_FAMILY_MIPS;
+#else
+        CPU_FAMILY_UNKNOWN;
+#endif
+
+}
diff --git a/base/android/java_handler_thread.cc b/base/android/java_handler_thread.cc
new file mode 100644
index 0000000..ea87ba7
--- /dev/null
+++ b/base/android/java_handler_thread.cc
@@ -0,0 +1,132 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/android/java_handler_thread.h"
+
+#include <jni.h>
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "base/bind.h"
+#include "base/run_loop.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/platform_thread_internal_posix.h"
+#include "base/threading/thread_restrictions.h"
+#include "jni/JavaHandlerThread_jni.h"
+
+using base::android::AttachCurrentThread;
+
+namespace base {
+
+namespace android {
+
+JavaHandlerThread::JavaHandlerThread(const char* name,
+                                     base::ThreadPriority priority)
+    : JavaHandlerThread(Java_JavaHandlerThread_create(
+          AttachCurrentThread(),
+          ConvertUTF8ToJavaString(AttachCurrentThread(), name),
+          base::internal::ThreadPriorityToNiceValue(priority))) {}
+
+JavaHandlerThread::JavaHandlerThread(
+    const base::android::ScopedJavaLocalRef<jobject>& obj)
+    : java_thread_(obj) {}
+
+JavaHandlerThread::~JavaHandlerThread() {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  DCHECK(!Java_JavaHandlerThread_isAlive(env, java_thread_));
+  DCHECK(!message_loop_ || message_loop_->IsAborted());
+  // TODO(mthiesse): We shouldn't leak the MessageLoop as this could affect
+  // future tests.
+  if (message_loop_ && message_loop_->IsAborted()) {
+    // When the message loop has been aborted due to a crash, we intentionally
+    // leak the message loop because the message loop hasn't been shut down
+    // properly and would trigger DCHECKS. This should only happen in tests,
+    // where we handle the exception instead of letting it take down the
+    // process.
+    message_loop_.release();
+  }
+}
+
+void JavaHandlerThread::Start() {
+  // Check the thread has not already been started.
+  DCHECK(!message_loop_);
+
+  JNIEnv* env = base::android::AttachCurrentThread();
+  base::WaitableEvent initialize_event(
+      WaitableEvent::ResetPolicy::AUTOMATIC,
+      WaitableEvent::InitialState::NOT_SIGNALED);
+  Java_JavaHandlerThread_startAndInitialize(
+      env, java_thread_, reinterpret_cast<intptr_t>(this),
+      reinterpret_cast<intptr_t>(&initialize_event));
+  // Wait for thread to be initialized so it is ready to be used when Start
+  // returns.
+  base::ThreadRestrictions::ScopedAllowWait wait_allowed;
+  initialize_event.Wait();
+}
+
+void JavaHandlerThread::Stop() {
+  DCHECK(!task_runner()->BelongsToCurrentThread());
+  task_runner()->PostTask(
+      FROM_HERE,
+      base::BindOnce(&JavaHandlerThread::StopOnThread, base::Unretained(this)));
+  JNIEnv* env = base::android::AttachCurrentThread();
+  Java_JavaHandlerThread_joinThread(env, java_thread_);
+}
+
+void JavaHandlerThread::InitializeThread(JNIEnv* env,
+                                         const JavaParamRef<jobject>& obj,
+                                         jlong event) {
+  // TYPE_JAVA to get the Android java style message loop.
+  message_loop_ =
+      std::make_unique<MessageLoopForUI>(base::MessageLoop::TYPE_JAVA);
+  Init();
+  reinterpret_cast<base::WaitableEvent*>(event)->Signal();
+}
+
+void JavaHandlerThread::OnLooperStopped(JNIEnv* env,
+                                        const JavaParamRef<jobject>& obj) {
+  DCHECK(task_runner()->BelongsToCurrentThread());
+  message_loop_.reset();
+  CleanUp();
+}
+
+void JavaHandlerThread::StopMessageLoopForTesting() {
+  DCHECK(task_runner()->BelongsToCurrentThread());
+  StopOnThread();
+}
+
+void JavaHandlerThread::JoinForTesting() {
+  DCHECK(!task_runner()->BelongsToCurrentThread());
+  JNIEnv* env = base::android::AttachCurrentThread();
+  Java_JavaHandlerThread_joinThread(env, java_thread_);
+}
+
+void JavaHandlerThread::ListenForUncaughtExceptionsForTesting() {
+  DCHECK(!task_runner()->BelongsToCurrentThread());
+  JNIEnv* env = base::android::AttachCurrentThread();
+  Java_JavaHandlerThread_listenForUncaughtExceptionsForTesting(env,
+                                                               java_thread_);
+}
+
+ScopedJavaLocalRef<jthrowable> JavaHandlerThread::GetUncaughtExceptionIfAny() {
+  DCHECK(!task_runner()->BelongsToCurrentThread());
+  JNIEnv* env = base::android::AttachCurrentThread();
+  return Java_JavaHandlerThread_getUncaughtExceptionIfAny(env, java_thread_);
+}
+
+void JavaHandlerThread::StopOnThread() {
+  DCHECK(task_runner()->BelongsToCurrentThread());
+  message_loop_->QuitWhenIdle(base::BindOnce(
+      &JavaHandlerThread::QuitThreadSafely, base::Unretained(this)));
+}
+
+void JavaHandlerThread::QuitThreadSafely() {
+  DCHECK(task_runner()->BelongsToCurrentThread());
+  JNIEnv* env = base::android::AttachCurrentThread();
+  Java_JavaHandlerThread_quitThreadSafely(env, java_thread_,
+                                          reinterpret_cast<intptr_t>(this));
+}
+
+} // namespace android
+} // namespace base
diff --git a/base/android/java_handler_thread.h b/base/android/java_handler_thread.h
new file mode 100644
index 0000000..d65dfe2
--- /dev/null
+++ b/base/android/java_handler_thread.h
@@ -0,0 +1,95 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_ANDROID_JAVA_HANDLER_THREAD_H_
+#define BASE_ANDROID_JAVA_HANDLER_THREAD_H_
+
+#include <jni.h>
+
+#include <memory>
+
+#include "base/android/scoped_java_ref.h"
+#include "base/message_loop/message_loop.h"
+#include "base/single_thread_task_runner.h"
+
+namespace base {
+
+class MessageLoop;
+
+namespace android {
+
+// A Java Thread with a native message loop. To run tasks, post them
+// to the message loop and they will be scheduled along with Java tasks
+// on the thread.
+// This is useful for callbacks where the receiver expects a thread
+// with a prepared Looper.
+class BASE_EXPORT JavaHandlerThread {
+ public:
+  // Create new thread.
+  explicit JavaHandlerThread(
+      const char* name,
+      base::ThreadPriority priority = base::ThreadPriority::NORMAL);
+  // Wrap and connect to an existing JavaHandlerThread.
+  // |obj| is an instance of JavaHandlerThread.
+  explicit JavaHandlerThread(
+      const base::android::ScopedJavaLocalRef<jobject>& obj);
+  virtual ~JavaHandlerThread();
+
+  // Called from any thread.
+  base::MessageLoop* message_loop() const { return message_loop_.get(); }
+
+  // Gets the TaskRunner associated with the message loop.
+  // Called from any thread.
+  scoped_refptr<SingleThreadTaskRunner> task_runner() const {
+    return message_loop_ ? message_loop_->task_runner() : nullptr;
+  }
+
+  // Called from the parent thread.
+  void Start();
+  void Stop();
+
+  // Called from java on the newly created thread.
+  // Start() will not return before this methods has finished.
+  void InitializeThread(JNIEnv* env,
+                        const JavaParamRef<jobject>& obj,
+                        jlong event);
+  // Called from java on this thread.
+  void OnLooperStopped(JNIEnv* env, const JavaParamRef<jobject>& obj);
+
+  // Called from this thread.
+  void StopMessageLoopForTesting();
+  // Called from this thread.
+  void JoinForTesting();
+
+  // Called from this thread.
+  // See comment in JavaHandlerThread.java regarding use of this function.
+  void ListenForUncaughtExceptionsForTesting();
+  // Called from this thread.
+  ScopedJavaLocalRef<jthrowable> GetUncaughtExceptionIfAny();
+
+ protected:
+  // Semantically the same as base::Thread#Init(), but unlike base::Thread the
+  // Android Looper will already be running. This Init() call will still run
+  // before other tasks are posted to the thread.
+  virtual void Init() {}
+
+  // Semantically the same as base::Thread#CleanUp(), called after the message
+  // loop ends. The Android Looper will also have been quit by this point.
+  virtual void CleanUp() {}
+
+  std::unique_ptr<base::MessageLoopForUI> message_loop_;
+
+ private:
+  void StartMessageLoop();
+
+  void StopOnThread();
+  void QuitThreadSafely();
+
+  ScopedJavaGlobalRef<jobject> java_thread_;
+};
+
+}  // namespace android
+}  // namespace base
+
+#endif  // BASE_ANDROID_JAVA_HANDLER_THREAD_H_
diff --git a/base/android/javatests/src/org/chromium/base/AssertsTest.java b/base/android/javatests/src/org/chromium/base/AssertsTest.java
deleted file mode 100644
index 37e3b40..0000000
--- a/base/android/javatests/src/org/chromium/base/AssertsTest.java
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.base;
-
-import android.support.test.filters.SmallTest;
-
-import org.junit.Assert;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import org.chromium.base.test.BaseJUnit4ClassRunner;
-
-/**
- * Test that ensures Java asserts are working.
- *
- * Not a robolectric test because we want to make sure asserts are enabled after dexing.
- */
-@RunWith(BaseJUnit4ClassRunner.class)
-public class AssertsTest {
-    @Test
-    @SmallTest
-    @SuppressWarnings("UseCorrectAssertInTests")
-    public void testAssertsWorkAsExpected() {
-        if (BuildConfig.DCHECK_IS_ON) {
-            try {
-                assert false;
-            } catch (AssertionError e) {
-                // When DCHECK is on, asserts should throw AssertionErrors.
-                return;
-            }
-            Assert.fail("Java assert unexpectedly didn't fire.");
-        } else {
-            // When DCHECK isn't on, asserts should be removed by proguard.
-            assert false : "Java assert unexpectedly fired.";
-        }
-    }
-}
diff --git a/base/android/javatests/src/org/chromium/base/AsyncTaskTest.java b/base/android/javatests/src/org/chromium/base/AsyncTaskTest.java
deleted file mode 100644
index 2fd92be..0000000
--- a/base/android/javatests/src/org/chromium/base/AsyncTaskTest.java
+++ /dev/null
@@ -1,128 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.base;
-
-import android.support.annotation.NonNull;
-import android.support.test.filters.SmallTest;
-
-import org.hamcrest.CoreMatchers;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.junit.runner.RunWith;
-
-import org.chromium.base.test.BaseJUnit4ClassRunner;
-
-import java.util.concurrent.ArrayBlockingQueue;
-import java.util.concurrent.Executor;
-import java.util.concurrent.RejectedExecutionException;
-import java.util.concurrent.ThreadFactory;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Tests for our AsyncTask modifications
- *
- * Not a robolectric test because the reflection doesn't work with ShadowAsyncTask.
- */
-@RunWith(BaseJUnit4ClassRunner.class)
-public class AsyncTaskTest {
-    private static class SpecialChromeAsyncTask extends AsyncTask<Void, Void, Void> {
-        @Override
-        protected Void doInBackground(Void... params) {
-            return null;
-        }
-    }
-
-    private static class SpecialOsAsyncTask extends android.os.AsyncTask<Void, Void, Void> {
-        @Override
-        protected Void doInBackground(Void... params) {
-            return null;
-        }
-    }
-
-    private static class SpecialRunnable implements Runnable {
-        @Override
-        public void run() {}
-    }
-
-    private static final int QUEUE_SIZE = 40;
-
-    @Rule
-    public ExpectedException thrown = ExpectedException.none();
-
-    /**
-     * Test filling the queue with basic Runnables, then add a final AsyncTask to overfill it, and
-     * ensure the Runnable is the one blamed in the exception message.
-     */
-    @Test
-    @SmallTest
-    public void testChromeThreadPoolExecutorRunnables() {
-        Executor executor = new AsyncTask.ChromeThreadPoolExecutor(1, 1, 1, TimeUnit.SECONDS,
-                new ArrayBlockingQueue<Runnable>(QUEUE_SIZE), new ThreadFactory() {
-                    @Override
-                    public Thread newThread(@NonNull Runnable r) {
-                        return null;
-                    }
-                });
-        for (int i = 0; i < QUEUE_SIZE; i++) {
-            executor.execute(new SpecialRunnable());
-        }
-        thrown.expect(RejectedExecutionException.class);
-        thrown.expectMessage(
-                CoreMatchers.containsString("org.chromium.base.AsyncTaskTest$SpecialRunnable"));
-        thrown.expectMessage(
-                CoreMatchers.not(CoreMatchers.containsString("SpecialChromeAsyncTask")));
-        new SpecialChromeAsyncTask().executeOnExecutor(executor);
-    }
-
-    /**
-     * Test filling the queue with Chrome AsyncTasks, then add a final OS AsyncTask to
-     * overfill it and ensure the Chrome AsyncTask is the one blamed in the exception message.
-     */
-    @Test
-    @SmallTest
-    public void testChromeThreadPoolExecutorChromeAsyncTask() {
-        Executor executor = new AsyncTask.ChromeThreadPoolExecutor(1, 1, 1, TimeUnit.SECONDS,
-                new ArrayBlockingQueue<Runnable>(QUEUE_SIZE), new ThreadFactory() {
-                    @Override
-                    public Thread newThread(@NonNull Runnable r) {
-                        return null;
-                    }
-                });
-        for (int i = 0; i < QUEUE_SIZE; i++) {
-            new SpecialChromeAsyncTask().executeOnExecutor(executor);
-        }
-        thrown.expect(RejectedExecutionException.class);
-        thrown.expectMessage(CoreMatchers.containsString(
-                "org.chromium.base.AsyncTaskTest$SpecialChromeAsyncTask"));
-        thrown.expectMessage(CoreMatchers.not(CoreMatchers.containsString("SpecialOsAsyncTask")));
-        new SpecialOsAsyncTask().executeOnExecutor(executor);
-    }
-
-    /**
-     * Test filling the queue with android.os.AsyncTasks, then add a final ChromeAsyncTask to
-     * overfill it and ensure the OsAsyncTask is the one blamed in the exception message.
-     */
-    @Test
-    @SmallTest
-    public void testChromeThreadPoolExecutorOsAsyncTask() {
-        Executor executor = new AsyncTask.ChromeThreadPoolExecutor(1, 1, 1, TimeUnit.SECONDS,
-                new ArrayBlockingQueue<Runnable>(QUEUE_SIZE), new ThreadFactory() {
-                    @Override
-                    public Thread newThread(@NonNull Runnable r) {
-                        return null;
-                    }
-                });
-        for (int i = 0; i < QUEUE_SIZE; i++) {
-            new SpecialOsAsyncTask().executeOnExecutor(executor);
-        }
-        thrown.expect(RejectedExecutionException.class);
-        thrown.expectMessage(
-                CoreMatchers.containsString("org.chromium.base.AsyncTaskTest$SpecialOsAsyncTask"));
-        thrown.expectMessage(
-                CoreMatchers.not(CoreMatchers.containsString("SpecialChromeAsyncTask")));
-        new SpecialChromeAsyncTask().executeOnExecutor(executor);
-    }
-}
diff --git a/base/android/jni_array_unittest.cc b/base/android/jni_array_unittest.cc
new file mode 100644
index 0000000..245cd50
--- /dev/null
+++ b/base/android/jni_array_unittest.cc
@@ -0,0 +1,389 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/android/jni_array.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <limits>
+
+#include "base/android/jni_android.h"
+#include "base/android/scoped_java_ref.h"
+#include "base/macros.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace android {
+
+TEST(JniArray, BasicConversions) {
+  const uint8_t kBytes[] = {0, 1, 2, 3};
+  const size_t kLen = arraysize(kBytes);
+  JNIEnv* env = AttachCurrentThread();
+  ScopedJavaLocalRef<jbyteArray> bytes = ToJavaByteArray(env, kBytes, kLen);
+  ASSERT_TRUE(bytes.obj());
+
+  std::vector<uint8_t> inputVector(kBytes, kBytes + kLen);
+  ScopedJavaLocalRef<jbyteArray> bytesFromVector =
+      ToJavaByteArray(env, inputVector);
+  ASSERT_TRUE(bytesFromVector.obj());
+
+  std::vector<uint8_t> vectorFromBytes(5);
+  std::vector<uint8_t> vectorFromVector(5);
+  JavaByteArrayToByteVector(env, bytes.obj(), &vectorFromBytes);
+  JavaByteArrayToByteVector(env, bytesFromVector.obj(), &vectorFromVector);
+  EXPECT_EQ(4U, vectorFromBytes.size());
+  EXPECT_EQ(4U, vectorFromVector.size());
+  std::vector<uint8_t> expected_vec(kBytes, kBytes + kLen);
+  EXPECT_EQ(expected_vec, vectorFromBytes);
+  EXPECT_EQ(expected_vec, vectorFromVector);
+
+  AppendJavaByteArrayToByteVector(env, bytes.obj(), &vectorFromBytes);
+  EXPECT_EQ(8U, vectorFromBytes.size());
+  expected_vec.insert(expected_vec.end(), kBytes, kBytes + kLen);
+  EXPECT_EQ(expected_vec, vectorFromBytes);
+}
+
+void CheckBoolConversion(JNIEnv* env,
+                         const bool* bool_array,
+                         const size_t len,
+                         const ScopedJavaLocalRef<jbooleanArray>& booleans) {
+  ASSERT_TRUE(booleans.obj());
+
+  jsize java_array_len = env->GetArrayLength(booleans.obj());
+  ASSERT_EQ(static_cast<jsize>(len), java_array_len);
+
+  jboolean value;
+  for (size_t i = 0; i < len; ++i) {
+    env->GetBooleanArrayRegion(booleans.obj(), i, 1, &value);
+    ASSERT_EQ(bool_array[i], value);
+  }
+}
+
+TEST(JniArray, BoolConversions) {
+  const bool kBools[] = {false, true, false};
+  const size_t kLen = arraysize(kBools);
+
+  JNIEnv* env = AttachCurrentThread();
+  CheckBoolConversion(env, kBools, kLen, ToJavaBooleanArray(env, kBools, kLen));
+}
+
+void CheckIntConversion(
+    JNIEnv* env,
+    const int* int_array,
+    const size_t len,
+    const ScopedJavaLocalRef<jintArray>& ints) {
+  ASSERT_TRUE(ints.obj());
+
+  jsize java_array_len = env->GetArrayLength(ints.obj());
+  ASSERT_EQ(static_cast<jsize>(len), java_array_len);
+
+  jint value;
+  for (size_t i = 0; i < len; ++i) {
+    env->GetIntArrayRegion(ints.obj(), i, 1, &value);
+    ASSERT_EQ(int_array[i], value);
+  }
+}
+
+TEST(JniArray, IntConversions) {
+  const int kInts[] = {0, 1, -1, std::numeric_limits<int32_t>::min(),
+                       std::numeric_limits<int32_t>::max()};
+  const size_t kLen = arraysize(kInts);
+
+  JNIEnv* env = AttachCurrentThread();
+  CheckIntConversion(env, kInts, kLen, ToJavaIntArray(env, kInts, kLen));
+
+  const std::vector<int> vec(kInts, kInts + kLen);
+  CheckIntConversion(env, kInts, kLen, ToJavaIntArray(env, vec));
+}
+
+void CheckLongConversion(JNIEnv* env,
+                         const int64_t* long_array,
+                         const size_t len,
+                         const ScopedJavaLocalRef<jlongArray>& longs) {
+  ASSERT_TRUE(longs.obj());
+
+  jsize java_array_len = env->GetArrayLength(longs.obj());
+  ASSERT_EQ(static_cast<jsize>(len), java_array_len);
+
+  jlong value;
+  for (size_t i = 0; i < len; ++i) {
+    env->GetLongArrayRegion(longs.obj(), i, 1, &value);
+    ASSERT_EQ(long_array[i], value);
+  }
+}
+
+TEST(JniArray, LongConversions) {
+  const int64_t kLongs[] = {0, 1, -1, std::numeric_limits<int64_t>::min(),
+                            std::numeric_limits<int64_t>::max()};
+  const size_t kLen = arraysize(kLongs);
+
+  JNIEnv* env = AttachCurrentThread();
+  CheckLongConversion(env, kLongs, kLen, ToJavaLongArray(env, kLongs, kLen));
+
+  const std::vector<int64_t> vec(kLongs, kLongs + kLen);
+  CheckLongConversion(env, kLongs, kLen, ToJavaLongArray(env, vec));
+}
+
+void CheckIntArrayConversion(JNIEnv* env,
+                             ScopedJavaLocalRef<jintArray> jints,
+                             std::vector<int> int_vector,
+                             const size_t len) {
+  jint value;
+  for (size_t i = 0; i < len; ++i) {
+    env->GetIntArrayRegion(jints.obj(), i, 1, &value);
+    ASSERT_EQ(int_vector[i], value);
+  }
+}
+
+void CheckBoolArrayConversion(JNIEnv* env,
+                              ScopedJavaLocalRef<jbooleanArray> jbooleans,
+                              std::vector<bool> bool_vector,
+                              const size_t len) {
+  jboolean value;
+  for (size_t i = 0; i < len; ++i) {
+    env->GetBooleanArrayRegion(jbooleans.obj(), i, 1, &value);
+    ASSERT_EQ(bool_vector[i], value);
+  }
+}
+
+void CheckFloatConversion(
+    JNIEnv* env,
+    const float* float_array,
+    const size_t len,
+    const ScopedJavaLocalRef<jfloatArray>& floats) {
+  ASSERT_TRUE(floats.obj());
+
+  jsize java_array_len = env->GetArrayLength(floats.obj());
+  ASSERT_EQ(static_cast<jsize>(len), java_array_len);
+
+  jfloat value;
+  for (size_t i = 0; i < len; ++i) {
+    env->GetFloatArrayRegion(floats.obj(), i, 1, &value);
+    ASSERT_EQ(float_array[i], value);
+  }
+}
+
+TEST(JniArray, FloatConversions) {
+  const float kFloats[] = { 0.0f, 1.0f, -10.0f};
+  const size_t kLen = arraysize(kFloats);
+
+  JNIEnv* env = AttachCurrentThread();
+  CheckFloatConversion(env, kFloats, kLen,
+                       ToJavaFloatArray(env, kFloats, kLen));
+
+  const std::vector<float> vec(kFloats, kFloats + kLen);
+  CheckFloatConversion(env, kFloats, kLen, ToJavaFloatArray(env, vec));
+}
+
+TEST(JniArray, JavaBooleanArrayToBoolVector) {
+  const bool kBools[] = {false, true, false};
+  const size_t kLen = arraysize(kBools);
+
+  JNIEnv* env = AttachCurrentThread();
+  ScopedJavaLocalRef<jbooleanArray> jbooleans(env, env->NewBooleanArray(kLen));
+  ASSERT_TRUE(jbooleans.obj());
+
+  for (size_t i = 0; i < kLen; ++i) {
+    jboolean j = static_cast<jboolean>(kBools[i]);
+    env->SetBooleanArrayRegion(jbooleans.obj(), i, 1, &j);
+    ASSERT_FALSE(HasException(env));
+  }
+
+  std::vector<bool> bools;
+  JavaBooleanArrayToBoolVector(env, jbooleans.obj(), &bools);
+
+  ASSERT_EQ(static_cast<jsize>(bools.size()),
+            env->GetArrayLength(jbooleans.obj()));
+
+  CheckBoolArrayConversion(env, jbooleans, bools, kLen);
+}
+
+TEST(JniArray, JavaIntArrayToIntVector) {
+  const int kInts[] = {0, 1, -1};
+  const size_t kLen = arraysize(kInts);
+
+  JNIEnv* env = AttachCurrentThread();
+  ScopedJavaLocalRef<jintArray> jints(env, env->NewIntArray(kLen));
+  ASSERT_TRUE(jints.obj());
+
+  for (size_t i = 0; i < kLen; ++i) {
+    jint j = static_cast<jint>(kInts[i]);
+    env->SetIntArrayRegion(jints.obj(), i, 1, &j);
+    ASSERT_FALSE(HasException(env));
+  }
+
+  std::vector<int> ints;
+  JavaIntArrayToIntVector(env, jints.obj(), &ints);
+
+  ASSERT_EQ(static_cast<jsize>(ints.size()), env->GetArrayLength(jints.obj()));
+
+  CheckIntArrayConversion(env, jints, ints, kLen);
+}
+
+TEST(JniArray, JavaLongArrayToInt64Vector) {
+  const int64_t kInt64s[] = {0LL, 1LL, -1LL};
+  const size_t kLen = arraysize(kInt64s);
+
+  JNIEnv* env = AttachCurrentThread();
+  ScopedJavaLocalRef<jlongArray> jlongs(env, env->NewLongArray(kLen));
+  ASSERT_TRUE(jlongs.obj());
+
+  for (size_t i = 0; i < kLen; ++i) {
+    jlong j = static_cast<jlong>(kInt64s[i]);
+    env->SetLongArrayRegion(jlongs.obj(), i, 1, &j);
+    ASSERT_FALSE(HasException(env));
+  }
+
+  std::vector<int64_t> int64s;
+  JavaLongArrayToInt64Vector(env, jlongs.obj(), &int64s);
+
+  ASSERT_EQ(static_cast<jsize>(int64s.size()),
+            env->GetArrayLength(jlongs.obj()));
+
+  jlong value;
+  for (size_t i = 0; i < kLen; ++i) {
+    env->GetLongArrayRegion(jlongs.obj(), i, 1, &value);
+    ASSERT_EQ(int64s[i], value);
+    ASSERT_EQ(kInt64s[i], int64s[i]);
+  }
+}
+
+TEST(JniArray, JavaLongArrayToLongVector) {
+  const int64_t kInt64s[] = {0LL, 1LL, -1LL};
+  const size_t kLen = arraysize(kInt64s);
+
+  JNIEnv* env = AttachCurrentThread();
+  ScopedJavaLocalRef<jlongArray> jlongs(env, env->NewLongArray(kLen));
+  ASSERT_TRUE(jlongs.obj());
+
+  for (size_t i = 0; i < kLen; ++i) {
+    jlong j = static_cast<jlong>(kInt64s[i]);
+    env->SetLongArrayRegion(jlongs.obj(), i, 1, &j);
+    ASSERT_FALSE(HasException(env));
+  }
+
+  std::vector<jlong> jlongs_vector;
+  JavaLongArrayToLongVector(env, jlongs.obj(), &jlongs_vector);
+
+  ASSERT_EQ(static_cast<jsize>(jlongs_vector.size()),
+            env->GetArrayLength(jlongs.obj()));
+
+  jlong value;
+  for (size_t i = 0; i < kLen; ++i) {
+    env->GetLongArrayRegion(jlongs.obj(), i, 1, &value);
+    ASSERT_EQ(jlongs_vector[i], value);
+  }
+}
+
+TEST(JniArray, JavaFloatArrayToFloatVector) {
+  const float kFloats[] = {0.0, 0.5, -0.5};
+  const size_t kLen = arraysize(kFloats);
+
+  JNIEnv* env = AttachCurrentThread();
+  ScopedJavaLocalRef<jfloatArray> jfloats(env, env->NewFloatArray(kLen));
+  ASSERT_TRUE(jfloats.obj());
+
+  for (size_t i = 0; i < kLen; ++i) {
+    jfloat j = static_cast<jfloat>(kFloats[i]);
+    env->SetFloatArrayRegion(jfloats.obj(), i, 1, &j);
+    ASSERT_FALSE(HasException(env));
+  }
+
+  std::vector<float> floats;
+  JavaFloatArrayToFloatVector(env, jfloats.obj(), &floats);
+
+  ASSERT_EQ(static_cast<jsize>(floats.size()),
+      env->GetArrayLength(jfloats.obj()));
+
+  jfloat value;
+  for (size_t i = 0; i < kLen; ++i) {
+    env->GetFloatArrayRegion(jfloats.obj(), i, 1, &value);
+    ASSERT_EQ(floats[i], value);
+  }
+}
+
+TEST(JniArray, JavaArrayOfByteArrayToStringVector) {
+  const int kMaxItems = 50;
+  JNIEnv* env = AttachCurrentThread();
+
+  // Create a byte[][] object.
+  ScopedJavaLocalRef<jclass> byte_array_clazz(env, env->FindClass("[B"));
+  ASSERT_TRUE(byte_array_clazz.obj());
+
+  ScopedJavaLocalRef<jobjectArray> array(
+      env, env->NewObjectArray(kMaxItems, byte_array_clazz.obj(), NULL));
+  ASSERT_TRUE(array.obj());
+
+  // Create kMaxItems byte buffers.
+  char text[16];
+  for (int i = 0; i < kMaxItems; ++i) {
+    snprintf(text, sizeof text, "%d", i);
+    ScopedJavaLocalRef<jbyteArray> byte_array =
+        ToJavaByteArray(env, reinterpret_cast<uint8_t*>(text),
+                        static_cast<size_t>(strlen(text)));
+    ASSERT_TRUE(byte_array.obj());
+
+    env->SetObjectArrayElement(array.obj(), i, byte_array.obj());
+    ASSERT_FALSE(HasException(env));
+  }
+
+  // Convert to std::vector<std::string>, check the content.
+  std::vector<std::string> vec;
+  JavaArrayOfByteArrayToStringVector(env, array.obj(), &vec);
+
+  EXPECT_EQ(static_cast<size_t>(kMaxItems), vec.size());
+  for (int i = 0; i < kMaxItems; ++i) {
+    snprintf(text, sizeof text, "%d", i);
+    EXPECT_STREQ(text, vec[i].c_str());
+  }
+}
+
+TEST(JniArray, JavaArrayOfIntArrayToIntVector) {
+  const size_t kNumItems = 4;
+  JNIEnv* env = AttachCurrentThread();
+
+  // Create an int[][] object.
+  ScopedJavaLocalRef<jclass> int_array_clazz(env, env->FindClass("[I"));
+  ASSERT_TRUE(int_array_clazz.obj());
+
+  ScopedJavaLocalRef<jobjectArray> array(
+      env, env->NewObjectArray(kNumItems, int_array_clazz.obj(), nullptr));
+  ASSERT_TRUE(array.obj());
+
+  // Populate int[][] object.
+  const int kInts0[] = {0, 1, -1, std::numeric_limits<int32_t>::min(),
+                        std::numeric_limits<int32_t>::max()};
+  const size_t kLen0 = arraysize(kInts0);
+  ScopedJavaLocalRef<jintArray> int_array0 = ToJavaIntArray(env, kInts0, kLen0);
+  env->SetObjectArrayElement(array.obj(), 0, int_array0.obj());
+
+  const int kInts1[] = {3, 4, 5};
+  const size_t kLen1 = arraysize(kInts1);
+  ScopedJavaLocalRef<jintArray> int_array1 = ToJavaIntArray(env, kInts1, kLen1);
+  env->SetObjectArrayElement(array.obj(), 1, int_array1.obj());
+
+  const int kInts2[] = {};
+  const size_t kLen2 = 0;
+  ScopedJavaLocalRef<jintArray> int_array2 = ToJavaIntArray(env, kInts2, kLen2);
+  env->SetObjectArrayElement(array.obj(), 2, int_array2.obj());
+
+  const int kInts3[] = {16};
+  const size_t kLen3 = arraysize(kInts3);
+  ScopedJavaLocalRef<jintArray> int_array3 = ToJavaIntArray(env, kInts3, kLen3);
+  env->SetObjectArrayElement(array.obj(), 3, int_array3.obj());
+
+  // Convert to std::vector<std::vector<int>>, check the content.
+  std::vector<std::vector<int>> out;
+  JavaArrayOfIntArrayToIntVector(env, array.obj(), &out);
+
+  EXPECT_EQ(kNumItems, out.size());
+  CheckIntArrayConversion(env, int_array0, out[0], kLen0);
+  CheckIntArrayConversion(env, int_array1, out[1], kLen1);
+  CheckIntArrayConversion(env, int_array2, out[2], kLen2);
+  CheckIntArrayConversion(env, int_array3, out[3], kLen3);
+}
+
+}  // namespace android
+}  // namespace base
diff --git a/base/android/jni_generator/PRESUBMIT.py b/base/android/jni_generator/PRESUBMIT.py
deleted file mode 100644
index bc76d5b..0000000
--- a/base/android/jni_generator/PRESUBMIT.py
+++ /dev/null
@@ -1,37 +0,0 @@
-# Copyright 2018 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-"""Presubmit script for android buildbot.
-
-See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts for
-details on the presubmit API built into depot_tools.
-"""
-
-
-def CommonChecks(input_api, output_api):
-  base_android_jni_generator_dir = input_api.PresubmitLocalPath()
-
-  env = dict(input_api.environ)
-  env.update({
-    'PYTHONPATH': base_android_jni_generator_dir,
-    'PYTHONDONTWRITEBYTECODE': '1',
-  })
-
-  return input_api.canned_checks.RunUnitTests(
-      input_api,
-      output_api,
-      unit_tests=[
-        input_api.os_path.join(
-            base_android_jni_generator_dir, 'jni_generator_tests.py')
-      ],
-      env=env,
-  )
-
-
-def CheckChangeOnUpload(input_api, output_api):
-    return CommonChecks(input_api, output_api)
-
-
-def CheckChangeOnCommit(input_api, output_api):
-    return CommonChecks(input_api, output_api)
diff --git a/base/android/jni_generator/README.md b/base/android/jni_generator/README.md
new file mode 100644
index 0000000..17ad150
--- /dev/null
+++ b/base/android/jni_generator/README.md
@@ -0,0 +1,118 @@
+# Overview
+JNI (Java Native Interface) is the mechanism that enables Java code to call
+native functions, and native code to call Java functions.
+
+ * Native code calls into Java using apis from `<jni.h>`, which basically mirror
+   Java's reflection APIs.
+ * Java code calls native functions by declaring body-less functions with the
+  `native` keyword, and then calling them as normal Java functions.
+
+`jni_generator` generates boiler-plate code with the goal of making our code:
+ 1. easier to write, and
+ 2. typesafe.
+
+`jni_generator` uses regular expressions to parse .Java files, so don't do
+anything too fancy. E.g.:
+ * Classes must be either explicitly imported, or are assumed to be in
+the same package. To use `java.lang` classes, add an explicit import.
+ * Inner classes need to be referenced through the outer class. E.g.:
+   `void call(Outer.Inner inner)`
+
+The presense of any JNI within a class will result in ProGuard obfuscation for
+the class to be disabled.
+
+### Exposing Native Methods
+
+**Without Crazy Linker:**
+ * Java->Native calls are exported from the shared library and lazily resolved
+   by the runtime (via `dlsym()`).
+
+**With Crazy Linker:**
+ * Java->Native calls are explicitly registered with JNI on the native side.
+   Explicit registration is necessary because crazy linker provides its own
+   `dlsym()`, but JNI is hardcoded to use the system's `dlsym()`.
+   * The logic to explicitly register stubs is generated by
+     `jni_registration_generator.py`.
+     * This script finds all native methods by scanning all source `.java` files
+       of an APK. Inefficient, but very convenient.
+   * Since `dlsym()` is not used in this case, we use a linker script to avoid
+     the cost of exporting symbols from the shared library (refer to
+     `//build/config/android:hide_all_but_jni_onload`).
+ * `jni_registration_generator.py` exposes two registrations methods:
+   * `RegisterNonMainDexNatives` - Registers native functions needed by multiple
+     process types (e.g. Rendereres, GPU process).
+   * `RegisterMainDexNatives` - Registers native functions needed only by the
+     browser process.
+
+### Exposing Java Methods
+
+Java methods just need to be annotated with `@CalledByNative`. The generated
+functions can be put into a namespace using `@JNINamespace("your_namespace")`.
+
+## Usage
+
+Because the generator does not generate any source files, generated headers must
+not be `#included` by multiple sources. If there are Java functions that need to
+be called by multiple sources, one source should be chosen to expose the
+functions to the others via additional wrapper functions.
+
+### Calling Java -> Native
+
+ * Methods marked as `native` will have stubs generated for them that forward
+   calls to C++ function (that you must write).
+ * If the first parameter is a C++ object (e.g. `long mNativePointer`), then the
+   bindings will automatically generate the appropriate cast and call into C++
+   code (JNI itself is only C).
+
+### Calling Native -> Java
+
+ * Methods annotated with `@CalledByNative` will have stubs generated for them.
+ * Just call the generated stubs defined in generated `.h` files.
+
+### Java Objects and Garbage Collection
+
+All pointers to Java objects must be registered with JNI in order to prevent
+garbage collection from invalidating them.
+
+For Strings & Arrays - it's common practice to use the `//base/android/jni_*`
+helpers to convert them to `std::vectors` and `std::strings` as soon as
+possible.
+
+For other objects - use smart pointers to store them:
+ * `ScopedJavaLocalRef<>` - When lifetime is the current function's scope.
+ * `ScopedJavaGlobalRef<>` - When lifetime is longer than the current function's
+   scope.
+ * `JavaObjectWeakGlobalRef<>` - Weak reference (do not prevent garbage
+   collection).
+ * `JavaParamRef<>` - Use to accept any of the above as a parameter to a
+   function without creating a redundant registration.
+
+### Additional Guidelines / Advice
+
+Minimize the surface API between the two sides. Rather than calling multiple
+functions across boundaries, call only one (and then on the other side, call as
+many little functions as required).
+
+If a Java object "owns" a native one, store the pointer via
+`"long mNativeClassName"`. Ensure to eventually call a native method to delete
+the object. For example, have a `close()` that deletes the native object.
+
+The best way to pass "compound" types across in either direction is to
+create an inner class with PODs and a factory function. If possible, make mark
+all the fields as "final".
+
+## Build Rules
+
+ * `generate_jni` - Generates a header file with stubs for given `.java` files
+ * `generate_jar_jni` - Generates a header file with stubs for a given `.jar`
+   file
+ * `generate_jni_registration` - Generates a header file with functions to
+   register native-side JNI methods (required only when using crazy linker).
+
+Refer to [//build/config/android/rules.gni](https://cs.chromium.org/chromium/src/build/config/android/rules.gni)
+for more about the GN templates.
+
+## Changing `jni_generator`
+
+ * Python unit tests live in `jni_generator_tests.py`
+ * A working demo app exists as `//base/android/jni_generator:sample_jni_apk`
diff --git a/base/android/jni_generator/SampleForTests_jni.golden b/base/android/jni_generator/SampleForTests_jni.golden
new file mode 100644
index 0000000..c3fe968
--- /dev/null
+++ b/base/android/jni_generator/SampleForTests_jni.golden
@@ -0,0 +1,494 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+
+// This file is autogenerated by
+//     base/android/jni_generator/jni_generator.py
+// For
+//     org/chromium/example/jni_generator/SampleForTests
+
+#ifndef org_chromium_example_jni_generator_SampleForTests_JNI
+#define org_chromium_example_jni_generator_SampleForTests_JNI
+
+#include <jni.h>
+
+#include "base/android/jni_generator/jni_generator_helper.h"
+
+
+// Step 1: Forward declarations.
+
+JNI_REGISTRATION_EXPORT extern const char
+    kClassPath_org_chromium_example_jni_1generator_SampleForTests_00024InnerStructA[];
+const char kClassPath_org_chromium_example_jni_1generator_SampleForTests_00024InnerStructA[] =
+    "org/chromium/example/jni_generator/SampleForTests$InnerStructA";
+
+JNI_REGISTRATION_EXPORT extern const char
+    kClassPath_org_chromium_example_jni_1generator_SampleForTests_00024InnerClass[];
+const char kClassPath_org_chromium_example_jni_1generator_SampleForTests_00024InnerClass[] =
+    "org/chromium/example/jni_generator/SampleForTests$InnerClass";
+
+JNI_REGISTRATION_EXPORT extern const char
+    kClassPath_org_chromium_example_jni_1generator_SampleForTests[];
+const char kClassPath_org_chromium_example_jni_1generator_SampleForTests[] =
+    "org/chromium/example/jni_generator/SampleForTests";
+
+JNI_REGISTRATION_EXPORT extern const char
+    kClassPath_org_chromium_example_jni_1generator_SampleForTests_00024InnerStructB[];
+const char kClassPath_org_chromium_example_jni_1generator_SampleForTests_00024InnerStructB[] =
+    "org/chromium/example/jni_generator/SampleForTests$InnerStructB";
+// Leaking this jclass as we cannot use LazyInstance from some threads.
+JNI_REGISTRATION_EXPORT base::subtle::AtomicWord
+    g_org_chromium_example_jni_1generator_SampleForTests_00024InnerStructA_clazz = 0;
+#ifndef org_chromium_example_jni_1generator_SampleForTests_00024InnerStructA_clazz_defined
+#define org_chromium_example_jni_1generator_SampleForTests_00024InnerStructA_clazz_defined
+inline jclass org_chromium_example_jni_1generator_SampleForTests_00024InnerStructA_clazz(JNIEnv*
+    env) {
+  return base::android::LazyGetClass(env,
+      kClassPath_org_chromium_example_jni_1generator_SampleForTests_00024InnerStructA,
+      &g_org_chromium_example_jni_1generator_SampleForTests_00024InnerStructA_clazz);
+}
+#endif
+// Leaking this jclass as we cannot use LazyInstance from some threads.
+JNI_REGISTRATION_EXPORT base::subtle::AtomicWord
+    g_org_chromium_example_jni_1generator_SampleForTests_00024InnerClass_clazz = 0;
+#ifndef org_chromium_example_jni_1generator_SampleForTests_00024InnerClass_clazz_defined
+#define org_chromium_example_jni_1generator_SampleForTests_00024InnerClass_clazz_defined
+inline jclass org_chromium_example_jni_1generator_SampleForTests_00024InnerClass_clazz(JNIEnv* env)
+    {
+  return base::android::LazyGetClass(env,
+      kClassPath_org_chromium_example_jni_1generator_SampleForTests_00024InnerClass,
+      &g_org_chromium_example_jni_1generator_SampleForTests_00024InnerClass_clazz);
+}
+#endif
+// Leaking this jclass as we cannot use LazyInstance from some threads.
+JNI_REGISTRATION_EXPORT base::subtle::AtomicWord
+    g_org_chromium_example_jni_1generator_SampleForTests_clazz = 0;
+#ifndef org_chromium_example_jni_1generator_SampleForTests_clazz_defined
+#define org_chromium_example_jni_1generator_SampleForTests_clazz_defined
+inline jclass org_chromium_example_jni_1generator_SampleForTests_clazz(JNIEnv* env) {
+  return base::android::LazyGetClass(env,
+      kClassPath_org_chromium_example_jni_1generator_SampleForTests,
+      &g_org_chromium_example_jni_1generator_SampleForTests_clazz);
+}
+#endif
+// Leaking this jclass as we cannot use LazyInstance from some threads.
+JNI_REGISTRATION_EXPORT base::subtle::AtomicWord
+    g_org_chromium_example_jni_1generator_SampleForTests_00024InnerStructB_clazz = 0;
+#ifndef org_chromium_example_jni_1generator_SampleForTests_00024InnerStructB_clazz_defined
+#define org_chromium_example_jni_1generator_SampleForTests_00024InnerStructB_clazz_defined
+inline jclass org_chromium_example_jni_1generator_SampleForTests_00024InnerStructB_clazz(JNIEnv*
+    env) {
+  return base::android::LazyGetClass(env,
+      kClassPath_org_chromium_example_jni_1generator_SampleForTests_00024InnerStructB,
+      &g_org_chromium_example_jni_1generator_SampleForTests_00024InnerStructB_clazz);
+}
+#endif
+
+
+// Step 2: Constants (optional).
+
+
+// Step 3: Method stubs.
+namespace base {
+namespace android {
+
+static jlong JNI_SampleForTests_Init(JNIEnv* env, const base::android::JavaParamRef<jobject>&
+    jcaller,
+    const base::android::JavaParamRef<jstring>& param);
+
+JNI_GENERATOR_EXPORT jlong Java_org_chromium_example_jni_1generator_SampleForTests_nativeInit(
+    JNIEnv* env,
+    jobject jcaller,
+    jstring param) {
+  return JNI_SampleForTests_Init(env, base::android::JavaParamRef<jobject>(env, jcaller),
+      base::android::JavaParamRef<jstring>(env, param));
+}
+
+JNI_GENERATOR_EXPORT void Java_org_chromium_example_jni_1generator_SampleForTests_nativeDestroy(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeCPPClass) {
+  CPPClass* native = reinterpret_cast<CPPClass*>(nativeCPPClass);
+  CHECK_NATIVE_PTR(env, jcaller, native, "Destroy");
+  return native->Destroy(env, base::android::JavaParamRef<jobject>(env, jcaller));
+}
+
+static jdouble JNI_SampleForTests_GetDoubleFunction(JNIEnv* env, const
+    base::android::JavaParamRef<jobject>& jcaller);
+
+JNI_GENERATOR_EXPORT jdouble
+    Java_org_chromium_example_jni_1generator_SampleForTests_nativeGetDoubleFunction(
+    JNIEnv* env,
+    jobject jcaller) {
+  return JNI_SampleForTests_GetDoubleFunction(env, base::android::JavaParamRef<jobject>(env,
+      jcaller));
+}
+
+static jfloat JNI_SampleForTests_GetFloatFunction(JNIEnv* env, const
+    base::android::JavaParamRef<jclass>& jcaller);
+
+JNI_GENERATOR_EXPORT jfloat
+    Java_org_chromium_example_jni_1generator_SampleForTests_nativeGetFloatFunction(
+    JNIEnv* env,
+    jclass jcaller) {
+  return JNI_SampleForTests_GetFloatFunction(env, base::android::JavaParamRef<jclass>(env,
+      jcaller));
+}
+
+static void JNI_SampleForTests_SetNonPODDatatype(JNIEnv* env, const
+    base::android::JavaParamRef<jobject>& jcaller,
+    const base::android::JavaParamRef<jobject>& rect);
+
+JNI_GENERATOR_EXPORT void
+    Java_org_chromium_example_jni_1generator_SampleForTests_nativeSetNonPODDatatype(
+    JNIEnv* env,
+    jobject jcaller,
+    jobject rect) {
+  return JNI_SampleForTests_SetNonPODDatatype(env, base::android::JavaParamRef<jobject>(env,
+      jcaller), base::android::JavaParamRef<jobject>(env, rect));
+}
+
+static base::android::ScopedJavaLocalRef<jobject> JNI_SampleForTests_GetNonPODDatatype(JNIEnv* env,
+    const base::android::JavaParamRef<jobject>& jcaller);
+
+JNI_GENERATOR_EXPORT jobject
+    Java_org_chromium_example_jni_1generator_SampleForTests_nativeGetNonPODDatatype(
+    JNIEnv* env,
+    jobject jcaller) {
+  return JNI_SampleForTests_GetNonPODDatatype(env, base::android::JavaParamRef<jobject>(env,
+      jcaller)).Release();
+}
+
+JNI_GENERATOR_EXPORT jint Java_org_chromium_example_jni_1generator_SampleForTests_nativeMethod(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeCPPClass) {
+  CPPClass* native = reinterpret_cast<CPPClass*>(nativeCPPClass);
+  CHECK_NATIVE_PTR(env, jcaller, native, "Method", 0);
+  return native->Method(env, base::android::JavaParamRef<jobject>(env, jcaller));
+}
+
+JNI_GENERATOR_EXPORT jdouble
+    Java_org_chromium_example_jni_1generator_SampleForTests_nativeMethodOtherP0(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativePtr) {
+  CPPClass::InnerClass* native = reinterpret_cast<CPPClass::InnerClass*>(nativePtr);
+  CHECK_NATIVE_PTR(env, jcaller, native, "MethodOtherP0", 0);
+  return native->MethodOtherP0(env, base::android::JavaParamRef<jobject>(env, jcaller));
+}
+
+JNI_GENERATOR_EXPORT void Java_org_chromium_example_jni_1generator_SampleForTests_nativeAddStructB(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeCPPClass,
+    jobject b) {
+  CPPClass* native = reinterpret_cast<CPPClass*>(nativeCPPClass);
+  CHECK_NATIVE_PTR(env, jcaller, native, "AddStructB");
+  return native->AddStructB(env, base::android::JavaParamRef<jobject>(env, jcaller),
+      base::android::JavaParamRef<jobject>(env, b));
+}
+
+JNI_GENERATOR_EXPORT void
+    Java_org_chromium_example_jni_1generator_SampleForTests_nativeIterateAndDoSomethingWithStructB(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeCPPClass) {
+  CPPClass* native = reinterpret_cast<CPPClass*>(nativeCPPClass);
+  CHECK_NATIVE_PTR(env, jcaller, native, "IterateAndDoSomethingWithStructB");
+  return native->IterateAndDoSomethingWithStructB(env, base::android::JavaParamRef<jobject>(env,
+      jcaller));
+}
+
+JNI_GENERATOR_EXPORT jstring
+    Java_org_chromium_example_jni_1generator_SampleForTests_nativeReturnAString(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeCPPClass) {
+  CPPClass* native = reinterpret_cast<CPPClass*>(nativeCPPClass);
+  CHECK_NATIVE_PTR(env, jcaller, native, "ReturnAString", NULL);
+  return native->ReturnAString(env, base::android::JavaParamRef<jobject>(env, jcaller)).Release();
+}
+
+static jint JNI_InnerClass_GetInnerIntFunction(JNIEnv* env, const
+    base::android::JavaParamRef<jclass>& jcaller);
+
+JNI_GENERATOR_EXPORT jint
+    Java_org_chromium_example_jni_1generator_SampleForTests_00024InnerClass_nativeGetInnerIntFunction(
+    JNIEnv* env,
+    jclass jcaller) {
+  return JNI_InnerClass_GetInnerIntFunction(env, base::android::JavaParamRef<jclass>(env, jcaller));
+}
+
+
+static base::subtle::AtomicWord g_org_chromium_example_jni_1generator_SampleForTests_javaMethod = 0;
+static jint Java_SampleForTests_javaMethod(JNIEnv* env, const base::android::JavaRef<jobject>& obj,
+    JniIntWrapper foo,
+    JniIntWrapper bar) {
+  CHECK_CLAZZ(env, obj.obj(),
+      org_chromium_example_jni_1generator_SampleForTests_clazz(env), 0);
+  jmethodID method_id = base::android::MethodID::LazyGet<
+      base::android::MethodID::TYPE_INSTANCE>(
+          env, org_chromium_example_jni_1generator_SampleForTests_clazz(env),
+          "javaMethod",
+          "(II)I",
+          &g_org_chromium_example_jni_1generator_SampleForTests_javaMethod);
+
+  jint ret =
+      env->CallIntMethod(obj.obj(),
+          method_id, as_jint(foo), as_jint(bar));
+  jni_generator::CheckException(env);
+  return ret;
+}
+
+static base::subtle::AtomicWord
+    g_org_chromium_example_jni_1generator_SampleForTests_staticJavaMethod = 0;
+static jboolean Java_SampleForTests_staticJavaMethod(JNIEnv* env) {
+  CHECK_CLAZZ(env, org_chromium_example_jni_1generator_SampleForTests_clazz(env),
+      org_chromium_example_jni_1generator_SampleForTests_clazz(env), false);
+  jmethodID method_id = base::android::MethodID::LazyGet<
+      base::android::MethodID::TYPE_STATIC>(
+          env, org_chromium_example_jni_1generator_SampleForTests_clazz(env),
+          "staticJavaMethod",
+          "()Z",
+          &g_org_chromium_example_jni_1generator_SampleForTests_staticJavaMethod);
+
+  jboolean ret =
+      env->CallStaticBooleanMethod(org_chromium_example_jni_1generator_SampleForTests_clazz(env),
+          method_id);
+  jni_generator::CheckException(env);
+  return ret;
+}
+
+static base::subtle::AtomicWord
+    g_org_chromium_example_jni_1generator_SampleForTests_packagePrivateJavaMethod = 0;
+static void Java_SampleForTests_packagePrivateJavaMethod(JNIEnv* env, const
+    base::android::JavaRef<jobject>& obj) {
+  CHECK_CLAZZ(env, obj.obj(),
+      org_chromium_example_jni_1generator_SampleForTests_clazz(env));
+  jmethodID method_id = base::android::MethodID::LazyGet<
+      base::android::MethodID::TYPE_INSTANCE>(
+          env, org_chromium_example_jni_1generator_SampleForTests_clazz(env),
+          "packagePrivateJavaMethod",
+          "()V",
+          &g_org_chromium_example_jni_1generator_SampleForTests_packagePrivateJavaMethod);
+
+     env->CallVoidMethod(obj.obj(),
+          method_id);
+  jni_generator::CheckException(env);
+}
+
+static base::subtle::AtomicWord
+    g_org_chromium_example_jni_1generator_SampleForTests_methodWithGenericParams = 0;
+static void Java_SampleForTests_methodWithGenericParams(JNIEnv* env, const
+    base::android::JavaRef<jobject>& obj, const base::android::JavaRef<jobject>& foo,
+    const base::android::JavaRef<jobject>& bar) {
+  CHECK_CLAZZ(env, obj.obj(),
+      org_chromium_example_jni_1generator_SampleForTests_clazz(env));
+  jmethodID method_id = base::android::MethodID::LazyGet<
+      base::android::MethodID::TYPE_INSTANCE>(
+          env, org_chromium_example_jni_1generator_SampleForTests_clazz(env),
+          "methodWithGenericParams",
+          "(Ljava/util/Map;Ljava/util/LinkedList;)V",
+          &g_org_chromium_example_jni_1generator_SampleForTests_methodWithGenericParams);
+
+     env->CallVoidMethod(obj.obj(),
+          method_id, foo.obj(), bar.obj());
+  jni_generator::CheckException(env);
+}
+
+static base::subtle::AtomicWord g_org_chromium_example_jni_1generator_SampleForTests_Constructor =
+    0;
+static base::android::ScopedJavaLocalRef<jobject> Java_SampleForTests_Constructor(JNIEnv* env,
+    JniIntWrapper foo,
+    JniIntWrapper bar) {
+  CHECK_CLAZZ(env, org_chromium_example_jni_1generator_SampleForTests_clazz(env),
+      org_chromium_example_jni_1generator_SampleForTests_clazz(env), NULL);
+  jmethodID method_id = base::android::MethodID::LazyGet<
+      base::android::MethodID::TYPE_INSTANCE>(
+          env, org_chromium_example_jni_1generator_SampleForTests_clazz(env),
+          "<init>",
+          "(II)V",
+          &g_org_chromium_example_jni_1generator_SampleForTests_Constructor);
+
+  jobject ret =
+      env->NewObject(org_chromium_example_jni_1generator_SampleForTests_clazz(env),
+          method_id, as_jint(foo), as_jint(bar));
+  jni_generator::CheckException(env);
+  return base::android::ScopedJavaLocalRef<jobject>(env, ret);
+}
+
+static base::subtle::AtomicWord
+    g_org_chromium_example_jni_1generator_SampleForTests_methodThatThrowsException = 0;
+static void Java_SampleForTests_methodThatThrowsException(JNIEnv* env, const
+    base::android::JavaRef<jobject>& obj) {
+  CHECK_CLAZZ(env, obj.obj(),
+      org_chromium_example_jni_1generator_SampleForTests_clazz(env));
+  jmethodID method_id = base::android::MethodID::LazyGet<
+      base::android::MethodID::TYPE_INSTANCE>(
+          env, org_chromium_example_jni_1generator_SampleForTests_clazz(env),
+          "methodThatThrowsException",
+          "()V",
+          &g_org_chromium_example_jni_1generator_SampleForTests_methodThatThrowsException);
+
+     env->CallVoidMethod(obj.obj(),
+          method_id);
+}
+
+static base::subtle::AtomicWord
+    g_org_chromium_example_jni_1generator_SampleForTests_javaMethodWithAnnotatedParam = 0;
+static void Java_SampleForTests_javaMethodWithAnnotatedParam(JNIEnv* env, const
+    base::android::JavaRef<jobject>& obj, JniIntWrapper foo) {
+  CHECK_CLAZZ(env, obj.obj(),
+      org_chromium_example_jni_1generator_SampleForTests_clazz(env));
+  jmethodID method_id = base::android::MethodID::LazyGet<
+      base::android::MethodID::TYPE_INSTANCE>(
+          env, org_chromium_example_jni_1generator_SampleForTests_clazz(env),
+          "javaMethodWithAnnotatedParam",
+          "(I)V",
+          &g_org_chromium_example_jni_1generator_SampleForTests_javaMethodWithAnnotatedParam);
+
+     env->CallVoidMethod(obj.obj(),
+          method_id, as_jint(foo));
+  jni_generator::CheckException(env);
+}
+
+static base::subtle::AtomicWord
+    g_org_chromium_example_jni_1generator_SampleForTests_00024InnerStructA_create = 0;
+static base::android::ScopedJavaLocalRef<jobject> Java_InnerStructA_create(JNIEnv* env, jlong l,
+    JniIntWrapper i,
+    const base::android::JavaRef<jstring>& s) {
+  CHECK_CLAZZ(env, org_chromium_example_jni_1generator_SampleForTests_00024InnerStructA_clazz(env),
+      org_chromium_example_jni_1generator_SampleForTests_00024InnerStructA_clazz(env), NULL);
+  jmethodID method_id = base::android::MethodID::LazyGet<
+      base::android::MethodID::TYPE_STATIC>(
+          env, org_chromium_example_jni_1generator_SampleForTests_00024InnerStructA_clazz(env),
+          "create",
+          "(JILjava/lang/String;)Lorg/chromium/example/jni_generator/SampleForTests$InnerStructA;",
+          &g_org_chromium_example_jni_1generator_SampleForTests_00024InnerStructA_create);
+
+  jobject ret =
+env->CallStaticObjectMethod(org_chromium_example_jni_1generator_SampleForTests_00024InnerStructA_clazz(env),
+          method_id, l, as_jint(i), s.obj());
+  jni_generator::CheckException(env);
+  return base::android::ScopedJavaLocalRef<jobject>(env, ret);
+}
+
+static base::subtle::AtomicWord g_org_chromium_example_jni_1generator_SampleForTests_addStructA = 0;
+static void Java_SampleForTests_addStructA(JNIEnv* env, const base::android::JavaRef<jobject>& obj,
+    const base::android::JavaRef<jobject>& a) {
+  CHECK_CLAZZ(env, obj.obj(),
+      org_chromium_example_jni_1generator_SampleForTests_clazz(env));
+  jmethodID method_id = base::android::MethodID::LazyGet<
+      base::android::MethodID::TYPE_INSTANCE>(
+          env, org_chromium_example_jni_1generator_SampleForTests_clazz(env),
+          "addStructA",
+          "(Lorg/chromium/example/jni_generator/SampleForTests$InnerStructA;)V",
+          &g_org_chromium_example_jni_1generator_SampleForTests_addStructA);
+
+     env->CallVoidMethod(obj.obj(),
+          method_id, a.obj());
+  jni_generator::CheckException(env);
+}
+
+static base::subtle::AtomicWord
+    g_org_chromium_example_jni_1generator_SampleForTests_iterateAndDoSomething = 0;
+static void Java_SampleForTests_iterateAndDoSomething(JNIEnv* env, const
+    base::android::JavaRef<jobject>& obj) {
+  CHECK_CLAZZ(env, obj.obj(),
+      org_chromium_example_jni_1generator_SampleForTests_clazz(env));
+  jmethodID method_id = base::android::MethodID::LazyGet<
+      base::android::MethodID::TYPE_INSTANCE>(
+          env, org_chromium_example_jni_1generator_SampleForTests_clazz(env),
+          "iterateAndDoSomething",
+          "()V",
+          &g_org_chromium_example_jni_1generator_SampleForTests_iterateAndDoSomething);
+
+     env->CallVoidMethod(obj.obj(),
+          method_id);
+  jni_generator::CheckException(env);
+}
+
+static base::subtle::AtomicWord
+    g_org_chromium_example_jni_1generator_SampleForTests_00024InnerStructB_getKey = 0;
+static jlong Java_InnerStructB_getKey(JNIEnv* env, const base::android::JavaRef<jobject>& obj) {
+  CHECK_CLAZZ(env, obj.obj(),
+      org_chromium_example_jni_1generator_SampleForTests_00024InnerStructB_clazz(env), 0);
+  jmethodID method_id = base::android::MethodID::LazyGet<
+      base::android::MethodID::TYPE_INSTANCE>(
+          env, org_chromium_example_jni_1generator_SampleForTests_00024InnerStructB_clazz(env),
+          "getKey",
+          "()J",
+          &g_org_chromium_example_jni_1generator_SampleForTests_00024InnerStructB_getKey);
+
+  jlong ret =
+      env->CallLongMethod(obj.obj(),
+          method_id);
+  jni_generator::CheckException(env);
+  return ret;
+}
+
+static base::subtle::AtomicWord
+    g_org_chromium_example_jni_1generator_SampleForTests_00024InnerStructB_getValue = 0;
+static base::android::ScopedJavaLocalRef<jstring> Java_InnerStructB_getValue(JNIEnv* env, const
+    base::android::JavaRef<jobject>& obj) {
+  CHECK_CLAZZ(env, obj.obj(),
+      org_chromium_example_jni_1generator_SampleForTests_00024InnerStructB_clazz(env), NULL);
+  jmethodID method_id = base::android::MethodID::LazyGet<
+      base::android::MethodID::TYPE_INSTANCE>(
+          env, org_chromium_example_jni_1generator_SampleForTests_00024InnerStructB_clazz(env),
+          "getValue",
+          "()Ljava/lang/String;",
+          &g_org_chromium_example_jni_1generator_SampleForTests_00024InnerStructB_getValue);
+
+  jstring ret =
+      static_cast<jstring>(env->CallObjectMethod(obj.obj(),
+          method_id));
+  jni_generator::CheckException(env);
+  return base::android::ScopedJavaLocalRef<jstring>(env, ret);
+}
+
+static base::subtle::AtomicWord
+    g_org_chromium_example_jni_1generator_SampleForTests_getInnerInterface = 0;
+static base::android::ScopedJavaLocalRef<jobject> Java_SampleForTests_getInnerInterface(JNIEnv* env)
+    {
+  CHECK_CLAZZ(env, org_chromium_example_jni_1generator_SampleForTests_clazz(env),
+      org_chromium_example_jni_1generator_SampleForTests_clazz(env), NULL);
+  jmethodID method_id = base::android::MethodID::LazyGet<
+      base::android::MethodID::TYPE_STATIC>(
+          env, org_chromium_example_jni_1generator_SampleForTests_clazz(env),
+          "getInnerInterface",
+          "()Lorg/chromium/example/jni_generator/SampleForTests$InnerInterface;",
+          &g_org_chromium_example_jni_1generator_SampleForTests_getInnerInterface);
+
+  jobject ret =
+      env->CallStaticObjectMethod(org_chromium_example_jni_1generator_SampleForTests_clazz(env),
+          method_id);
+  jni_generator::CheckException(env);
+  return base::android::ScopedJavaLocalRef<jobject>(env, ret);
+}
+
+static base::subtle::AtomicWord g_org_chromium_example_jni_1generator_SampleForTests_getInnerEnum =
+    0;
+static base::android::ScopedJavaLocalRef<jobject> Java_SampleForTests_getInnerEnum(JNIEnv* env) {
+  CHECK_CLAZZ(env, org_chromium_example_jni_1generator_SampleForTests_clazz(env),
+      org_chromium_example_jni_1generator_SampleForTests_clazz(env), NULL);
+  jmethodID method_id = base::android::MethodID::LazyGet<
+      base::android::MethodID::TYPE_STATIC>(
+          env, org_chromium_example_jni_1generator_SampleForTests_clazz(env),
+          "getInnerEnum",
+          "()Lorg/chromium/example/jni_generator/SampleForTests$InnerEnum;",
+          &g_org_chromium_example_jni_1generator_SampleForTests_getInnerEnum);
+
+  jobject ret =
+      env->CallStaticObjectMethod(org_chromium_example_jni_1generator_SampleForTests_clazz(env),
+          method_id);
+  jni_generator::CheckException(env);
+  return base::android::ScopedJavaLocalRef<jobject>(env, ret);
+}
+
+}  // namespace android
+}  // namespace base
+
+#endif  // org_chromium_example_jni_generator_SampleForTests_JNI
diff --git a/base/android/jni_generator/testNativeExportsOnlyOption.golden b/base/android/jni_generator/testNativeExportsOnlyOption.golden
new file mode 100644
index 0000000..bdc8d29
--- /dev/null
+++ b/base/android/jni_generator/testNativeExportsOnlyOption.golden
@@ -0,0 +1,214 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+
+// This file is autogenerated by
+//     base/android/jni_generator/jni_generator.py
+// For
+//     org/chromium/example/jni_generator/SampleForTests
+
+#ifndef org_chromium_example_jni_generator_SampleForTests_JNI
+#define org_chromium_example_jni_generator_SampleForTests_JNI
+
+#include <jni.h>
+
+#include "base/android/jni_generator/jni_generator_helper.h"
+
+
+// Step 1: Forward declarations.
+
+JNI_REGISTRATION_EXPORT extern const char
+    kClassPath_org_chromium_example_jni_1generator_SampleForTests_00024MyOtherInnerClass[];
+const char kClassPath_org_chromium_example_jni_1generator_SampleForTests_00024MyOtherInnerClass[] =
+    "org/chromium/example/jni_generator/SampleForTests$MyOtherInnerClass";
+
+JNI_REGISTRATION_EXPORT extern const char
+    kClassPath_org_chromium_example_jni_1generator_SampleForTests_00024MyInnerClass[];
+const char kClassPath_org_chromium_example_jni_1generator_SampleForTests_00024MyInnerClass[] =
+    "org/chromium/example/jni_generator/SampleForTests$MyInnerClass";
+
+JNI_REGISTRATION_EXPORT extern const char
+    kClassPath_org_chromium_example_jni_1generator_SampleForTests[];
+const char kClassPath_org_chromium_example_jni_1generator_SampleForTests[] =
+    "org/chromium/example/jni_generator/SampleForTests";
+// Leaking this jclass as we cannot use LazyInstance from some threads.
+JNI_REGISTRATION_EXPORT base::subtle::AtomicWord
+    g_org_chromium_example_jni_1generator_SampleForTests_00024MyOtherInnerClass_clazz = 0;
+#ifndef org_chromium_example_jni_1generator_SampleForTests_00024MyOtherInnerClass_clazz_defined
+#define org_chromium_example_jni_1generator_SampleForTests_00024MyOtherInnerClass_clazz_defined
+inline jclass
+    org_chromium_example_jni_1generator_SampleForTests_00024MyOtherInnerClass_clazz(JNIEnv* env) {
+  return base::android::LazyGetClass(env,
+      kClassPath_org_chromium_example_jni_1generator_SampleForTests_00024MyOtherInnerClass,
+      &g_org_chromium_example_jni_1generator_SampleForTests_00024MyOtherInnerClass_clazz);
+}
+#endif
+// Leaking this jclass as we cannot use LazyInstance from some threads.
+JNI_REGISTRATION_EXPORT base::subtle::AtomicWord
+    g_org_chromium_example_jni_1generator_SampleForTests_00024MyInnerClass_clazz = 0;
+#ifndef org_chromium_example_jni_1generator_SampleForTests_00024MyInnerClass_clazz_defined
+#define org_chromium_example_jni_1generator_SampleForTests_00024MyInnerClass_clazz_defined
+inline jclass org_chromium_example_jni_1generator_SampleForTests_00024MyInnerClass_clazz(JNIEnv*
+    env) {
+  return base::android::LazyGetClass(env,
+      kClassPath_org_chromium_example_jni_1generator_SampleForTests_00024MyInnerClass,
+      &g_org_chromium_example_jni_1generator_SampleForTests_00024MyInnerClass_clazz);
+}
+#endif
+// Leaking this jclass as we cannot use LazyInstance from some threads.
+JNI_REGISTRATION_EXPORT base::subtle::AtomicWord
+    g_org_chromium_example_jni_1generator_SampleForTests_clazz = 0;
+#ifndef org_chromium_example_jni_1generator_SampleForTests_clazz_defined
+#define org_chromium_example_jni_1generator_SampleForTests_clazz_defined
+inline jclass org_chromium_example_jni_1generator_SampleForTests_clazz(JNIEnv* env) {
+  return base::android::LazyGetClass(env,
+      kClassPath_org_chromium_example_jni_1generator_SampleForTests,
+      &g_org_chromium_example_jni_1generator_SampleForTests_clazz);
+}
+#endif
+
+
+// Step 2: Constants (optional).
+
+
+// Step 3: Method stubs.
+JNI_GENERATOR_EXPORT jint
+    Java_org_chromium_example_jni_1generator_SampleForTests_nativeStaticMethod(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeTest,
+    jint arg1) {
+  Test* native = reinterpret_cast<Test*>(nativeTest);
+  CHECK_NATIVE_PTR(env, jcaller, native, "StaticMethod", 0);
+  return native->StaticMethod(env, base::android::JavaParamRef<jobject>(env, jcaller), arg1);
+}
+
+JNI_GENERATOR_EXPORT jint Java_org_chromium_example_jni_1generator_SampleForTests_nativeMethod(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeTest,
+    jint arg1) {
+  Test* native = reinterpret_cast<Test*>(nativeTest);
+  CHECK_NATIVE_PTR(env, jcaller, native, "Method", 0);
+  return native->Method(env, base::android::JavaParamRef<jobject>(env, jcaller), arg1);
+}
+
+static jint JNI_MyInnerClass_Init(JNIEnv* env, const base::android::JavaParamRef<jobject>& jcaller);
+
+JNI_GENERATOR_EXPORT jint
+    Java_org_chromium_example_jni_1generator_SampleForTests_00024MyInnerClass_nativeInit(
+    JNIEnv* env,
+    jobject jcaller) {
+  return JNI_MyInnerClass_Init(env, base::android::JavaParamRef<jobject>(env, jcaller));
+}
+
+static jint JNI_MyOtherInnerClass_Init(JNIEnv* env, const base::android::JavaParamRef<jobject>&
+    jcaller);
+
+JNI_GENERATOR_EXPORT jint
+    Java_org_chromium_example_jni_1generator_SampleForTests_00024MyOtherInnerClass_nativeInit(
+    JNIEnv* env,
+    jobject jcaller) {
+  return JNI_MyOtherInnerClass_Init(env, base::android::JavaParamRef<jobject>(env, jcaller));
+}
+
+
+static base::subtle::AtomicWord
+    g_org_chromium_example_jni_1generator_SampleForTests_testMethodWithParam = 0;
+static void Java_SampleForTests_testMethodWithParam(JNIEnv* env, const
+    base::android::JavaRef<jobject>& obj, JniIntWrapper iParam) {
+  CHECK_CLAZZ(env, obj.obj(),
+      org_chromium_example_jni_1generator_SampleForTests_clazz(env));
+  jmethodID method_id = base::android::MethodID::LazyGet<
+      base::android::MethodID::TYPE_INSTANCE>(
+          env, org_chromium_example_jni_1generator_SampleForTests_clazz(env),
+          "testMethodWithParam",
+          "(I)V",
+          &g_org_chromium_example_jni_1generator_SampleForTests_testMethodWithParam);
+
+     env->CallVoidMethod(obj.obj(),
+          method_id, as_jint(iParam));
+  jni_generator::CheckException(env);
+}
+
+static base::subtle::AtomicWord
+    g_org_chromium_example_jni_1generator_SampleForTests_testMethodWithParamAndReturn = 0;
+static base::android::ScopedJavaLocalRef<jstring>
+    Java_SampleForTests_testMethodWithParamAndReturn(JNIEnv* env, const
+    base::android::JavaRef<jobject>& obj, JniIntWrapper iParam) {
+  CHECK_CLAZZ(env, obj.obj(),
+      org_chromium_example_jni_1generator_SampleForTests_clazz(env), NULL);
+  jmethodID method_id = base::android::MethodID::LazyGet<
+      base::android::MethodID::TYPE_INSTANCE>(
+          env, org_chromium_example_jni_1generator_SampleForTests_clazz(env),
+          "testMethodWithParamAndReturn",
+          "(I)Ljava/lang/String;",
+          &g_org_chromium_example_jni_1generator_SampleForTests_testMethodWithParamAndReturn);
+
+  jstring ret =
+      static_cast<jstring>(env->CallObjectMethod(obj.obj(),
+          method_id, as_jint(iParam)));
+  jni_generator::CheckException(env);
+  return base::android::ScopedJavaLocalRef<jstring>(env, ret);
+}
+
+static base::subtle::AtomicWord
+    g_org_chromium_example_jni_1generator_SampleForTests_testStaticMethodWithParam = 0;
+static jint Java_SampleForTests_testStaticMethodWithParam(JNIEnv* env, JniIntWrapper iParam) {
+  CHECK_CLAZZ(env, org_chromium_example_jni_1generator_SampleForTests_clazz(env),
+      org_chromium_example_jni_1generator_SampleForTests_clazz(env), 0);
+  jmethodID method_id = base::android::MethodID::LazyGet<
+      base::android::MethodID::TYPE_STATIC>(
+          env, org_chromium_example_jni_1generator_SampleForTests_clazz(env),
+          "testStaticMethodWithParam",
+          "(I)I",
+          &g_org_chromium_example_jni_1generator_SampleForTests_testStaticMethodWithParam);
+
+  jint ret =
+      env->CallStaticIntMethod(org_chromium_example_jni_1generator_SampleForTests_clazz(env),
+          method_id, as_jint(iParam));
+  jni_generator::CheckException(env);
+  return ret;
+}
+
+static base::subtle::AtomicWord
+    g_org_chromium_example_jni_1generator_SampleForTests_testMethodWithNoParam = 0;
+static jdouble Java_SampleForTests_testMethodWithNoParam(JNIEnv* env) {
+  CHECK_CLAZZ(env, org_chromium_example_jni_1generator_SampleForTests_clazz(env),
+      org_chromium_example_jni_1generator_SampleForTests_clazz(env), 0);
+  jmethodID method_id = base::android::MethodID::LazyGet<
+      base::android::MethodID::TYPE_STATIC>(
+          env, org_chromium_example_jni_1generator_SampleForTests_clazz(env),
+          "testMethodWithNoParam",
+          "()D",
+          &g_org_chromium_example_jni_1generator_SampleForTests_testMethodWithNoParam);
+
+  jdouble ret =
+      env->CallStaticDoubleMethod(org_chromium_example_jni_1generator_SampleForTests_clazz(env),
+          method_id);
+  jni_generator::CheckException(env);
+  return ret;
+}
+
+static base::subtle::AtomicWord
+    g_org_chromium_example_jni_1generator_SampleForTests_testStaticMethodWithNoParam = 0;
+static base::android::ScopedJavaLocalRef<jstring>
+    Java_SampleForTests_testStaticMethodWithNoParam(JNIEnv* env) {
+  CHECK_CLAZZ(env, org_chromium_example_jni_1generator_SampleForTests_clazz(env),
+      org_chromium_example_jni_1generator_SampleForTests_clazz(env), NULL);
+  jmethodID method_id = base::android::MethodID::LazyGet<
+      base::android::MethodID::TYPE_STATIC>(
+          env, org_chromium_example_jni_1generator_SampleForTests_clazz(env),
+          "testStaticMethodWithNoParam",
+          "()Ljava/lang/String;",
+          &g_org_chromium_example_jni_1generator_SampleForTests_testStaticMethodWithNoParam);
+
+  jstring ret =
+static_cast<jstring>(env->CallStaticObjectMethod(org_chromium_example_jni_1generator_SampleForTests_clazz(env),
+          method_id));
+  jni_generator::CheckException(env);
+  return base::android::ScopedJavaLocalRef<jstring>(env, ret);
+}
+
+#endif  // org_chromium_example_jni_generator_SampleForTests_JNI
diff --git a/base/android/jni_registrar.cc b/base/android/jni_registrar.cc
new file mode 100644
index 0000000..8e13e60
--- /dev/null
+++ b/base/android/jni_registrar.cc
@@ -0,0 +1,30 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/android/jni_registrar.h"
+
+#include "base/logging.h"
+#include "base/android/jni_android.h"
+#include "base/trace_event/trace_event.h"
+
+namespace base {
+namespace android {
+
+bool RegisterNativeMethods(JNIEnv* env,
+                           const RegistrationMethod* method,
+                           size_t count) {
+  TRACE_EVENT0("startup", "base_android::RegisterNativeMethods")
+  const RegistrationMethod* end = method + count;
+  while (method != end) {
+    if (!method->func(env)) {
+      DLOG(ERROR) << method->name << " failed registration!";
+      return false;
+    }
+    method++;
+  }
+  return true;
+}
+
+}  // namespace android
+}  // namespace base
diff --git a/base/android/jni_registrar.h b/base/android/jni_registrar.h
new file mode 100644
index 0000000..31a4750
--- /dev/null
+++ b/base/android/jni_registrar.h
@@ -0,0 +1,28 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_ANDROID_JNI_REGISTRAR_H_
+#define BASE_ANDROID_JNI_REGISTRAR_H_
+
+#include <jni.h>
+#include <stddef.h>
+
+#include "base/base_export.h"
+
+namespace base {
+namespace android {
+
+struct RegistrationMethod;
+
+// Registers the JNI bindings for the specified |method| definition containing
+// |count| elements.  Returns whether the registration of the given methods
+// succeeded.
+BASE_EXPORT bool RegisterNativeMethods(JNIEnv* env,
+                                       const RegistrationMethod* method,
+                                       size_t count);
+
+}  // namespace android
+}  // namespace base
+
+#endif  // BASE_ANDROID_JNI_REGISTRAR_H_
diff --git a/base/android/jni_utils.cc b/base/android/jni_utils.cc
new file mode 100644
index 0000000..c5e370c
--- /dev/null
+++ b/base/android/jni_utils.cc
@@ -0,0 +1,24 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/android/jni_utils.h"
+
+#include "base/android/scoped_java_ref.h"
+
+#include "jni/JNIUtils_jni.h"
+
+namespace base {
+namespace android {
+
+ScopedJavaLocalRef<jobject> GetClassLoader(JNIEnv* env) {
+  return Java_JNIUtils_getClassLoader(env);
+}
+
+bool IsSelectiveJniRegistrationEnabled(JNIEnv* env) {
+  return Java_JNIUtils_isSelectiveJniRegistrationEnabled(env);
+}
+
+}  // namespace android
+}  // namespace base
+
diff --git a/base/android/jni_utils.h b/base/android/jni_utils.h
new file mode 100644
index 0000000..c626ba4
--- /dev/null
+++ b/base/android/jni_utils.h
@@ -0,0 +1,28 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_ANDROID_JNI_UTILS_H_
+#define BASE_ANDROID_JNI_UTILS_H_
+
+#include <jni.h>
+
+#include "base/android/scoped_java_ref.h"
+
+namespace base {
+
+namespace android {
+
+// Gets a ClassLoader instance capable of loading Chromium java classes.
+// This should be called either from JNI_OnLoad or from within a method called
+// via JNI from Java.
+BASE_EXPORT ScopedJavaLocalRef<jobject> GetClassLoader(JNIEnv* env);
+
+// Returns true if the current process permits selective JNI registration.
+BASE_EXPORT bool IsSelectiveJniRegistrationEnabled(JNIEnv* env);
+
+}  // namespace android
+}  // namespace base
+
+#endif  // BASE_ANDROID_JNI_UTILS_H_
+
diff --git a/base/android/jni_weak_ref.cc b/base/android/jni_weak_ref.cc
new file mode 100644
index 0000000..88efa72
--- /dev/null
+++ b/base/android/jni_weak_ref.cc
@@ -0,0 +1,79 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/android/jni_weak_ref.h"
+
+#include <utility>
+
+#include "base/android/jni_android.h"
+#include "base/logging.h"
+
+using base::android::AttachCurrentThread;
+
+JavaObjectWeakGlobalRef::JavaObjectWeakGlobalRef() : obj_(nullptr) {}
+
+JavaObjectWeakGlobalRef::JavaObjectWeakGlobalRef(
+    const JavaObjectWeakGlobalRef& orig)
+    : obj_(nullptr) {
+  Assign(orig);
+}
+
+JavaObjectWeakGlobalRef::JavaObjectWeakGlobalRef(
+    JavaObjectWeakGlobalRef&& orig) noexcept
+    : obj_(orig.obj_) {
+  orig.obj_ = nullptr;
+}
+
+JavaObjectWeakGlobalRef::JavaObjectWeakGlobalRef(JNIEnv* env, jobject obj)
+    : obj_(env->NewWeakGlobalRef(obj)) {
+}
+
+JavaObjectWeakGlobalRef::JavaObjectWeakGlobalRef(
+    JNIEnv* env,
+    const base::android::JavaRef<jobject>& obj)
+    : obj_(env->NewWeakGlobalRef(obj.obj())) {
+}
+
+JavaObjectWeakGlobalRef::~JavaObjectWeakGlobalRef() {
+  reset();
+}
+
+void JavaObjectWeakGlobalRef::operator=(const JavaObjectWeakGlobalRef& rhs) {
+  Assign(rhs);
+}
+
+void JavaObjectWeakGlobalRef::operator=(JavaObjectWeakGlobalRef&& rhs) {
+  std::swap(obj_, rhs.obj_);
+}
+
+void JavaObjectWeakGlobalRef::reset() {
+  if (obj_) {
+    AttachCurrentThread()->DeleteWeakGlobalRef(obj_);
+    obj_ = nullptr;
+  }
+}
+
+base::android::ScopedJavaLocalRef<jobject>
+    JavaObjectWeakGlobalRef::get(JNIEnv* env) const {
+  return GetRealObject(env, obj_);
+}
+
+base::android::ScopedJavaLocalRef<jobject> GetRealObject(
+    JNIEnv* env, jweak obj) {
+  jobject real = nullptr;
+  if (obj)
+    real = env->NewLocalRef(obj);
+  return base::android::ScopedJavaLocalRef<jobject>(env, real);
+}
+
+void JavaObjectWeakGlobalRef::Assign(const JavaObjectWeakGlobalRef& other) {
+  if (&other == this)
+    return;
+
+  JNIEnv* env = AttachCurrentThread();
+  if (obj_)
+    env->DeleteWeakGlobalRef(obj_);
+
+  obj_ = other.obj_ ? env->NewWeakGlobalRef(other.obj_) : nullptr;
+}
diff --git a/base/android/jni_weak_ref.h b/base/android/jni_weak_ref.h
new file mode 100644
index 0000000..43a26b5
--- /dev/null
+++ b/base/android/jni_weak_ref.h
@@ -0,0 +1,51 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_ANDROID_JNI_WEAK_REF_H_
+#define BASE_ANDROID_JNI_WEAK_REF_H_
+
+#include <jni.h>
+
+#include "base/android/scoped_java_ref.h"
+#include "base/base_export.h"
+
+// Manages WeakGlobalRef lifecycle.
+// This class is not thread-safe w.r.t. get() and reset(). Multiple threads may
+// safely use get() concurrently, but if the user calls reset() (or of course,
+// calls the destructor) they'll need to provide their own synchronization.
+class BASE_EXPORT JavaObjectWeakGlobalRef {
+ public:
+  JavaObjectWeakGlobalRef();
+  JavaObjectWeakGlobalRef(const JavaObjectWeakGlobalRef& orig);
+  JavaObjectWeakGlobalRef(JavaObjectWeakGlobalRef&& orig) noexcept;
+  JavaObjectWeakGlobalRef(JNIEnv* env, jobject obj);
+  JavaObjectWeakGlobalRef(JNIEnv* env,
+                          const base::android::JavaRef<jobject>& obj);
+  virtual ~JavaObjectWeakGlobalRef();
+
+  void operator=(const JavaObjectWeakGlobalRef& rhs);
+  void operator=(JavaObjectWeakGlobalRef&& rhs);
+
+  base::android::ScopedJavaLocalRef<jobject> get(JNIEnv* env) const;
+
+  // Returns true if the weak reference has not been initialized to point at
+  // an object (or ḣas had reset() called).
+  // Do not call this to test if the object referred to still exists! The weak
+  // reference remains initialized even if the target object has been collected.
+  bool is_uninitialized() const { return obj_ == nullptr; }
+
+  void reset();
+
+ private:
+  void Assign(const JavaObjectWeakGlobalRef& rhs);
+
+  jweak obj_;
+};
+
+// Get the real object stored in the weak reference returned as a
+// ScopedJavaLocalRef.
+BASE_EXPORT base::android::ScopedJavaLocalRef<jobject> GetRealObject(
+    JNIEnv* env, jweak obj);
+
+#endif  // BASE_ANDROID_JNI_WEAK_REF_H_
diff --git a/base/android/junit/src/org/chromium/base/ApplicationStatusTest.java b/base/android/junit/src/org/chromium/base/ApplicationStatusTest.java
new file mode 100644
index 0000000..eb18295
--- /dev/null
+++ b/base/android/junit/src/org/chromium/base/ApplicationStatusTest.java
@@ -0,0 +1,70 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.app.Activity;
+import android.view.KeyEvent;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.Robolectric;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.Shadows;
+import org.robolectric.android.controller.ActivityController;
+import org.robolectric.annotation.Config;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.shadows.ShadowActivity;
+import org.robolectric.shadows.multidex.ShadowMultiDex;
+
+import org.chromium.base.test.BaseRobolectricTestRunner;
+
+/** Unit tests for {@link ApplicationStatus}. */
+@RunWith(BaseRobolectricTestRunner.class)
+@Config(manifest = Config.NONE,
+        shadows = {ApplicationStatusTest.TrackingShadowActivity.class, ShadowMultiDex.class})
+public class ApplicationStatusTest {
+    /** Shadow that tracks calls to onWindowFocusChanged and dispatchKeyEvent. */
+    @Implements(Activity.class)
+    public static class TrackingShadowActivity extends ShadowActivity {
+        private int mWindowFocusCalls;
+        private int mDispatchKeyEventCalls;
+        private boolean mReturnValueForKeyDispatch;
+
+        @Implementation
+        public void onWindowFocusChanged(@SuppressWarnings("unused") boolean hasFocus) {
+            mWindowFocusCalls++;
+        }
+
+        @Implementation
+        public boolean dispatchKeyEvent(@SuppressWarnings("unused") KeyEvent event) {
+            mDispatchKeyEventCalls++;
+            return mReturnValueForKeyDispatch;
+        }
+    }
+
+    @Test
+    public void testWindowsFocusChanged() throws Exception {
+        ApplicationStatus.initialize(RuntimeEnvironment.application);
+
+        ApplicationStatus.WindowFocusChangedListener mock =
+                mock(ApplicationStatus.WindowFocusChangedListener.class);
+        ApplicationStatus.registerWindowFocusChangedListener(mock);
+
+        ActivityController<Activity> controller =
+                Robolectric.buildActivity(Activity.class).create().start().visible();
+        TrackingShadowActivity shadow = (TrackingShadowActivity) Shadows.shadowOf(controller.get());
+
+        controller.get().getWindow().getCallback().onWindowFocusChanged(true);
+        // Assert that listeners were notified.
+        verify(mock).onWindowFocusChanged(controller.get(), true);
+        // Also ensure that the original activity is forwarded the notification.
+        Assert.assertEquals(1, shadow.mWindowFocusCalls);
+    }
+}
diff --git a/base/android/junit/src/org/chromium/base/LogTest.java b/base/android/junit/src/org/chromium/base/LogTest.java
new file mode 100644
index 0000000..a3832a0
--- /dev/null
+++ b/base/android/junit/src/org/chromium/base/LogTest.java
@@ -0,0 +1,87 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowLog;
+
+import org.chromium.base.test.BaseRobolectricTestRunner;
+
+import java.util.List;
+
+/** Unit tests for {@link Log}. */
+@RunWith(BaseRobolectricTestRunner.class)
+@Config(manifest = Config.NONE)
+public class LogTest {
+    /** Tests that the computed call origin is the correct one. */
+    @Test
+    public void callOriginTest() {
+        Log.d("Foo", "Bar");
+
+        List<ShadowLog.LogItem> logs = ShadowLog.getLogs();
+        assertEquals("Only one log should be written", 1, logs.size());
+
+        assertTrue("The origin of the log message (" + logs.get(0).msg + ") looks wrong.",
+                logs.get(0).msg.matches("\\[LogTest.java:\\d+\\].*"));
+    }
+
+    @Test
+    public void normalizeTagTest() {
+        assertEquals("cr_foo", Log.normalizeTag("cr.foo"));
+        assertEquals("cr_foo", Log.normalizeTag("cr_foo"));
+        assertEquals("cr_foo", Log.normalizeTag("foo"));
+        assertEquals("cr_ab_foo", Log.normalizeTag("ab_foo"));
+    }
+
+    /** Tests that exceptions provided to the log functions are properly recognized and printed. */
+    @Test
+    public void exceptionLoggingTest() {
+        Throwable t = new Throwable() {
+            @Override
+            public String toString() {
+                return "MyThrowable";
+            }
+        };
+
+        Throwable t2 = new Throwable() {
+            @Override
+            public String toString() {
+                return "MyOtherThrowable";
+            }
+        };
+
+        List<ShadowLog.LogItem> logs;
+
+        // The throwable gets printed out
+        Log.i("Foo", "Bar", t);
+        logs = ShadowLog.getLogs();
+        assertEquals(t, logs.get(logs.size() - 1).throwable);
+        assertEquals("Bar", logs.get(logs.size() - 1).msg);
+
+        // The throwable can be both added to the message itself and printed out
+        Log.i("Foo", "Bar %s", t);
+        logs = ShadowLog.getLogs();
+        assertEquals(t, logs.get(logs.size() - 1).throwable);
+        assertEquals("Bar MyThrowable", logs.get(logs.size() - 1).msg);
+
+        // Non throwable are properly identified
+        Log.i("Foo", "Bar %s", t, "Baz");
+        logs = ShadowLog.getLogs();
+        assertNull(logs.get(logs.size() - 1).throwable);
+        assertEquals("Bar MyThrowable", logs.get(logs.size() - 1).msg);
+
+        // The last throwable is the one used that is going to be printed out
+        Log.i("Foo", "Bar %s %s", t, t2);
+        logs = ShadowLog.getLogs();
+        assertEquals(t2, logs.get(logs.size() - 1).throwable);
+        assertEquals("Bar MyThrowable MyOtherThrowable", logs.get(logs.size() - 1).msg);
+    }
+}
diff --git a/base/android/junit/src/org/chromium/base/NonThreadSafeTest.java b/base/android/junit/src/org/chromium/base/NonThreadSafeTest.java
new file mode 100644
index 0000000..9c57199
--- /dev/null
+++ b/base/android/junit/src/org/chromium/base/NonThreadSafeTest.java
@@ -0,0 +1,63 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.BlockJUnit4ClassRunner;
+
+import org.chromium.base.test.util.Feature;
+
+/**
+ * Tests for NonThreadSafe.
+ */
+@RunWith(BlockJUnit4ClassRunner.class)
+public class NonThreadSafeTest {
+    /**
+     * Test for creating and using on the same thread
+     */
+    @Test
+    @Feature({"Android-AppBase"})
+    public void testCreateAndUseOnSameThread() {
+        NonThreadSafe t = new NonThreadSafe();
+        Assert.assertTrue(t.calledOnValidThread());
+    }
+
+    /**
+     * Test if calledOnValidThread returns false if used on another thread.
+     */
+    @Test
+    @Feature({"Android-AppBase"})
+    public void testCreateAndUseOnDifferentThread() {
+        final NonThreadSafe t = new NonThreadSafe();
+
+        new Thread(new Runnable() {
+            @Override
+            public void run() {
+                Assert.assertFalse(t.calledOnValidThread());
+            }
+        }).start();
+    }
+
+    /**
+     * Test if detachFromThread reassigns the thread.
+     */
+    @Test
+    @Feature({"Android-AppBase"})
+    public void testDetachFromThread() {
+        final NonThreadSafe t = new NonThreadSafe();
+        Assert.assertTrue(t.calledOnValidThread());
+        t.detachFromThread();
+
+        new Thread(new Runnable() {
+            @Override
+            public void run() {
+                Assert.assertTrue(t.calledOnValidThread());
+                Assert.assertTrue(t.calledOnValidThread());
+            }
+        }).start();
+    }
+}
diff --git a/base/android/junit/src/org/chromium/base/PromiseTest.java b/base/android/junit/src/org/chromium/base/PromiseTest.java
new file mode 100644
index 0000000..58d3956
--- /dev/null
+++ b/base/android/junit/src/org/chromium/base/PromiseTest.java
@@ -0,0 +1,316 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowLooper;
+
+import org.chromium.base.Promise.UnhandledRejectionException;
+import org.chromium.base.test.BaseRobolectricTestRunner;
+
+/** Unit tests for {@link Promise}. */
+@RunWith(BaseRobolectricTestRunner.class)
+@Config(manifest = Config.NONE)
+public class PromiseTest {
+    // We need a simple mutable reference type for testing.
+    private static class Value {
+        private int mValue;
+
+        public int get() {
+            return mValue;
+        }
+
+        public void set(int value) {
+            mValue = value;
+        }
+    }
+
+    /** Tests that the callback is called on fulfillment. */
+    @Test
+    public void callback() {
+        final Value value = new Value();
+
+        Promise<Integer> promise = new Promise<Integer>();
+        promise.then(PromiseTest.<Integer>setValue(value, 1));
+
+        assertEquals(value.get(), 0);
+
+        promise.fulfill(new Integer(1));
+        assertEquals(value.get(), 1);
+    }
+
+    /** Tests that multiple callbacks are called. */
+    @Test
+    public void multipleCallbacks() {
+        final Value value = new Value();
+
+        Promise<Integer> promise = new Promise<Integer>();
+        Callback<Integer> callback = new Callback<Integer>() {
+            @Override
+            public void onResult(Integer result) {
+                value.set(value.get() + 1);
+            }
+        };
+        promise.then(callback);
+        promise.then(callback);
+
+        assertEquals(value.get(), 0);
+
+        promise.fulfill(new Integer(0));
+        assertEquals(value.get(), 2);
+    }
+
+    /** Tests that a callback is called immediately when given to a fulfilled Promise. */
+    @Test
+    public void callbackOnFulfilled() {
+        final Value value = new Value();
+
+        Promise<Integer> promise = Promise.fulfilled(new Integer(0));
+        assertEquals(value.get(), 0);
+
+        promise.then(PromiseTest.<Integer>setValue(value, 1));
+
+        assertEquals(value.get(), 1);
+    }
+
+    /** Tests that promises can chain synchronous functions correctly. */
+    @Test
+    public void promiseChaining() {
+        Promise<Integer> promise = new Promise<Integer>();
+        final Value value = new Value();
+
+        promise.then(new Promise.Function<Integer, String>(){
+                    @Override
+                    public String apply(Integer arg) {
+                        return arg.toString();
+                    }
+                }).then(new Promise.Function<String, String>(){
+                    @Override
+                    public String apply(String arg) {
+                        return arg + arg;
+                    }
+                }).then(new Callback<String>() {
+                    @Override
+                    public void onResult(String result) {
+                        value.set(result.length());
+                    }
+                });
+
+        promise.fulfill(new Integer(123));
+        ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
+        assertEquals(6, value.get());
+    }
+
+    /** Tests that promises can chain asynchronous functions correctly. */
+    @Test
+    public void promiseChainingAsyncFunctions() {
+        Promise<Integer> promise = new Promise<Integer>();
+        final Value value = new Value();
+
+        final Promise<String> innerPromise = new Promise<String>();
+
+        promise.then(new Promise.AsyncFunction<Integer, String>() {
+                    @Override
+                    public Promise<String> apply(Integer arg) {
+                        return innerPromise;
+                    }
+                }).then(new Callback<String>(){
+                    @Override
+                    public void onResult(String result) {
+                        value.set(result.length());
+                    }
+                });
+
+        assertEquals(0, value.get());
+
+        promise.fulfill(5);
+        ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
+        assertEquals(0, value.get());
+
+        innerPromise.fulfill("abc");
+        ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
+        assertEquals(3, value.get());
+    }
+
+    /** Tests that a Promise that does not use its result does not throw on rejection. */
+    @Test
+    public void rejectPromiseNoCallbacks() {
+        Promise<Integer> promise = new Promise<Integer>();
+
+        boolean caught = false;
+        try {
+            promise.reject();
+            ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
+        } catch (UnhandledRejectionException e) {
+            caught = true;
+        }
+        assertFalse(caught);
+    }
+
+    /** Tests that a Promise that uses its result throws on rejection if it has no handler. */
+    @Test
+    public void rejectPromiseNoHandler() {
+        Promise<Integer> promise = new Promise<Integer>();
+        promise.then(PromiseTest.<Integer>identity()).then(PromiseTest.<Integer>pass());
+
+        boolean caught = false;
+        try {
+            promise.reject();
+            ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
+        } catch (UnhandledRejectionException e) {
+            caught = true;
+        }
+        assertTrue(caught);
+    }
+
+    /** Tests that a Promise that handles rejection does not throw on rejection. */
+    @Test
+    public void rejectPromiseHandled() {
+        Promise<Integer> promise = new Promise<Integer>();
+        promise.then(PromiseTest.<Integer>identity())
+                .then(PromiseTest.<Integer>pass(), PromiseTest.<Exception>pass());
+
+        boolean caught = false;
+        try {
+            promise.reject();
+            ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
+        } catch (UnhandledRejectionException e) {
+            caught = true;
+        }
+        assertFalse(caught);
+    }
+
+    /** Tests that rejections carry the exception information. */
+    @Test
+    public void rejectionInformation() {
+        Promise<Integer> promise = new Promise<Integer>();
+        promise.then(PromiseTest.<Integer>pass());
+
+        String message = "Promise Test";
+        try {
+            promise.reject(new NegativeArraySizeException(message));
+            ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
+            fail();
+        } catch (UnhandledRejectionException e) {
+            assertTrue(e.getCause() instanceof NegativeArraySizeException);
+            assertEquals(e.getCause().getMessage(), message);
+        }
+    }
+
+    /** Tests that rejections propagate. */
+    @Test
+    public void rejectionChaining() {
+        final Value value = new Value();
+        Promise<Integer> promise = new Promise<Integer>();
+
+        Promise<Integer> result =
+                promise.then(PromiseTest.<Integer>identity()).then(PromiseTest.<Integer>identity());
+
+        result.then(PromiseTest.<Integer>pass(), PromiseTest.<Exception>setValue(value, 5));
+
+        promise.reject(new Exception());
+        ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
+
+        assertEquals(value.get(), 5);
+        assertTrue(result.isRejected());
+    }
+
+    /** Tests that Promises get rejected if a Function throws. */
+    @Test
+    public void rejectOnThrow() {
+        Value value = new Value();
+        Promise<Integer> promise = new Promise<Integer>();
+        promise.then(new Promise.Function<Integer, Integer>() {
+            @Override
+            public Integer apply(Integer argument) {
+                throw new IllegalArgumentException();
+            }
+        }).then(PromiseTest.<Integer>pass(), PromiseTest.<Exception>setValue(value, 5));
+
+        promise.fulfill(0);
+
+        ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
+        assertEquals(value.get(), 5);
+    }
+
+    /** Tests that Promises get rejected if an AsyncFunction throws. */
+    @Test
+    public void rejectOnAsyncThrow() {
+        Value value = new Value();
+        Promise<Integer> promise = new Promise<Integer>();
+
+        promise.then(new Promise.AsyncFunction<Integer, Integer>() {
+            @Override
+            public Promise<Integer> apply(Integer argument) {
+                throw new IllegalArgumentException();
+            }
+        }).then(PromiseTest.<Integer>pass(), PromiseTest.<Exception>setValue(value, 5));
+
+        promise.fulfill(0);
+
+        ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
+        assertEquals(value.get(), 5);
+    }
+
+    /** Tests that Promises get rejected if an AsyncFunction rejects. */
+    @Test
+    public void rejectOnAsyncReject() {
+        Value value = new Value();
+        Promise<Integer> promise = new Promise<Integer>();
+        final Promise<Integer> inner = new Promise<Integer>();
+
+        promise.then(new Promise.AsyncFunction<Integer, Integer>() {
+            @Override
+            public Promise<Integer> apply(Integer argument) {
+                return inner;
+            }
+        }).then(PromiseTest.<Integer>pass(), PromiseTest.<Exception>setValue(value, 5));
+
+        promise.fulfill(0);
+
+        ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
+        assertEquals(value.get(), 0);
+
+        inner.reject();
+
+        ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
+        assertEquals(value.get(), 5);
+    }
+
+    /** Convenience method that returns a Callback that does nothing with its result. */
+    private static <T> Callback<T> pass() {
+        return new Callback<T>() {
+            @Override
+            public void onResult(T result) {}
+        };
+    }
+
+    /** Convenience method that returns a Function that just passes through its argument. */
+    private static <T> Promise.Function<T, T> identity() {
+        return new Promise.Function<T, T>() {
+            @Override
+            public T apply(T argument) {
+                return argument;
+            }
+        };
+    }
+
+    /** Convenience method that returns a Callback that sets the given Value on execution. */
+    private static <T> Callback<T> setValue(final Value toSet, final int value) {
+        return new Callback<T>() {
+            @Override
+            public void onResult(T result) {
+                toSet.set(value);
+            }
+        };
+    }
+}
diff --git a/base/android/junit/src/org/chromium/base/memory/MemoryPressureMonitorTest.java b/base/android/junit/src/org/chromium/base/memory/MemoryPressureMonitorTest.java
new file mode 100644
index 0000000..1249a66
--- /dev/null
+++ b/base/android/junit/src/org/chromium/base/memory/MemoryPressureMonitorTest.java
@@ -0,0 +1,356 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base.memory;
+
+import android.content.ComponentCallbacks2;
+import android.os.Looper;
+import android.support.test.filters.SmallTest;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowLooper;
+
+import org.chromium.base.MemoryPressureLevel;
+import org.chromium.base.Supplier;
+import org.chromium.base.ThreadUtils;
+import org.chromium.base.test.BaseRobolectricTestRunner;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Test for MemoryPressureMonitor.
+ */
+@RunWith(BaseRobolectricTestRunner.class)
+@Config(manifest = Config.NONE)
+public class MemoryPressureMonitorTest {
+    private MemoryPressureMonitor mMonitor;
+
+    private static class TestPressureCallback implements MemoryPressureCallback {
+        private Integer mReportedPressure;
+
+        public void assertCalledWith(@MemoryPressureLevel int expectedPressure) {
+            Assert.assertNotNull("Callback was not called", mReportedPressure);
+            Assert.assertEquals(expectedPressure, (int) mReportedPressure);
+        }
+
+        public void assertNotCalled() {
+            Assert.assertNull(mReportedPressure);
+        }
+
+        public void reset() {
+            mReportedPressure = null;
+        }
+
+        @Override
+        public void onPressure(@MemoryPressureLevel int pressure) {
+            assertNotCalled();
+            mReportedPressure = pressure;
+        }
+    }
+
+    private static class TestPressureSupplier implements Supplier<Integer> {
+        private @MemoryPressureLevel Integer mPressure;
+        private boolean mIsCalled;
+
+        public TestPressureSupplier(@MemoryPressureLevel Integer pressure) {
+            mPressure = pressure;
+        }
+
+        @Override
+        public @MemoryPressureLevel Integer get() {
+            assertNotCalled();
+            mIsCalled = true;
+            return mPressure;
+        }
+
+        public void assertCalled() {
+            Assert.assertTrue(mIsCalled);
+        }
+
+        public void assertNotCalled() {
+            Assert.assertFalse(mIsCalled);
+        }
+    }
+
+    private static final int THROTTLING_INTERVAL_MS = 1000;
+
+    @Before
+    public void setUp() {
+        // Explicitly set main thread as UiThread. Other places rely on that.
+        ThreadUtils.setUiThread(Looper.getMainLooper());
+
+        // Pause main thread to get control over when tasks are run (see runUiThreadFor()).
+        ShadowLooper.pauseMainLooper();
+
+        mMonitor = new MemoryPressureMonitor(THROTTLING_INTERVAL_MS);
+        mMonitor.setCurrentPressureSupplierForTesting(null);
+    }
+
+    /**
+     * Runs all UiThread tasks posted |delayMs| in the future.
+     * @param delayMs
+     */
+    private void runUiThreadFor(long delayMs) {
+        ShadowLooper.idleMainLooper(delayMs, TimeUnit.MILLISECONDS);
+    }
+
+    @Test
+    @SmallTest
+    public void testTrimLevelTranslation() {
+        Integer[][] trimLevelToPressureMap = {//
+                // Levels >= TRIM_MEMORY_COMPLETE map to CRITICAL.
+                {ComponentCallbacks2.TRIM_MEMORY_COMPLETE + 1, MemoryPressureLevel.CRITICAL},
+                {ComponentCallbacks2.TRIM_MEMORY_COMPLETE, MemoryPressureLevel.CRITICAL},
+
+                // TRIM_MEMORY_RUNNING_CRITICAL maps to CRITICAL.
+                {ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL, MemoryPressureLevel.CRITICAL},
+
+                // Levels < TRIM_MEMORY_COMPLETE && >= TRIM_MEMORY_BACKGROUND map to MODERATE.
+                {ComponentCallbacks2.TRIM_MEMORY_COMPLETE - 1, MemoryPressureLevel.MODERATE},
+                {ComponentCallbacks2.TRIM_MEMORY_BACKGROUND + 1, MemoryPressureLevel.MODERATE},
+                {ComponentCallbacks2.TRIM_MEMORY_BACKGROUND, MemoryPressureLevel.MODERATE},
+
+                // Other levels are not mapped.
+                {ComponentCallbacks2.TRIM_MEMORY_BACKGROUND - 1, null},
+                {ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW, null},
+                {ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE, null},
+                {ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN, null}};
+        for (Integer[] trimLevelAndPressure : trimLevelToPressureMap) {
+            int trimLevel = trimLevelAndPressure[0];
+            Integer expectedPressure = trimLevelAndPressure[1];
+            Integer actualPressure = MemoryPressureMonitor.memoryPressureFromTrimLevel(trimLevel);
+            Assert.assertEquals(expectedPressure, actualPressure);
+        }
+    }
+
+    @Test
+    @SmallTest
+    public void testThrottleInterval() {
+        TestPressureCallback callback = new TestPressureCallback();
+        mMonitor.setReportingCallbackForTesting(callback);
+
+        // First notification should go through.
+        mMonitor.notifyPressure(MemoryPressureLevel.NONE);
+        callback.assertCalledWith(MemoryPressureLevel.NONE);
+
+        callback.reset();
+
+        // This one should be throttled.
+        mMonitor.notifyPressure(MemoryPressureLevel.NONE);
+        callback.assertNotCalled();
+
+        runUiThreadFor(THROTTLING_INTERVAL_MS - 1);
+
+        // We're still within the throttling interval, so this notification should
+        // still be throttled.
+        mMonitor.notifyPressure(MemoryPressureLevel.NONE);
+        callback.assertNotCalled();
+
+        runUiThreadFor(1);
+
+        // We're past the throttling interval at this point, so this notification
+        // should go through.
+        mMonitor.notifyPressure(MemoryPressureLevel.NONE);
+        callback.assertCalledWith(MemoryPressureLevel.NONE);
+    }
+
+    @Test
+    @SmallTest
+    public void testChangeNotIgnored() {
+        TestPressureCallback callback = new TestPressureCallback();
+        mMonitor.setReportingCallbackForTesting(callback);
+
+        mMonitor.notifyPressure(MemoryPressureLevel.NONE);
+        callback.assertCalledWith(MemoryPressureLevel.NONE);
+
+        callback.reset();
+
+        // Second notification is throttled, but should be reported after the
+        // throttling interval ends.
+        mMonitor.notifyPressure(MemoryPressureLevel.MODERATE);
+        callback.assertNotCalled();
+
+        runUiThreadFor(THROTTLING_INTERVAL_MS - 1);
+
+        // Shouldn't be reported at this point.
+        callback.assertNotCalled();
+
+        runUiThreadFor(1);
+
+        callback.assertCalledWith(MemoryPressureLevel.MODERATE);
+    }
+
+    @Test
+    @SmallTest
+    public void testNoopChangeIgnored() {
+        TestPressureCallback callback = new TestPressureCallback();
+        mMonitor.setReportingCallbackForTesting(callback);
+
+        mMonitor.notifyPressure(MemoryPressureLevel.NONE);
+        callback.assertCalledWith(MemoryPressureLevel.NONE);
+
+        callback.reset();
+
+        // Report MODERATE and then NONE, so that the throttling interval finishes with the
+        // same pressure that started it (i.e. NONE). In this case MODERATE should be ignored.
+        mMonitor.notifyPressure(MemoryPressureLevel.MODERATE);
+        mMonitor.notifyPressure(MemoryPressureLevel.NONE);
+
+        runUiThreadFor(THROTTLING_INTERVAL_MS);
+
+        callback.assertNotCalled();
+    }
+
+    @Test
+    @SmallTest
+    public void testPollingInitiallyDisabled() {
+        TestPressureSupplier pressureSupplier =
+                new TestPressureSupplier(MemoryPressureLevel.MODERATE);
+        mMonitor.setCurrentPressureSupplierForTesting(pressureSupplier);
+
+        mMonitor.notifyPressure(MemoryPressureLevel.CRITICAL);
+        runUiThreadFor(THROTTLING_INTERVAL_MS);
+
+        // We finished the interval with CRITICAL, but since polling is disabled, we shouldn't
+        // poll the current pressure.
+        pressureSupplier.assertNotCalled();
+    }
+
+    @Test
+    @SmallTest
+    public void testEnablePollingPolls() {
+        TestPressureCallback callback = new TestPressureCallback();
+        mMonitor.setReportingCallbackForTesting(callback);
+
+        TestPressureSupplier pressureSupplier =
+                new TestPressureSupplier(MemoryPressureLevel.MODERATE);
+        mMonitor.setCurrentPressureSupplierForTesting(pressureSupplier);
+
+        mMonitor.enablePolling();
+
+        // When polling is enabled, current pressure should be retrieved and reported.
+        pressureSupplier.assertCalled();
+        callback.assertCalledWith(MemoryPressureLevel.MODERATE);
+    }
+
+    @Test
+    @SmallTest
+    public void testNullSupplierResultIgnored() {
+        TestPressureCallback callback = new TestPressureCallback();
+        mMonitor.setReportingCallbackForTesting(callback);
+
+        TestPressureSupplier pressureSupplier = new TestPressureSupplier(null);
+        mMonitor.setCurrentPressureSupplierForTesting(pressureSupplier);
+
+        mMonitor.enablePolling();
+
+        // The pressure supplier should be called, but its null result should be ignored.
+        pressureSupplier.assertCalled();
+        callback.assertNotCalled();
+    }
+
+    @Test
+    @SmallTest
+    public void testEnablePollingRespectsThrottling() {
+        TestPressureSupplier pressureSupplier =
+                new TestPressureSupplier(MemoryPressureLevel.MODERATE);
+        mMonitor.setCurrentPressureSupplierForTesting(pressureSupplier);
+
+        mMonitor.notifyPressure(MemoryPressureLevel.NONE);
+
+        // The notification above started a throttling interval, so we shouldn't ask for the
+        // current pressure when polling is enabled.
+        mMonitor.enablePolling();
+
+        pressureSupplier.assertNotCalled();
+    }
+
+    @Test
+    @SmallTest
+    public void testPollingIfCRITICAL() {
+        TestPressureCallback callback = new TestPressureCallback();
+        mMonitor.setReportingCallbackForTesting(callback);
+
+        TestPressureSupplier pressureSupplier =
+                new TestPressureSupplier(MemoryPressureLevel.MODERATE);
+        mMonitor.setCurrentPressureSupplierForTesting(pressureSupplier);
+
+        mMonitor.notifyPressure(MemoryPressureLevel.CRITICAL);
+        callback.reset();
+
+        mMonitor.enablePolling();
+
+        runUiThreadFor(THROTTLING_INTERVAL_MS - 1);
+
+        // Pressure should be polled after the interval ends, not before.
+        pressureSupplier.assertNotCalled();
+
+        runUiThreadFor(1);
+
+        // We started and finished the throttling interval with CRITICAL pressure, so
+        // we should poll the current pressure at the end of the interval.
+        pressureSupplier.assertCalled();
+        callback.assertCalledWith(MemoryPressureLevel.MODERATE);
+    }
+
+    @Test
+    @SmallTest
+    public void testNoPollingIfNotCRITICAL() {
+        TestPressureSupplier pressureSupplier = new TestPressureSupplier(MemoryPressureLevel.NONE);
+        mMonitor.setCurrentPressureSupplierForTesting(pressureSupplier);
+
+        mMonitor.notifyPressure(MemoryPressureLevel.MODERATE);
+
+        mMonitor.enablePolling();
+
+        runUiThreadFor(THROTTLING_INTERVAL_MS);
+
+        // We started and finished the throttling interval with non-CRITICAL pressure,
+        // so no polling should take place.
+        pressureSupplier.assertNotCalled();
+    }
+
+    @Test
+    @SmallTest
+    public void testNoPollingIfChangedToCRITICAL() {
+        TestPressureSupplier pressureSupplier = new TestPressureSupplier(MemoryPressureLevel.NONE);
+        mMonitor.setCurrentPressureSupplierForTesting(pressureSupplier);
+
+        mMonitor.notifyPressure(MemoryPressureLevel.MODERATE);
+        mMonitor.notifyPressure(MemoryPressureLevel.CRITICAL);
+
+        mMonitor.enablePolling();
+
+        runUiThreadFor(THROTTLING_INTERVAL_MS);
+
+        // We finished the throttling interval with CRITITCAL, but started with MODERATE,
+        // so no polling should take place.
+        pressureSupplier.assertNotCalled();
+    }
+
+    @Test
+    @SmallTest
+    public void testDisablePolling() {
+        TestPressureSupplier pressureSupplier = new TestPressureSupplier(MemoryPressureLevel.NONE);
+        mMonitor.setCurrentPressureSupplierForTesting(pressureSupplier);
+
+        mMonitor.notifyPressure(MemoryPressureLevel.CRITICAL);
+
+        mMonitor.enablePolling();
+
+        runUiThreadFor(THROTTLING_INTERVAL_MS - 1);
+
+        // Whether polling is enabled or not should be taken into account only after the interval
+        // finishes, so disabling it here should have the same affect as if it was never enabled.
+        mMonitor.disablePolling();
+
+        runUiThreadFor(1);
+
+        pressureSupplier.assertNotCalled();
+    }
+}
diff --git a/base/android/junit/src/org/chromium/base/metrics/test/ShadowRecordHistogram.java b/base/android/junit/src/org/chromium/base/metrics/test/ShadowRecordHistogram.java
new file mode 100644
index 0000000..18fe6ce
--- /dev/null
+++ b/base/android/junit/src/org/chromium/base/metrics/test/ShadowRecordHistogram.java
@@ -0,0 +1,57 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base.metrics.test;
+
+import android.util.Pair;
+
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.annotation.Resetter;
+
+import org.chromium.base.metrics.RecordHistogram;
+
+import java.util.HashMap;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Implementation of RecordHistogram which does not rely on native and still enables testing of
+ * histogram counts.
+ */
+@Implements(RecordHistogram.class)
+public class ShadowRecordHistogram {
+    private static HashMap<Pair<String, Integer>, Integer> sSamples =
+            new HashMap<Pair<String, Integer>, Integer>();
+
+    @Resetter
+    public static void reset() {
+        sSamples.clear();
+    }
+
+    @Implementation
+    public static void recordCountHistogram(String name, int sample) {
+        Pair<String, Integer> key = Pair.create(name, sample);
+        incrementSampleCount(key);
+    }
+
+    @Implementation
+    public static void recordLongTimesHistogram100(String name, long duration, TimeUnit timeUnit) {
+        Pair<String, Integer> key = Pair.create(name, (int) timeUnit.toMillis(duration));
+        incrementSampleCount(key);
+    }
+
+    @Implementation
+    public static int getHistogramValueCountForTesting(String name, int sample) {
+        Integer i = sSamples.get(Pair.create(name, sample));
+        return (i != null) ? i : 0;
+    }
+
+    private static void incrementSampleCount(Pair<String, Integer> key) {
+        Integer i = sSamples.get(key);
+        if (i == null) {
+            i = 0;
+        }
+        sSamples.put(key, i + 1);
+    }
+}
diff --git a/base/android/junit/src/org/chromium/base/process_launcher/ChildConnectionAllocatorTest.java b/base/android/junit/src/org/chromium/base/process_launcher/ChildConnectionAllocatorTest.java
new file mode 100644
index 0000000..5c5bca9
--- /dev/null
+++ b/base/android/junit/src/org/chromium/base/process_launcher/ChildConnectionAllocatorTest.java
@@ -0,0 +1,332 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base.process_launcher;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.Bundle;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowLooper;
+
+import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.base.test.util.Feature;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/** Unit tests for the ChildConnectionAllocator class. */
+@Config(manifest = Config.NONE)
+@RunWith(BaseRobolectricTestRunner.class)
+public class ChildConnectionAllocatorTest {
+    private static final String TEST_PACKAGE_NAME = "org.chromium.allocator_test";
+
+    private static final int MAX_CONNECTION_NUMBER = 2;
+
+    private static final int FREE_CONNECTION_TEST_CALLBACK_START_FAILED = 1;
+    private static final int FREE_CONNECTION_TEST_CALLBACK_PROCESS_DIED = 2;
+
+    @Mock
+    private ChildProcessConnection.ServiceCallback mServiceCallback;
+
+    static class TestConnectionFactory implements ChildConnectionAllocator.ConnectionFactory {
+        private ComponentName mLastServiceName;
+
+        private ChildProcessConnection mConnection;
+
+        private ChildProcessConnection.ServiceCallback mConnectionServiceCallback;
+
+        @Override
+        public ChildProcessConnection createConnection(Context context, ComponentName serviceName,
+                boolean bindToCaller, boolean bindAsExternalService, Bundle serviceBundle) {
+            mLastServiceName = serviceName;
+            if (mConnection == null) {
+                mConnection = mock(ChildProcessConnection.class);
+                // Retrieve the ServiceCallback so we can simulate the service process dying.
+                doAnswer(new Answer() {
+                    @Override
+                    public Object answer(InvocationOnMock invocation) {
+                        mConnectionServiceCallback =
+                                (ChildProcessConnection.ServiceCallback) invocation.getArgument(1);
+                        return null;
+                    }
+                })
+                        .when(mConnection)
+                        .start(anyBoolean(), any(ChildProcessConnection.ServiceCallback.class));
+            }
+            return mConnection;
+        }
+
+        public ComponentName getAndResetLastServiceName() {
+            ComponentName serviceName = mLastServiceName;
+            mLastServiceName = null;
+            return serviceName;
+        }
+
+        // Use this method to have a callback invoked when the connection is started on the next
+        // created connection.
+        public void invokeCallbackOnConnectionStart(final boolean onChildStarted,
+                final boolean onStartFailed, final boolean onChildProcessDied) {
+            final ChildProcessConnection connection = mock(ChildProcessConnection.class);
+            mConnection = connection;
+            doAnswer(new Answer() {
+                @Override
+                public Object answer(InvocationOnMock invocation) {
+                    ChildProcessConnection.ServiceCallback serviceCallback =
+                            (ChildProcessConnection.ServiceCallback) invocation.getArgument(1);
+                    if (onChildStarted) {
+                        serviceCallback.onChildStarted();
+                    }
+                    if (onStartFailed) {
+                        serviceCallback.onChildStartFailed(connection);
+                    }
+                    if (onChildProcessDied) {
+                        serviceCallback.onChildProcessDied(connection);
+                    }
+                    return null;
+                }
+            })
+                    .when(mConnection)
+                    .start(anyBoolean(), any(ChildProcessConnection.ServiceCallback.class));
+        }
+
+        public void simulateServiceStartFailed() {
+            mConnectionServiceCallback.onChildStartFailed(mConnection);
+        }
+
+        public void simulateServiceProcessDying() {
+            mConnectionServiceCallback.onChildProcessDied(mConnection);
+        }
+    }
+
+    private final TestConnectionFactory mTestConnectionFactory = new TestConnectionFactory();
+
+    private ChildConnectionAllocator mAllocator;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mAllocator = ChildConnectionAllocator.createForTest(null, TEST_PACKAGE_NAME,
+                "AllocatorTest", MAX_CONNECTION_NUMBER, true /* bindToCaller */,
+                false /* bindAsExternalService */, false /* useStrongBinding */);
+        mAllocator.setConnectionFactoryForTesting(mTestConnectionFactory);
+    }
+
+    @Test
+    @Feature({"ProcessManagement"})
+    public void testPlainAllocate() {
+        assertFalse(mAllocator.anyConnectionAllocated());
+        assertEquals(MAX_CONNECTION_NUMBER, mAllocator.getNumberOfServices());
+
+        ChildProcessConnection connection =
+                mAllocator.allocate(null /* context */, null /* serviceBundle */, mServiceCallback);
+        assertNotNull(connection);
+
+        verify(connection, times(1))
+                .start(eq(false) /* useStrongBinding */,
+                        any(ChildProcessConnection.ServiceCallback.class));
+        assertTrue(mAllocator.anyConnectionAllocated());
+    }
+
+    /** Tests that different services are created until we reach the max number specified. */
+    @Test
+    @Feature({"ProcessManagement"})
+    public void testAllocateMaxNumber() {
+        assertTrue(mAllocator.isFreeConnectionAvailable());
+        Set<ComponentName> serviceNames = new HashSet<>();
+        for (int i = 0; i < MAX_CONNECTION_NUMBER; i++) {
+            ChildProcessConnection connection = mAllocator.allocate(
+                    null /* context */, null /* serviceBundle */, mServiceCallback);
+            assertNotNull(connection);
+            ComponentName serviceName = mTestConnectionFactory.getAndResetLastServiceName();
+            assertFalse(serviceNames.contains(serviceName));
+            serviceNames.add(serviceName);
+        }
+        assertFalse(mAllocator.isFreeConnectionAvailable());
+        assertNull(mAllocator.allocate(
+                null /* context */, null /* serviceBundle */, mServiceCallback));
+    }
+
+    @Test
+    @Feature({"ProcessManagement"})
+    public void testQueueAllocation() {
+        Runnable freeConnectionCallback = mock(Runnable.class);
+        mAllocator = ChildConnectionAllocator.createForTest(freeConnectionCallback,
+                TEST_PACKAGE_NAME, "AllocatorTest", 1, true /* bindToCaller */,
+                false /* bindAsExternalService */, false /* useStrongBinding */);
+        mAllocator.setConnectionFactoryForTesting(mTestConnectionFactory);
+        // Occupy all slots.
+        ChildProcessConnection connection =
+                mAllocator.allocate(null /* context */, null /* serviceBundle */, mServiceCallback);
+        assertNotNull(connection);
+        assertFalse(mAllocator.isFreeConnectionAvailable());
+
+        final ChildProcessConnection newConnection[] = new ChildProcessConnection[2];
+        Runnable allocate1 = () -> {
+            newConnection[0] = mAllocator.allocate(
+                    null /* context */, null /* serviceBundle */, mServiceCallback);
+        };
+        Runnable allocate2 = () -> {
+            newConnection[1] = mAllocator.allocate(
+                    null /* context */, null /* serviceBundle */, mServiceCallback);
+        };
+        mAllocator.queueAllocation(allocate1);
+        mAllocator.queueAllocation(allocate2);
+        verify(freeConnectionCallback, times(1)).run();
+        assertNull(newConnection[0]);
+
+        mTestConnectionFactory.simulateServiceProcessDying();
+        ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
+        assertNotNull(newConnection[0]);
+        assertNull(newConnection[1]);
+
+        mTestConnectionFactory.simulateServiceProcessDying();
+        ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
+        assertNotNull(newConnection[1]);
+    }
+
+    /**
+     * Tests that the connection is created with the useStrongBinding parameter specified in the
+     * allocator.
+     */
+    @Test
+    @Feature({"ProcessManagement"})
+    public void testStrongBindingParam() {
+        for (boolean useStrongBinding : new boolean[] {true, false}) {
+            ChildConnectionAllocator allocator = ChildConnectionAllocator.createForTest(null,
+                    TEST_PACKAGE_NAME, "AllocatorTest", MAX_CONNECTION_NUMBER,
+                    true /* bindToCaller */, false /* bindAsExternalService */, useStrongBinding);
+            allocator.setConnectionFactoryForTesting(mTestConnectionFactory);
+            ChildProcessConnection connection = allocator.allocate(
+                    null /* context */, null /* serviceBundle */, mServiceCallback);
+            verify(connection, times(0)).start(useStrongBinding, mServiceCallback);
+        }
+    }
+
+    /**
+     * Tests that the various ServiceCallbacks are propagated and posted, so they happen after the
+     * ChildProcessAllocator,allocate() method has returned.
+     */
+    public void runTestWithConnectionCallbacks(
+            boolean onChildStarted, boolean onChildStartFailed, boolean onChildProcessDied) {
+        // We have to pause the Roboletric looper or it'll execute the posted tasks synchronoulsy.
+        ShadowLooper.pauseMainLooper();
+        mTestConnectionFactory.invokeCallbackOnConnectionStart(
+                onChildStarted, onChildStartFailed, onChildProcessDied);
+        ChildProcessConnection connection =
+                mAllocator.allocate(null /* context */, null /* serviceBundle */, mServiceCallback);
+        assertNotNull(connection);
+
+        // Callbacks are posted.
+        verify(mServiceCallback, never()).onChildStarted();
+        verify(mServiceCallback, never()).onChildStartFailed(any());
+        verify(mServiceCallback, never()).onChildProcessDied(any());
+        ShadowLooper.unPauseMainLooper();
+        ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
+        verify(mServiceCallback, times(onChildStarted ? 1 : 0)).onChildStarted();
+        verify(mServiceCallback, times(onChildStartFailed ? 1 : 0)).onChildStartFailed(any());
+        verify(mServiceCallback, times(onChildProcessDied ? 1 : 0)).onChildProcessDied(any());
+    }
+
+    @Test
+    @Feature({"ProcessManagement"})
+    public void testOnChildStartedCallback() {
+        runTestWithConnectionCallbacks(true /* onChildStarted */, false /* onChildStartFailed */,
+                false /* onChildProcessDied */);
+    }
+
+    @Test
+    @Feature({"ProcessManagement"})
+    public void testOnChildStartFailedCallback() {
+        runTestWithConnectionCallbacks(false /* onChildStarted */, true /* onChildStartFailed */,
+                false /* onChildProcessDied */);
+    }
+
+    @Test
+    @Feature({"ProcessManagement"})
+    public void testOnChildProcessDiedCallback() {
+        runTestWithConnectionCallbacks(false /* onChildStarted */, false /* onChildStartFailed */,
+                true /* onChildProcessDied */);
+    }
+
+    /**
+     * Tests that the allocator clears the connection when it fails to bind/process dies.
+     */
+    private void testFreeConnection(int callbackType) {
+        ChildProcessConnection connection =
+                mAllocator.allocate(null /* context */, null /* serviceBundle */, mServiceCallback);
+
+        assertNotNull(connection);
+        ComponentName serviceName = mTestConnectionFactory.getAndResetLastServiceName();
+        verify(connection, times(1))
+                .start(eq(false) /* useStrongBinding */,
+                        any(ChildProcessConnection.ServiceCallback.class));
+        assertTrue(mAllocator.anyConnectionAllocated());
+        int onChildStartFailedExpectedCount = 0;
+        int onChildProcessDiedExpectedCount = 0;
+        switch (callbackType) {
+            case FREE_CONNECTION_TEST_CALLBACK_START_FAILED:
+                mTestConnectionFactory.simulateServiceStartFailed();
+                onChildStartFailedExpectedCount = 1;
+                break;
+            case FREE_CONNECTION_TEST_CALLBACK_PROCESS_DIED:
+                mTestConnectionFactory.simulateServiceProcessDying();
+                onChildProcessDiedExpectedCount = 1;
+                break;
+            default:
+                fail();
+                break;
+        }
+        ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
+        assertFalse(mAllocator.anyConnectionAllocated());
+        verify(mServiceCallback, never()).onChildStarted();
+        verify(mServiceCallback, times(onChildStartFailedExpectedCount))
+                .onChildStartFailed(connection);
+        verify(mServiceCallback, times(onChildProcessDiedExpectedCount))
+                .onChildProcessDied(connection);
+
+        // Allocate a new connection to make sure we are not getting the same connection.
+        connection =
+                mAllocator.allocate(null /* context */, null /* serviceBundle */, mServiceCallback);
+        assertNotNull(connection);
+        assertNotEquals(mTestConnectionFactory.getAndResetLastServiceName(), serviceName);
+    }
+
+    @Test
+    @Feature({"ProcessManagement"})
+    public void testFreeConnectionOnChildStartFailed() {
+        testFreeConnection(FREE_CONNECTION_TEST_CALLBACK_START_FAILED);
+    }
+
+    @Test
+    @Feature({"ProcessManagement"})
+    public void testFreeConnectionOnChildProcessDied() {
+        testFreeConnection(FREE_CONNECTION_TEST_CALLBACK_PROCESS_DIED);
+    }
+}
diff --git a/base/android/junit/src/org/chromium/base/process_launcher/ChildProcessConnectionTest.java b/base/android/junit/src/org/chromium/base/process_launcher/ChildProcessConnectionTest.java
new file mode 100644
index 0000000..95474ea
--- /dev/null
+++ b/base/android/junit/src/org/chromium/base/process_launcher/ChildProcessConnectionTest.java
@@ -0,0 +1,425 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base.process_launcher;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.AdditionalMatchers.or;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.isNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ComponentName;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowLooper;
+
+import org.chromium.base.ChildBindingState;
+import org.chromium.base.test.BaseRobolectricTestRunner;
+
+/** Unit tests for ChildProcessConnection. */
+@RunWith(BaseRobolectricTestRunner.class)
+@Config(manifest = Config.NONE)
+public class ChildProcessConnectionTest {
+    private static class ChildServiceConnectionMock
+            implements ChildProcessConnection.ChildServiceConnection {
+        private final Intent mBindIntent;
+        private final ChildProcessConnection.ChildServiceConnectionDelegate mDelegate;
+        private boolean mBound;
+
+        public ChildServiceConnectionMock(
+                Intent bindIntent, ChildProcessConnection.ChildServiceConnectionDelegate delegate) {
+            mBindIntent = bindIntent;
+            mDelegate = delegate;
+        }
+
+        @Override
+        public boolean bind() {
+            mBound = true;
+            return true;
+        }
+
+        @Override
+        public void unbind() {
+            mBound = false;
+        }
+
+        @Override
+        public boolean isBound() {
+            return mBound;
+        }
+
+        public void notifyServiceConnected(IBinder service) {
+            mDelegate.onServiceConnected(service);
+        }
+
+        public void notifyServiceDisconnected() {
+            mDelegate.onServiceDisconnected();
+        }
+
+        public Intent getBindIntent() {
+            return mBindIntent;
+        }
+    };
+
+    private final ChildProcessConnection.ChildServiceConnectionFactory mServiceConnectionFactory =
+            new ChildProcessConnection.ChildServiceConnectionFactory() {
+                @Override
+                public ChildProcessConnection.ChildServiceConnection createConnection(
+                        Intent bindIntent, int bindFlags,
+                        ChildProcessConnection.ChildServiceConnectionDelegate delegate) {
+                    ChildServiceConnectionMock connection =
+                            spy(new ChildServiceConnectionMock(bindIntent, delegate));
+                    if (mFirstServiceConnection == null) {
+                        mFirstServiceConnection = connection;
+                    }
+                    return connection;
+                }
+            };
+
+    @Mock
+    private ChildProcessConnection.ServiceCallback mServiceCallback;
+
+    @Mock
+    private ChildProcessConnection.ConnectionCallback mConnectionCallback;
+
+    private IChildProcessService mIChildProcessService;
+
+    private Binder mChildProcessServiceBinder;
+
+    private ChildServiceConnectionMock mFirstServiceConnection;
+
+    // Parameters captured from the IChildProcessService.setupConnection() call
+    private Bundle mConnectionBundle;
+    private ICallbackInt mConnectionPidCallback;
+    private IBinder mConnectionIBinderCallback;
+
+    @Before
+    public void setUp() throws RemoteException {
+        MockitoAnnotations.initMocks(this);
+
+        mIChildProcessService = mock(IChildProcessService.class);
+        // Capture the parameters passed to the IChildProcessService.setupConnection() call.
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) {
+                mConnectionBundle = (Bundle) invocation.getArgument(0);
+                mConnectionPidCallback = (ICallbackInt) invocation.getArgument(1);
+                mConnectionIBinderCallback = (IBinder) invocation.getArgument(2);
+                return null;
+            }
+        })
+                .when(mIChildProcessService)
+                .setupConnection(
+                        or(isNull(), any(Bundle.class)), or(isNull(), any()), or(isNull(), any()));
+
+        mChildProcessServiceBinder = new Binder();
+        mChildProcessServiceBinder.attachInterface(
+                mIChildProcessService, IChildProcessService.class.getName());
+    }
+
+    private ChildProcessConnection createDefaultTestConnection() {
+        return createTestConnection(false /* bindToCaller */, false /* bindAsExternalService */,
+                null /* serviceBundle */);
+    }
+
+    private ChildProcessConnection createTestConnection(
+            boolean bindToCaller, boolean bindAsExternalService, Bundle serviceBundle) {
+        String packageName = "org.chromium.test";
+        String serviceName = "TestService";
+        return new ChildProcessConnection(null /* context */,
+                new ComponentName(packageName, serviceName), bindToCaller, bindAsExternalService,
+                serviceBundle, mServiceConnectionFactory);
+    }
+
+    @Test
+    public void testStrongBinding() {
+        ChildProcessConnection connection = createDefaultTestConnection();
+        connection.start(true /* useStrongBinding */, null /* serviceCallback */);
+        assertTrue(connection.isStrongBindingBound());
+
+        connection = createDefaultTestConnection();
+        connection.start(false /* useStrongBinding */, null /* serviceCallback */);
+        assertFalse(connection.isStrongBindingBound());
+    }
+
+    @Test
+    public void testServiceBundle() {
+        Bundle serviceBundle = new Bundle();
+        final String intKey = "org.chromium.myInt";
+        final int intValue = 34;
+        final int defaultValue = -1;
+        serviceBundle.putInt(intKey, intValue);
+        String stringKey = "org.chromium.myString";
+        String stringValue = "thirty four";
+        serviceBundle.putString(stringKey, stringValue);
+
+        ChildProcessConnection connection = createTestConnection(
+                false /* bindToCaller */, false /* bindAsExternalService */, serviceBundle);
+        // Start the connection without the ChildServiceConnection connecting.
+        connection.start(false /* useStrongBinding */, null /* serviceCallback */);
+        assertNotNull(mFirstServiceConnection);
+        Intent bindIntent = mFirstServiceConnection.getBindIntent();
+        assertNotNull(bindIntent);
+        assertEquals(intValue, bindIntent.getIntExtra(intKey, defaultValue));
+        assertEquals(stringValue, bindIntent.getStringExtra(stringKey));
+    }
+
+    @Test
+    public void testServiceStartsSuccessfully() {
+        ChildProcessConnection connection = createDefaultTestConnection();
+        assertNotNull(mFirstServiceConnection);
+        connection.start(false /* useStrongBinding */, mServiceCallback);
+        Assert.assertTrue(connection.isModerateBindingBound());
+        Assert.assertFalse(connection.didOnServiceConnectedForTesting());
+        verify(mServiceCallback, never()).onChildStarted();
+        verify(mServiceCallback, never()).onChildStartFailed(any());
+        verify(mServiceCallback, never()).onChildProcessDied(any());
+
+        // The service connects.
+        mFirstServiceConnection.notifyServiceConnected(null /* iBinder */);
+        Assert.assertTrue(connection.didOnServiceConnectedForTesting());
+        verify(mServiceCallback, times(1)).onChildStarted();
+        verify(mServiceCallback, never()).onChildStartFailed(any());
+        verify(mServiceCallback, never()).onChildProcessDied(any());
+    }
+
+    @Test
+    public void testServiceStartsAndFailsToBind() {
+        ChildProcessConnection connection = createDefaultTestConnection();
+        assertNotNull(mFirstServiceConnection);
+        // Note we use doReturn so the actual bind() method is not called (it would with
+        // when(mFirstServiceConnection.bind()).thenReturn(false).
+        doReturn(false).when(mFirstServiceConnection).bind();
+        connection.start(false /* useStrongBinding */, mServiceCallback);
+
+        Assert.assertFalse(connection.isModerateBindingBound());
+        Assert.assertFalse(connection.didOnServiceConnectedForTesting());
+        verify(mServiceCallback, never()).onChildStarted();
+        verify(mServiceCallback, never()).onChildStartFailed(any());
+        verify(mServiceCallback, times(1)).onChildProcessDied(connection);
+    }
+
+    @Test
+    public void testServiceStops() {
+        ChildProcessConnection connection = createDefaultTestConnection();
+        assertNotNull(mFirstServiceConnection);
+        connection.start(false /* useStrongBinding */, mServiceCallback);
+        mFirstServiceConnection.notifyServiceConnected(null /* iBinder */);
+        connection.stop();
+        verify(mServiceCallback, times(1)).onChildStarted();
+        verify(mServiceCallback, never()).onChildStartFailed(any());
+        verify(mServiceCallback, times(1)).onChildProcessDied(connection);
+    }
+
+    @Test
+    public void testServiceDisconnects() {
+        ChildProcessConnection connection = createDefaultTestConnection();
+        assertNotNull(mFirstServiceConnection);
+        connection.start(false /* useStrongBinding */, mServiceCallback);
+        mFirstServiceConnection.notifyServiceConnected(null /* iBinder */);
+        mFirstServiceConnection.notifyServiceDisconnected();
+        verify(mServiceCallback, times(1)).onChildStarted();
+        verify(mServiceCallback, never()).onChildStartFailed(any());
+        verify(mServiceCallback, times(1)).onChildProcessDied(connection);
+    }
+
+    @Test
+    public void testNotBoundToCaller() throws RemoteException {
+        ChildProcessConnection connection = createTestConnection(false /* bindToCaller */,
+                false /* bindAsExternalService */, null /* serviceBundle */);
+        assertNotNull(mFirstServiceConnection);
+        connection.start(false /* useStrongBinding */, mServiceCallback);
+        mFirstServiceConnection.notifyServiceConnected(mChildProcessServiceBinder);
+        // Service is started and bindToCallback is not called.
+        verify(mServiceCallback, times(1)).onChildStarted();
+        verify(mServiceCallback, never()).onChildStartFailed(any());
+        verify(mServiceCallback, never()).onChildProcessDied(connection);
+        verify(mIChildProcessService, never()).bindToCaller();
+    }
+
+    @Test
+    public void testBoundToCallerSuccess() throws RemoteException {
+        ChildProcessConnection connection = createTestConnection(true /* bindToCaller */,
+                false /* bindAsExternalService */, null /* serviceBundle */);
+        assertNotNull(mFirstServiceConnection);
+        connection.start(false /* useStrongBinding */, mServiceCallback);
+        when(mIChildProcessService.bindToCaller()).thenReturn(true);
+        mFirstServiceConnection.notifyServiceConnected(mChildProcessServiceBinder);
+        // Service is started and bindToCallback is called.
+        verify(mServiceCallback, times(1)).onChildStarted();
+        verify(mServiceCallback, never()).onChildStartFailed(any());
+        verify(mServiceCallback, never()).onChildProcessDied(connection);
+        verify(mIChildProcessService, times(1)).bindToCaller();
+    }
+
+    @Test
+    public void testBoundToCallerFailure() throws RemoteException {
+        ChildProcessConnection connection = createTestConnection(true /* bindToCaller */,
+                false /* bindAsExternalService */, null /* serviceBundle */);
+        assertNotNull(mFirstServiceConnection);
+        connection.start(false /* useStrongBinding */, mServiceCallback);
+        // Pretend bindToCaller returns false, i.e. the service is already bound to a different
+        // service.
+        when(mIChildProcessService.bindToCaller()).thenReturn(false);
+        mFirstServiceConnection.notifyServiceConnected(mChildProcessServiceBinder);
+        // Service fails to start.
+        verify(mServiceCallback, never()).onChildStarted();
+        verify(mServiceCallback, times(1)).onChildStartFailed(any());
+        verify(mServiceCallback, never()).onChildProcessDied(connection);
+        verify(mIChildProcessService, times(1)).bindToCaller();
+    }
+
+    @Test
+    public void testSetupConnectionBeforeServiceConnected() throws RemoteException {
+        ChildProcessConnection connection = createDefaultTestConnection();
+        assertNotNull(mFirstServiceConnection);
+        connection.start(false /* useStrongBinding */, null /* serviceCallback */);
+        connection.setupConnection(
+                null /* connectionBundle */, null /* callback */, mConnectionCallback);
+        verify(mConnectionCallback, never()).onConnected(any());
+        mFirstServiceConnection.notifyServiceConnected(mChildProcessServiceBinder);
+        ShadowLooper.runUiThreadTasks();
+        assertNotNull(mConnectionPidCallback);
+        mConnectionPidCallback.call(34 /* pid */);
+        verify(mConnectionCallback, times(1)).onConnected(connection);
+    }
+
+    @Test
+    public void testSetupConnectionAfterServiceConnected() throws RemoteException {
+        ChildProcessConnection connection = createDefaultTestConnection();
+        assertNotNull(mFirstServiceConnection);
+        connection.start(false /* useStrongBinding */, null /* serviceCallback */);
+        mFirstServiceConnection.notifyServiceConnected(mChildProcessServiceBinder);
+        connection.setupConnection(
+                null /* connectionBundle */, null /* callback */, mConnectionCallback);
+        verify(mConnectionCallback, never()).onConnected(any());
+        ShadowLooper.runUiThreadTasks();
+        assertNotNull(mConnectionPidCallback);
+        mConnectionPidCallback.call(34 /* pid */);
+        verify(mConnectionCallback, times(1)).onConnected(connection);
+    }
+
+    @Test
+    public void testKill() throws RemoteException {
+        ChildProcessConnection connection = createDefaultTestConnection();
+        assertNotNull(mFirstServiceConnection);
+        connection.start(false /* useStrongBinding */, null /* serviceCallback */);
+        mFirstServiceConnection.notifyServiceConnected(mChildProcessServiceBinder);
+        connection.setupConnection(
+                null /* connectionBundle */, null /* callback */, mConnectionCallback);
+        verify(mConnectionCallback, never()).onConnected(any());
+        ShadowLooper.runUiThreadTasks();
+        assertNotNull(mConnectionPidCallback);
+        mConnectionPidCallback.call(34 /* pid */);
+        verify(mConnectionCallback, times(1)).onConnected(connection);
+
+        // Add strong binding so that connection is oom protected.
+        connection.removeModerateBinding();
+        assertEquals(ChildBindingState.WAIVED, connection.bindingStateCurrentOrWhenDied());
+        connection.addModerateBinding();
+        assertEquals(ChildBindingState.MODERATE, connection.bindingStateCurrentOrWhenDied());
+        connection.addStrongBinding();
+        assertEquals(ChildBindingState.STRONG, connection.bindingStateCurrentOrWhenDied());
+
+        // Kill and verify state.
+        connection.kill();
+        verify(mIChildProcessService).forceKill();
+        assertEquals(ChildBindingState.STRONG, connection.bindingStateCurrentOrWhenDied());
+        Assert.assertTrue(connection.isKilledByUs());
+    }
+
+    @Test
+    public void testBindingStateCounts() throws RemoteException {
+        ChildProcessConnection.resetBindingStateCountsForTesting();
+        ChildProcessConnection connection0 = createDefaultTestConnection();
+        ChildServiceConnectionMock connectionMock0 = mFirstServiceConnection;
+        mFirstServiceConnection = null;
+        ChildProcessConnection connection1 = createDefaultTestConnection();
+        ChildServiceConnectionMock connectionMock1 = mFirstServiceConnection;
+        mFirstServiceConnection = null;
+        ChildProcessConnection connection2 = createDefaultTestConnection();
+        ChildServiceConnectionMock connectionMock2 = mFirstServiceConnection;
+        mFirstServiceConnection = null;
+
+        assertArrayEquals(
+                connection0.bindingStateCountsCurrentOrWhenDied(), new int[] {0, 0, 0, 0});
+
+        connection0.start(false /* useStrongBinding */, null /* serviceCallback */);
+        assertArrayEquals(
+                connection0.bindingStateCountsCurrentOrWhenDied(), new int[] {0, 0, 1, 0});
+
+        connection1.start(true /* useStrongBinding */, null /* serviceCallback */);
+        assertArrayEquals(
+                connection0.bindingStateCountsCurrentOrWhenDied(), new int[] {0, 0, 1, 1});
+
+        connection2.start(false /* useStrongBinding */, null /* serviceCallback */);
+        assertArrayEquals(
+                connection0.bindingStateCountsCurrentOrWhenDied(), new int[] {0, 0, 2, 1});
+
+        Binder binder0 = new Binder();
+        Binder binder1 = new Binder();
+        Binder binder2 = new Binder();
+        binder0.attachInterface(mIChildProcessService, IChildProcessService.class.getName());
+        binder1.attachInterface(mIChildProcessService, IChildProcessService.class.getName());
+        binder2.attachInterface(mIChildProcessService, IChildProcessService.class.getName());
+        connectionMock0.notifyServiceConnected(binder0);
+        connectionMock1.notifyServiceConnected(binder1);
+        connectionMock2.notifyServiceConnected(binder2);
+        ShadowLooper.runUiThreadTasks();
+
+        // Add and remove moderate binding works as expected.
+        connection2.removeModerateBinding();
+        assertArrayEquals(
+                connection0.bindingStateCountsCurrentOrWhenDied(), new int[] {0, 1, 1, 1});
+        connection2.addModerateBinding();
+        assertArrayEquals(
+                connection0.bindingStateCountsCurrentOrWhenDied(), new int[] {0, 0, 2, 1});
+
+        // Add and remove strong binding works as expected.
+        connection0.addStrongBinding();
+        assertArrayEquals(
+                connection0.bindingStateCountsCurrentOrWhenDied(), new int[] {0, 0, 1, 2});
+        connection0.removeStrongBinding();
+        assertArrayEquals(
+                connection0.bindingStateCountsCurrentOrWhenDied(), new int[] {0, 0, 2, 1});
+
+        // Stopped connection should no longe update.
+        connection0.stop();
+        assertArrayEquals(
+                connection0.bindingStateCountsCurrentOrWhenDied(), new int[] {0, 0, 1, 1});
+        assertArrayEquals(
+                connection1.bindingStateCountsCurrentOrWhenDied(), new int[] {0, 0, 1, 1});
+
+        connection2.removeModerateBinding();
+        assertArrayEquals(
+                connection0.bindingStateCountsCurrentOrWhenDied(), new int[] {0, 0, 1, 1});
+        assertArrayEquals(
+                connection1.bindingStateCountsCurrentOrWhenDied(), new int[] {0, 1, 0, 1});
+    }
+}
diff --git a/base/android/library_loader/library_load_from_apk_status_codes.h b/base/android/library_loader/library_load_from_apk_status_codes.h
new file mode 100644
index 0000000..8910d48
--- /dev/null
+++ b/base/android/library_loader/library_load_from_apk_status_codes.h
@@ -0,0 +1,49 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_ANDROID_LIBRARY_LOADER_LIBRARY_LOAD_FROM_APK_STATUS_CODES_H_
+#define BASE_ANDROID_LIBRARY_LOADER_LIBRARY_LOAD_FROM_APK_STATUS_CODES_H_
+
+namespace base {
+namespace android {
+
+namespace {
+
+// This enum must be kept in sync with the LibraryLoadFromApkStatus enum in
+// tools/metrics/histograms/histograms.xml.
+// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.base.library_loader
+enum LibraryLoadFromApkStatusCodes {
+  // The loader was unable to determine whether the functionality is supported.
+  LIBRARY_LOAD_FROM_APK_STATUS_CODES_UNKNOWN = 0,
+
+  // The device does not support loading a library directly from the APK file
+  // (obsolete).
+  LIBRARY_LOAD_FROM_APK_STATUS_CODES_NOT_SUPPORTED_OBSOLETE = 1,
+
+  // The device supports loading a library directly from the APK file.
+  // (obsolete).
+  LIBRARY_LOAD_FROM_APK_STATUS_CODES_SUPPORTED_OBSOLETE = 2,
+
+  // The Chromium library was successfully loaded directly from the APK file.
+  LIBRARY_LOAD_FROM_APK_STATUS_CODES_SUCCESSFUL = 3,
+
+  // The Chromium library was successfully loaded using the unpack library
+  // fallback because it was compressed or not page aligned in the APK file.
+  LIBRARY_LOAD_FROM_APK_STATUS_CODES_USED_UNPACK_LIBRARY_FALLBACK = 4,
+
+  // The Chromium library was successfully loaded using the no map executable
+  // support fallback (obsolete).
+  LIBRARY_LOAD_FROM_APK_STATUS_CODES_USED_NO_MAP_EXEC_SUPPORT_FALLBACK_OBSOLETE
+      = 5,
+
+  // End sentinel.
+  LIBRARY_LOAD_FROM_APK_STATUS_CODES_MAX = 6,
+};
+
+}  // namespace
+
+}  // namespace android
+}  // namespace base
+
+#endif  // BASE_ANDROID_LIBRARY_LOADER_LIBRARY_LOAD_FROM_APK_STATUS_CODES_H_
diff --git a/base/android/library_loader/library_loader_hooks.cc b/base/android/library_loader/library_loader_hooks.cc
new file mode 100644
index 0000000..40bff5e
--- /dev/null
+++ b/base/android/library_loader/library_loader_hooks.cc
@@ -0,0 +1,254 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/android/library_loader/library_loader_hooks.h"
+
+#include "base/android/jni_string.h"
+#include "base/android/library_loader/anchor_functions_buildflags.h"
+#include "base/android/library_loader/library_load_from_apk_status_codes.h"
+#include "base/android/library_loader/library_prefetcher.h"
+#include "base/android/orderfile/orderfile_buildflags.h"
+#include "base/at_exit.h"
+#include "base/base_switches.h"
+#include "base/command_line.h"
+#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_functions.h"
+#include "base/metrics/histogram_macros.h"
+#include "jni/LibraryLoader_jni.h"
+
+#if BUILDFLAG(ORDERFILE_INSTRUMENTATION)
+#include "base/android/orderfile/orderfile_instrumentation.h"
+#endif
+
+namespace base {
+namespace android {
+
+namespace {
+
+base::AtExitManager* g_at_exit_manager = NULL;
+const char* g_library_version_number = "";
+LibraryLoadedHook* g_registration_callback = NULL;
+NativeInitializationHook* g_native_initialization_hook = NULL;
+
+enum RendererHistogramCode {
+  // Renderer load at fixed address success, fail, or not attempted.
+  // Renderers do not attempt to load at at fixed address if on a
+  // low-memory device on which browser load at fixed address has already
+  // failed.
+  LFA_SUCCESS = 0,
+  LFA_BACKOFF_USED = 1,
+  LFA_NOT_ATTEMPTED = 2,
+
+  // End sentinel, also used as nothing-pending indicator.
+  MAX_RENDERER_HISTOGRAM_CODE = 3,
+  NO_PENDING_HISTOGRAM_CODE = MAX_RENDERER_HISTOGRAM_CODE
+};
+
+enum BrowserHistogramCode {
+  // Non-low-memory random address browser loads.
+  NORMAL_LRA_SUCCESS = 0,
+
+  // Low-memory browser loads at fixed address, success or fail.
+  LOW_MEMORY_LFA_SUCCESS = 1,
+  LOW_MEMORY_LFA_BACKOFF_USED = 2,
+
+  MAX_BROWSER_HISTOGRAM_CODE = 3,
+};
+
+RendererHistogramCode g_renderer_histogram_code = NO_PENDING_HISTOGRAM_CODE;
+
+// Indicate whether g_library_preloader_renderer_histogram_code is valid
+bool g_library_preloader_renderer_histogram_code_registered = false;
+
+// The return value of NativeLibraryPreloader.loadLibrary() in child processes,
+// it is initialized to the invalid value which shouldn't showup in UMA report.
+int g_library_preloader_renderer_histogram_code = -1;
+
+// The amount of time, in milliseconds, that it took to load the shared
+// libraries in the renderer. Set in
+// RegisterChromiumAndroidLinkerRendererHistogram.
+long g_renderer_library_load_time_ms = 0;
+
+void RecordChromiumAndroidLinkerRendererHistogram() {
+  if (g_renderer_histogram_code == NO_PENDING_HISTOGRAM_CODE)
+    return;
+  // Record and release the pending histogram value.
+  UMA_HISTOGRAM_ENUMERATION("ChromiumAndroidLinker.RendererStates",
+                            g_renderer_histogram_code,
+                            MAX_RENDERER_HISTOGRAM_CODE);
+  g_renderer_histogram_code = NO_PENDING_HISTOGRAM_CODE;
+
+  // Record how long it took to load the shared libraries.
+  UMA_HISTOGRAM_TIMES("ChromiumAndroidLinker.RendererLoadTime",
+      base::TimeDelta::FromMilliseconds(g_renderer_library_load_time_ms));
+}
+
+void RecordLibraryPreloaderRendereHistogram() {
+  if (g_library_preloader_renderer_histogram_code_registered) {
+    UmaHistogramSparse("Android.NativeLibraryPreloader.Result.Renderer",
+                       g_library_preloader_renderer_histogram_code);
+  }
+}
+
+#if BUILDFLAG(SUPPORTS_CODE_ORDERING)
+bool ShouldDoOrderfileMemoryOptimization() {
+  return CommandLine::ForCurrentProcess()->HasSwitch(
+      switches::kOrderfileMemoryOptimization);
+}
+#endif
+
+}  // namespace
+
+static void JNI_LibraryLoader_RegisterChromiumAndroidLinkerRendererHistogram(
+    JNIEnv* env,
+    const JavaParamRef<jobject>& jcaller,
+    jboolean requested_shared_relro,
+    jboolean load_at_fixed_address_failed,
+    jlong library_load_time_ms) {
+  // Note a pending histogram value for later recording.
+  if (requested_shared_relro) {
+    g_renderer_histogram_code = load_at_fixed_address_failed
+                                ? LFA_BACKOFF_USED : LFA_SUCCESS;
+  } else {
+    g_renderer_histogram_code = LFA_NOT_ATTEMPTED;
+  }
+
+  g_renderer_library_load_time_ms = library_load_time_ms;
+}
+
+static void JNI_LibraryLoader_RecordChromiumAndroidLinkerBrowserHistogram(
+    JNIEnv* env,
+    const JavaParamRef<jobject>& jcaller,
+    jboolean is_using_browser_shared_relros,
+    jboolean load_at_fixed_address_failed,
+    jint library_load_from_apk_status,
+    jlong library_load_time_ms) {
+  // For low-memory devices, record whether or not we successfully loaded the
+  // browser at a fixed address. Otherwise just record a normal invocation.
+  BrowserHistogramCode histogram_code;
+  if (is_using_browser_shared_relros) {
+    histogram_code = load_at_fixed_address_failed
+                     ? LOW_MEMORY_LFA_BACKOFF_USED : LOW_MEMORY_LFA_SUCCESS;
+  } else {
+    histogram_code = NORMAL_LRA_SUCCESS;
+  }
+  UMA_HISTOGRAM_ENUMERATION("ChromiumAndroidLinker.BrowserStates",
+                            histogram_code,
+                            MAX_BROWSER_HISTOGRAM_CODE);
+
+  // Record the device support for loading a library directly from the APK file.
+  UMA_HISTOGRAM_ENUMERATION(
+      "ChromiumAndroidLinker.LibraryLoadFromApkStatus",
+      static_cast<LibraryLoadFromApkStatusCodes>(library_load_from_apk_status),
+      LIBRARY_LOAD_FROM_APK_STATUS_CODES_MAX);
+
+  // Record how long it took to load the shared libraries.
+  UMA_HISTOGRAM_TIMES("ChromiumAndroidLinker.BrowserLoadTime",
+                      base::TimeDelta::FromMilliseconds(library_load_time_ms));
+}
+
+static void JNI_LibraryLoader_RecordLibraryPreloaderBrowserHistogram(
+    JNIEnv* env,
+    const JavaParamRef<jobject>& jcaller,
+    jint status) {
+  UmaHistogramSparse("Android.NativeLibraryPreloader.Result.Browser", status);
+}
+
+static void JNI_LibraryLoader_RegisterLibraryPreloaderRendererHistogram(
+    JNIEnv* env,
+    const JavaParamRef<jobject>& jcaller,
+    jint status) {
+  g_library_preloader_renderer_histogram_code = status;
+  g_library_preloader_renderer_histogram_code_registered = true;
+}
+
+void SetNativeInitializationHook(
+    NativeInitializationHook native_initialization_hook) {
+  g_native_initialization_hook = native_initialization_hook;
+}
+
+void RecordLibraryLoaderRendererHistograms() {
+  RecordChromiumAndroidLinkerRendererHistogram();
+  RecordLibraryPreloaderRendereHistogram();
+}
+
+void SetLibraryLoadedHook(LibraryLoadedHook* func) {
+  g_registration_callback = func;
+}
+
+static jboolean JNI_LibraryLoader_LibraryLoaded(
+    JNIEnv* env,
+    const JavaParamRef<jobject>& jcaller,
+    jint library_process_type) {
+#if BUILDFLAG(ORDERFILE_INSTRUMENTATION)
+  orderfile::StartDelayedDump();
+#endif
+
+#if BUILDFLAG(SUPPORTS_CODE_ORDERING)
+  if (ShouldDoOrderfileMemoryOptimization()) {
+    NativeLibraryPrefetcher::MadviseForOrderfile();
+  }
+#endif
+
+  if (g_native_initialization_hook &&
+      !g_native_initialization_hook(
+          static_cast<LibraryProcessType>(library_process_type)))
+    return false;
+  if (g_registration_callback && !g_registration_callback(env, nullptr))
+    return false;
+  return true;
+}
+
+void LibraryLoaderExitHook() {
+  if (g_at_exit_manager) {
+    delete g_at_exit_manager;
+    g_at_exit_manager = NULL;
+  }
+}
+
+static void JNI_LibraryLoader_ForkAndPrefetchNativeLibrary(
+    JNIEnv* env,
+    const JavaParamRef<jclass>& clazz) {
+#if BUILDFLAG(SUPPORTS_CODE_ORDERING)
+  return NativeLibraryPrefetcher::ForkAndPrefetchNativeLibrary(
+      ShouldDoOrderfileMemoryOptimization());
+#endif
+}
+
+static jint JNI_LibraryLoader_PercentageOfResidentNativeLibraryCode(
+    JNIEnv* env,
+    const JavaParamRef<jclass>& clazz) {
+#if BUILDFLAG(SUPPORTS_CODE_ORDERING)
+  return NativeLibraryPrefetcher::PercentageOfResidentNativeLibraryCode();
+#else
+  return -1;
+#endif
+}
+
+static void JNI_LibraryLoader_PeriodicallyCollectResidency(
+    JNIEnv* env,
+    const JavaParamRef<jclass>& clazz) {
+#if BUILDFLAG(SUPPORTS_CODE_ORDERING)
+  NativeLibraryPrefetcher::PeriodicallyCollectResidency();
+#else
+  LOG(WARNING) << "Collecting residency is not supported.";
+#endif
+}
+
+void SetVersionNumber(const char* version_number) {
+  g_library_version_number = strdup(version_number);
+}
+
+ScopedJavaLocalRef<jstring> JNI_LibraryLoader_GetVersionNumber(
+    JNIEnv* env,
+    const JavaParamRef<jobject>& jcaller) {
+  return ConvertUTF8ToJavaString(env, g_library_version_number);
+}
+
+void InitAtExitManager() {
+  g_at_exit_manager = new base::AtExitManager();
+}
+
+}  // namespace android
+}  // namespace base
diff --git a/base/android/library_loader/library_loader_hooks.h b/base/android/library_loader/library_loader_hooks.h
new file mode 100644
index 0000000..9dff26a
--- /dev/null
+++ b/base/android/library_loader/library_loader_hooks.h
@@ -0,0 +1,73 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_ANDROID_LIBRARY_LOADER_LIBRARY_LOADER_HOOKS_H_
+#define BASE_ANDROID_LIBRARY_LOADER_LIBRARY_LOADER_HOOKS_H_
+
+#include <jni.h>
+
+#include "base/base_export.h"
+#include "base/callback.h"
+
+namespace base {
+namespace android {
+
+// The process the shared library is loaded in.
+// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.base.library_loader
+enum LibraryProcessType {
+  // The LibraryLoad has not been initialized.
+  PROCESS_UNINITIALIZED = 0,
+  // Shared library is running in browser process.
+  PROCESS_BROWSER = 1,
+  // Shared library is running in child process.
+  PROCESS_CHILD = 2,
+  // Shared library is running in the app that uses webview.
+  PROCESS_WEBVIEW = 3,
+  // Shared library is running in child process as part of webview.
+  PROCESS_WEBVIEW_CHILD = 4,
+};
+
+typedef bool NativeInitializationHook(LibraryProcessType library_process_type);
+
+BASE_EXPORT void SetNativeInitializationHook(
+    NativeInitializationHook native_initialization_hook);
+
+// Record any pending renderer histogram value as histograms.  Pending values
+// are set by RegisterChromiumAndroidLinkerRendererHistogram and
+// RegisterLibraryPreloaderRendererHistogram.
+BASE_EXPORT void RecordLibraryLoaderRendererHistograms();
+
+// Typedef for hook function to be called (indirectly from Java) once the
+// libraries are loaded. The hook function should register the JNI bindings
+// required to start the application. It should return true for success and
+// false for failure.
+// Note: this can't use base::Callback because there is no way of initializing
+// the default callback without using static objects, which we forbid.
+typedef bool LibraryLoadedHook(JNIEnv* env,
+                               jclass clazz);
+
+// Set the hook function to be called (from Java) once the libraries are loaded.
+// SetLibraryLoadedHook may only be called from JNI_OnLoad. The hook function
+// should register the JNI bindings required to start the application.
+
+BASE_EXPORT void SetLibraryLoadedHook(LibraryLoadedHook* func);
+
+// Pass the version name to the loader. This used to check that the library
+// version matches the version expected by Java before completing JNI
+// registration.
+// Note: argument must remain valid at least until library loading is complete.
+BASE_EXPORT void SetVersionNumber(const char* version_number);
+
+// Call on exit to delete the AtExitManager which OnLibraryLoadedOnUIThread
+// created.
+BASE_EXPORT void LibraryLoaderExitHook();
+
+// Initialize AtExitManager, this must be done at the begining of loading
+// shared library.
+void InitAtExitManager();
+
+}  // namespace android
+}  // namespace base
+
+#endif  // BASE_ANDROID_LIBRARY_LOADER_LIBRARY_LOADER_HOOKS_H_
diff --git a/base/android/library_loader/library_prefetcher.cc b/base/android/library_loader/library_prefetcher.cc
new file mode 100644
index 0000000..2ccf6a0
--- /dev/null
+++ b/base/android/library_loader/library_prefetcher.cc
@@ -0,0 +1,333 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/android/library_loader/library_prefetcher.h"
+
+#include <stddef.h>
+#include <sys/mman.h>
+#include <sys/resource.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <algorithm>
+#include <atomic>
+#include <cstdlib>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "base/android/library_loader/anchor_functions.h"
+#include "base/android/orderfile/orderfile_buildflags.h"
+#include "base/bits.h"
+#include "base/files/file.h"
+#include "base/format_macros.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "build/build_config.h"
+
+#if BUILDFLAG(ORDERFILE_INSTRUMENTATION)
+#include "base/android/orderfile/orderfile_instrumentation.h"
+#endif
+
+#if BUILDFLAG(SUPPORTS_CODE_ORDERING)
+
+namespace base {
+namespace android {
+
+namespace {
+
+// Android defines the background priority to this value since at least 2009
+// (see Process.java).
+constexpr int kBackgroundPriority = 10;
+// Valid for all Android architectures.
+constexpr size_t kPageSize = 4096;
+
+// Reads a byte per page between |start| and |end| to force it into the page
+// cache.
+// Heap allocations, syscalls and library functions are not allowed in this
+// function.
+// Returns true for success.
+#if defined(ADDRESS_SANITIZER)
+// Disable AddressSanitizer instrumentation for this function. It is touching
+// memory that hasn't been allocated by the app, though the addresses are
+// valid. Furthermore, this takes place in a child process. See crbug.com/653372
+// for the context.
+__attribute__((no_sanitize_address))
+#endif
+void Prefetch(size_t start, size_t end) {
+  unsigned char* start_ptr = reinterpret_cast<unsigned char*>(start);
+  unsigned char* end_ptr = reinterpret_cast<unsigned char*>(end);
+  unsigned char dummy = 0;
+  for (unsigned char* ptr = start_ptr; ptr < end_ptr; ptr += kPageSize) {
+    // Volatile is required to prevent the compiler from eliminating this
+    // loop.
+    dummy ^= *static_cast<volatile unsigned char*>(ptr);
+  }
+}
+
+// Populates the per-page residency between |start| and |end| in |residency|. If
+// successful, |residency| has the size of |end| - |start| in pages.
+// Returns true for success.
+bool Mincore(size_t start, size_t end, std::vector<unsigned char>* residency) {
+  if (start % kPageSize || end % kPageSize)
+    return false;
+  size_t size = end - start;
+  size_t size_in_pages = size / kPageSize;
+  if (residency->size() != size_in_pages)
+    residency->resize(size_in_pages);
+  int err = HANDLE_EINTR(
+      mincore(reinterpret_cast<void*>(start), size, &(*residency)[0]));
+  PLOG_IF(ERROR, err) << "mincore() failed";
+  return !err;
+}
+
+// Returns the start and end of .text, aligned to the lower and upper page
+// boundaries, respectively.
+std::pair<size_t, size_t> GetTextRange() {
+  // |kStartOfText| may not be at the beginning of a page, since .plt can be
+  // before it, yet in the same mapping for instance.
+  size_t start_page = kStartOfText - kStartOfText % kPageSize;
+  // Set the end to the page on which the beginning of the last symbol is. The
+  // actual symbol may spill into the next page by a few bytes, but this is
+  // outside of the executable code range anyway.
+  size_t end_page = base::bits::Align(kEndOfText, kPageSize);
+  return {start_page, end_page};
+}
+
+// Returns the start and end pages of the unordered section of .text, aligned to
+// lower and upper page boundaries, respectively.
+std::pair<size_t, size_t> GetOrderedTextRange() {
+  size_t start_page = kStartOfOrderedText - kStartOfOrderedText % kPageSize;
+  // kEndOfUnorderedText is not considered ordered, but the byte immediately
+  // before is considered ordered and so can not be contained in the start page.
+  size_t end_page = base::bits::Align(kEndOfOrderedText, kPageSize);
+  return {start_page, end_page};
+}
+
+// Calls madvise(advice) on the specified range. Does nothing if the range is
+// empty.
+void MadviseOnRange(const std::pair<size_t, size_t>& range, int advice) {
+  if (range.first >= range.second) {
+    return;
+  }
+  size_t size = range.second - range.first;
+  int err = madvise(reinterpret_cast<void*>(range.first), size, advice);
+  if (err) {
+    PLOG(ERROR) << "madvise() failed";
+  }
+}
+
+// Timestamp in ns since Unix Epoch, and residency, as returned by mincore().
+struct TimestampAndResidency {
+  uint64_t timestamp_nanos;
+  std::vector<unsigned char> residency;
+
+  TimestampAndResidency(uint64_t timestamp_nanos,
+                        std::vector<unsigned char>&& residency)
+      : timestamp_nanos(timestamp_nanos), residency(residency) {}
+};
+
+// Returns true for success.
+bool CollectResidency(size_t start,
+                      size_t end,
+                      std::vector<TimestampAndResidency>* data) {
+  // Not using base::TimeTicks() to not call too many base:: symbol that would
+  // pollute the reached symbols dumps.
+  struct timespec ts;
+  if (HANDLE_EINTR(clock_gettime(CLOCK_MONOTONIC, &ts))) {
+    PLOG(ERROR) << "Cannot get the time.";
+    return false;
+  }
+  uint64_t now =
+      static_cast<uint64_t>(ts.tv_sec) * 1000 * 1000 * 1000 + ts.tv_nsec;
+  std::vector<unsigned char> residency;
+  if (!Mincore(start, end, &residency))
+    return false;
+
+  data->emplace_back(now, std::move(residency));
+  return true;
+}
+
+void DumpResidency(size_t start,
+                   size_t end,
+                   std::unique_ptr<std::vector<TimestampAndResidency>> data) {
+  auto path = base::FilePath(
+      base::StringPrintf("/data/local/tmp/chrome/residency-%d.txt", getpid()));
+  auto file =
+      base::File(path, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
+  if (!file.IsValid()) {
+    PLOG(ERROR) << "Cannot open file to dump the residency data "
+                << path.value();
+    return;
+  }
+
+  // First line: start-end of text range.
+  CHECK(IsOrderingSane());
+  CHECK_LT(start, kStartOfText);
+  CHECK_LT(kEndOfText, end);
+  auto start_end = base::StringPrintf("%" PRIuS " %" PRIuS "\n",
+                                      kStartOfText - start, kEndOfText - start);
+  file.WriteAtCurrentPos(start_end.c_str(), start_end.size());
+
+  for (const auto& data_point : *data) {
+    auto timestamp =
+        base::StringPrintf("%" PRIu64 " ", data_point.timestamp_nanos);
+    file.WriteAtCurrentPos(timestamp.c_str(), timestamp.size());
+
+    std::vector<char> dump;
+    dump.reserve(data_point.residency.size() + 1);
+    for (auto c : data_point.residency)
+      dump.push_back(c ? '1' : '0');
+    dump[dump.size() - 1] = '\n';
+    file.WriteAtCurrentPos(&dump[0], dump.size());
+  }
+}
+
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+// Used for "LibraryLoader.PrefetchDetailedStatus".
+enum class PrefetchStatus {
+  kSuccess = 0,
+  kWrongOrdering = 1,
+  kForkFailed = 2,
+  kChildProcessCrashed = 3,
+  kChildProcessKilled = 4,
+  kMaxValue = kChildProcessKilled
+};
+
+PrefetchStatus ForkAndPrefetch(bool ordered_only) {
+  if (!IsOrderingSane()) {
+    LOG(WARNING) << "Incorrect code ordering";
+    return PrefetchStatus::kWrongOrdering;
+  }
+
+  // Looking for ranges is done before the fork, to avoid syscalls and/or memory
+  // allocations in the forked process. The child process inherits the lock
+  // state of its parent thread. It cannot rely on being able to acquire any
+  // lock (unless special care is taken in a pre-fork handler), including being
+  // able to call malloc().
+  //
+  // Always prefetch the ordered section first, as it's reached early during
+  // startup, and not necessarily located at the beginning of .text.
+  std::vector<std::pair<size_t, size_t>> ranges = {GetOrderedTextRange()};
+  if (!ordered_only)
+    ranges.push_back(GetTextRange());
+
+  pid_t pid = fork();
+  if (pid == 0) {
+    setpriority(PRIO_PROCESS, 0, kBackgroundPriority);
+    // _exit() doesn't call the atexit() handlers.
+    for (const auto& range : ranges) {
+      Prefetch(range.first, range.second);
+    }
+    _exit(EXIT_SUCCESS);
+  } else {
+    if (pid < 0) {
+      return PrefetchStatus::kForkFailed;
+    }
+    int status;
+    const pid_t result = HANDLE_EINTR(waitpid(pid, &status, 0));
+    if (result == pid) {
+      if (WIFEXITED(status))
+        return PrefetchStatus::kSuccess;
+      if (WIFSIGNALED(status)) {
+        int signal = WTERMSIG(status);
+        switch (signal) {
+          case SIGSEGV:
+          case SIGBUS:
+            return PrefetchStatus::kChildProcessCrashed;
+            break;
+          case SIGKILL:
+          case SIGTERM:
+          default:
+            return PrefetchStatus::kChildProcessKilled;
+        }
+      }
+    }
+    // Should not happen. Per man waitpid(2), errors are:
+    // - EINTR: handled.
+    // - ECHILD if the process doesn't have an unwaited-for child with this PID.
+    // - EINVAL.
+    return PrefetchStatus::kChildProcessKilled;
+  }
+}
+
+}  // namespace
+
+// static
+void NativeLibraryPrefetcher::ForkAndPrefetchNativeLibrary(bool ordered_only) {
+#if BUILDFLAG(ORDERFILE_INSTRUMENTATION)
+  // Avoid forking with orderfile instrumentation because the child process
+  // would create a dump as well.
+  return;
+#endif
+
+  PrefetchStatus status = ForkAndPrefetch(ordered_only);
+  UMA_HISTOGRAM_BOOLEAN("LibraryLoader.PrefetchStatus",
+                        status == PrefetchStatus::kSuccess);
+  UMA_HISTOGRAM_ENUMERATION("LibraryLoader.PrefetchDetailedStatus", status);
+  if (status != PrefetchStatus::kSuccess) {
+    LOG(WARNING) << "Cannot prefetch the library. status = "
+                 << static_cast<int>(status);
+  }
+}
+
+// static
+int NativeLibraryPrefetcher::PercentageOfResidentCode(size_t start,
+                                                      size_t end) {
+  size_t total_pages = 0;
+  size_t resident_pages = 0;
+
+  std::vector<unsigned char> residency;
+  bool ok = Mincore(start, end, &residency);
+  if (!ok)
+    return -1;
+  total_pages += residency.size();
+  resident_pages += std::count_if(residency.begin(), residency.end(),
+                                  [](unsigned char x) { return x & 1; });
+  if (total_pages == 0)
+    return -1;
+  return static_cast<int>((100 * resident_pages) / total_pages);
+}
+
+// static
+int NativeLibraryPrefetcher::PercentageOfResidentNativeLibraryCode() {
+  if (!IsOrderingSane()) {
+    LOG(WARNING) << "Incorrect code ordering";
+    return -1;
+  }
+  const auto& range = GetTextRange();
+  return PercentageOfResidentCode(range.first, range.second);
+}
+
+// static
+void NativeLibraryPrefetcher::PeriodicallyCollectResidency() {
+  CHECK_EQ(static_cast<long>(kPageSize), sysconf(_SC_PAGESIZE));
+
+  const auto& range = GetTextRange();
+  auto data = std::make_unique<std::vector<TimestampAndResidency>>();
+  for (int i = 0; i < 60; ++i) {
+    if (!CollectResidency(range.first, range.second, data.get()))
+      return;
+    usleep(2e5);
+  }
+  DumpResidency(range.first, range.second, std::move(data));
+}
+
+// static
+void NativeLibraryPrefetcher::MadviseForOrderfile() {
+  CHECK(IsOrderingSane());
+  LOG(WARNING) << "Performing experimental madvise from orderfile information";
+  // First MADV_RANDOM on all of text, then turn the ordered text range back to
+  // normal. The ordered range may be placed anywhere within .text.
+  MadviseOnRange(GetTextRange(), MADV_RANDOM);
+  MadviseOnRange(GetOrderedTextRange(), MADV_NORMAL);
+}
+
+}  // namespace android
+}  // namespace base
+#endif  // BUILDFLAG(SUPPORTS_CODE_ORDERING)
diff --git a/base/android/library_loader/library_prefetcher.h b/base/android/library_loader/library_prefetcher.h
new file mode 100644
index 0000000..29f294e
--- /dev/null
+++ b/base/android/library_loader/library_prefetcher.h
@@ -0,0 +1,66 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_ANDROID_LIBRARY_LOADER_LIBRARY_PREFETCHER_H_
+#define BASE_ANDROID_LIBRARY_LOADER_LIBRARY_PREFETCHER_H_
+
+#include <jni.h>
+
+#include <stdint.h>
+
+#include "base/android/library_loader/anchor_functions_buildflags.h"
+#include "base/base_export.h"
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+
+#if BUILDFLAG(SUPPORTS_CODE_ORDERING)
+
+namespace base {
+namespace android {
+
+// Forks and waits for a process prefetching the native library. This is done in
+// a forked process for the following reasons:
+// - Isolating the main process from mistakes in getting the address range, only
+//   crashing the forked process in case of mistake.
+// - Not inflating the memory used by the main process uselessly, which could
+//   increase its likelihood to be killed.
+// The forked process has background priority and, since it is not declared to
+// the Android runtime, can be killed at any time, which is not an issue here.
+class BASE_EXPORT NativeLibraryPrefetcher {
+ public:
+  // Finds the executable code range, forks a low priority process pre-fetching
+  // it wait()s for the process to exit or die. If ordered_only is true, only
+  // the ordered section is prefetched. See GetOrdrderedTextRange() in
+  // library_prefetcher.cc.
+  static void ForkAndPrefetchNativeLibrary(bool ordered_only);
+
+  // Returns the percentage of the native library code currently resident in
+  // memory, or -1 in case of error.
+  static int PercentageOfResidentNativeLibraryCode();
+
+  // Collects residency for the native library executable multiple times, then
+  // dumps it to disk.
+  static void PeriodicallyCollectResidency();
+
+  // Calls madvise() on the native library executable, using orderfile
+  // information to decide how to advise each part of the library.
+  static void MadviseForOrderfile();
+
+ private:
+  // Returns the percentage of [start, end] currently resident in
+  // memory, or -1 in case of error.
+  static int PercentageOfResidentCode(size_t start, size_t end);
+
+  FRIEND_TEST_ALL_PREFIXES(NativeLibraryPrefetcherTest,
+                           TestPercentageOfResidentCode);
+
+  DISALLOW_IMPLICIT_CONSTRUCTORS(NativeLibraryPrefetcher);
+};
+
+}  // namespace android
+}  // namespace base
+
+#endif  // BUILDFLAG(SUPPORTS_CODE_ORDERING)
+
+#endif  // BASE_ANDROID_LIBRARY_LOADER_LIBRARY_PREFETCHER_H_
diff --git a/base/android/library_loader/library_prefetcher_unittest.cc b/base/android/library_loader/library_prefetcher_unittest.cc
new file mode 100644
index 0000000..ebb0d48
--- /dev/null
+++ b/base/android/library_loader/library_prefetcher_unittest.cc
@@ -0,0 +1,46 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/android/library_loader/library_prefetcher.h"
+
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/mman.h>
+#include "base/android/library_loader/anchor_functions_buildflags.h"
+#include "base/memory/shared_memory.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if BUILDFLAG(SUPPORTS_CODE_ORDERING)
+namespace base {
+namespace android {
+
+// Fails with ASAN, crbug.com/570423.
+#if !defined(ADDRESS_SANITIZER)
+namespace {
+const size_t kPageSize = 4096;
+}  // namespace
+
+TEST(NativeLibraryPrefetcherTest, TestPercentageOfResidentCode) {
+  size_t length = 4 * kPageSize;
+  base::SharedMemory shared_mem;
+  ASSERT_TRUE(shared_mem.CreateAndMapAnonymous(length));
+  void* address = shared_mem.memory();
+  size_t start = reinterpret_cast<size_t>(address);
+  size_t end = start + length;
+
+  // Remove everything.
+  ASSERT_EQ(0, madvise(address, length, MADV_DONTNEED));
+  EXPECT_EQ(0, NativeLibraryPrefetcher::PercentageOfResidentCode(start, end));
+
+  // Get everything back.
+  ASSERT_EQ(0, mlock(address, length));
+  EXPECT_EQ(100, NativeLibraryPrefetcher::PercentageOfResidentCode(start, end));
+  munlock(address, length);
+}
+#endif  // !defined(ADDRESS_SANITIZER)
+
+}  // namespace android
+}  // namespace base
+#endif  // BUILDFLAG(SUPPORTS_CODE_ORDERING)
diff --git a/base/android/linker/config.gni b/base/android/linker/config.gni
new file mode 100644
index 0000000..27793ff
--- /dev/null
+++ b/base/android/linker/config.gni
@@ -0,0 +1,13 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/android/config.gni")
+import("//build/config/compiler/compiler.gni")
+import("//build/config/sanitizers/sanitizers.gni")
+
+# Chromium linker doesn't reliably support loading multiple libraries;
+# disable for component builds, see crbug.com/657093.
+# Chromium linker causes instrumentation to return incorrect results.
+chromium_linker_supported =
+    !is_component_build && !enable_profiling && !use_order_profiling && !is_asan
diff --git a/base/android/linker/linker_jni.cc b/base/android/linker/linker_jni.cc
new file mode 100644
index 0000000..ba632db
--- /dev/null
+++ b/base/android/linker/linker_jni.cc
@@ -0,0 +1,696 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This is the Android-specific Chromium linker, a tiny shared library
+// implementing a custom dynamic linker that can be used to load the
+// real Chromium libraries.
+
+// The main point of this linker is to be able to share the RELRO
+// section of libchrome.so (or equivalent) between renderer processes.
+
+// This source code *cannot* depend on anything from base/ or the C++
+// STL, to keep the final library small, and avoid ugly dependency issues.
+
+#include <android/log.h>
+#include <jni.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+
+#include "build/build_config.h"
+
+#include <crazy_linker.h>
+
+// Set this to 1 to enable debug traces to the Android log.
+// Note that LOG() from "base/logging.h" cannot be used, since it is
+// in base/ which hasn't been loaded yet.
+#define DEBUG 0
+
+#define TAG "cr_ChromiumAndroidLinker"
+
+#if DEBUG
+#define LOG_INFO(FORMAT, ...)                                             \
+  __android_log_print(ANDROID_LOG_INFO, TAG, "%s: " FORMAT, __FUNCTION__, \
+                      ##__VA_ARGS__)
+#else
+#define LOG_INFO(FORMAT, ...) ((void)0)
+#endif
+#define LOG_ERROR(FORMAT, ...)                                             \
+  __android_log_print(ANDROID_LOG_ERROR, TAG, "%s: " FORMAT, __FUNCTION__, \
+                      ##__VA_ARGS__)
+
+#define UNUSED __attribute__((unused))
+
+// See commentary in crazy_linker_elf_loader.cpp for the effect of setting
+// this. If changing there, change here also.
+//
+// For more, see:
+//   https://crbug.com/504410
+#define RESERVE_BREAKPAD_GUARD_REGION 1
+
+#if defined(ARCH_CPU_X86)
+// Dalvik JIT generated code doesn't guarantee 16-byte stack alignment on
+// x86 - use force_align_arg_pointer to realign the stack at the JNI
+// boundary. https://crbug.com/655248
+#define JNI_GENERATOR_EXPORT \
+  extern "C" __attribute__((visibility("default"), force_align_arg_pointer))
+#else
+#define JNI_GENERATOR_EXPORT extern "C" __attribute__((visibility("default")))
+#endif
+
+namespace chromium_android_linker {
+
+namespace {
+
+// Larger than the largest library we might attempt to load.
+constexpr size_t kAddressSpaceReservationSize = 192 * 1024 * 1024;
+
+// Size of any Breakpad guard region. 16MB is comfortably larger than the
+// ~6MB relocation packing of the current 64-bit libchrome.so, the largest we
+// expect to encounter.
+#if RESERVE_BREAKPAD_GUARD_REGION
+constexpr size_t kBreakpadGuardRegionBytes = 16 * 1024 * 1024;
+#endif
+
+// A simple scoped UTF String class that can be initialized from
+// a Java jstring handle. Modeled like std::string, which cannot
+// be used here.
+class String {
+ public:
+  String(JNIEnv* env, jstring str);
+
+  inline ~String() { ::free(ptr_); }
+
+  inline const char* c_str() const { return ptr_ ? ptr_ : ""; }
+  inline size_t size() const { return size_; }
+
+ private:
+  char* ptr_;
+  size_t size_;
+};
+
+// Simple scoped UTF String class constructor.
+String::String(JNIEnv* env, jstring str) {
+  size_ = env->GetStringUTFLength(str);
+  ptr_ = static_cast<char*>(::malloc(size_ + 1));
+
+  // Note: This runs before browser native code is loaded, and so cannot
+  // rely on anything from base/. This means that we must use
+  // GetStringUTFChars() and not base::android::ConvertJavaStringToUTF8().
+  //
+  // GetStringUTFChars() suffices because the only strings used here are
+  // paths to APK files or names of shared libraries, all of which are
+  // plain ASCII, defined and hard-coded by the Chromium Android build.
+  //
+  // For more: see
+  //   https://crbug.com/508876
+  //
+  // Note: GetStringUTFChars() returns Java UTF-8 bytes. This is good
+  // enough for the linker though.
+  const char* bytes = env->GetStringUTFChars(str, nullptr);
+  ::memcpy(ptr_, bytes, size_);
+  ptr_[size_] = '\0';
+
+  env->ReleaseStringUTFChars(str, bytes);
+}
+
+// Find the jclass JNI reference corresponding to a given |class_name|.
+// |env| is the current JNI environment handle.
+// On success, return true and set |*clazz|.
+bool InitClassReference(JNIEnv* env, const char* class_name, jclass* clazz) {
+  *clazz = env->FindClass(class_name);
+  if (!*clazz) {
+    LOG_ERROR("Could not find class for %s", class_name);
+    return false;
+  }
+  return true;
+}
+
+// Initialize a jfieldID corresponding to the field of a given |clazz|,
+// with name |field_name| and signature |field_sig|.
+// |env| is the current JNI environment handle.
+// On success, return true and set |*field_id|.
+bool InitFieldId(JNIEnv* env,
+                 jclass clazz,
+                 const char* field_name,
+                 const char* field_sig,
+                 jfieldID* field_id) {
+  *field_id = env->GetFieldID(clazz, field_name, field_sig);
+  if (!*field_id) {
+    LOG_ERROR("Could not find ID for field '%s'", field_name);
+    return false;
+  }
+  LOG_INFO("Found ID %p for field '%s'", *field_id, field_name);
+  return true;
+}
+
+// Initialize a jmethodID corresponding to the static method of a given
+// |clazz|, with name |method_name| and signature |method_sig|.
+// |env| is the current JNI environment handle.
+// On success, return true and set |*method_id|.
+bool InitStaticMethodId(JNIEnv* env,
+                        jclass clazz,
+                        const char* method_name,
+                        const char* method_sig,
+                        jmethodID* method_id) {
+  *method_id = env->GetStaticMethodID(clazz, method_name, method_sig);
+  if (!*method_id) {
+    LOG_ERROR("Could not find ID for static method '%s'", method_name);
+    return false;
+  }
+  LOG_INFO("Found ID %p for static method '%s'", *method_id, method_name);
+  return true;
+}
+
+// Initialize a jfieldID corresponding to the static field of a given |clazz|,
+// with name |field_name| and signature |field_sig|.
+// |env| is the current JNI environment handle.
+// On success, return true and set |*field_id|.
+bool InitStaticFieldId(JNIEnv* env,
+                       jclass clazz,
+                       const char* field_name,
+                       const char* field_sig,
+                       jfieldID* field_id) {
+  *field_id = env->GetStaticFieldID(clazz, field_name, field_sig);
+  if (!*field_id) {
+    LOG_ERROR("Could not find ID for static field '%s'", field_name);
+    return false;
+  }
+  LOG_INFO("Found ID %p for static field '%s'", *field_id, field_name);
+  return true;
+}
+
+// Initialize a jint corresponding to the static integer field of a class
+// with class name |class_name| and field name |field_name|.
+// |env| is the current JNI environment handle.
+// On success, return true and set |*value|.
+bool InitStaticInt(JNIEnv* env,
+                   const char* class_name,
+                   const char* field_name,
+                   jint* value) {
+  jclass clazz;
+  if (!InitClassReference(env, class_name, &clazz))
+    return false;
+
+  jfieldID field_id;
+  if (!InitStaticFieldId(env, clazz, field_name, "I", &field_id))
+    return false;
+
+  *value = env->GetStaticIntField(clazz, field_id);
+  LOG_INFO("Found value %d for class '%s', static field '%s'",
+           *value, class_name, field_name);
+
+  return true;
+}
+
+// A class used to model the field IDs of the org.chromium.base.Linker
+// LibInfo inner class, used to communicate data with the Java side
+// of the linker.
+struct LibInfo_class {
+  jfieldID load_address_id;
+  jfieldID load_size_id;
+  jfieldID relro_start_id;
+  jfieldID relro_size_id;
+  jfieldID relro_fd_id;
+
+  // Initialize an instance.
+  bool Init(JNIEnv* env) {
+    jclass clazz;
+    if (!InitClassReference(
+            env, "org/chromium/base/library_loader/Linker$LibInfo", &clazz)) {
+      return false;
+    }
+
+    return InitFieldId(env, clazz, "mLoadAddress", "J", &load_address_id) &&
+           InitFieldId(env, clazz, "mLoadSize", "J", &load_size_id) &&
+           InitFieldId(env, clazz, "mRelroStart", "J", &relro_start_id) &&
+           InitFieldId(env, clazz, "mRelroSize", "J", &relro_size_id) &&
+           InitFieldId(env, clazz, "mRelroFd", "I", &relro_fd_id);
+  }
+
+  void SetLoadInfo(JNIEnv* env,
+                   jobject library_info_obj,
+                   size_t load_address,
+                   size_t load_size) {
+    env->SetLongField(library_info_obj, load_address_id, load_address);
+    env->SetLongField(library_info_obj, load_size_id, load_size);
+  }
+
+  void SetRelroInfo(JNIEnv* env,
+                    jobject library_info_obj,
+                    size_t relro_start,
+                    size_t relro_size,
+                    int relro_fd) {
+    env->SetLongField(library_info_obj, relro_start_id, relro_start);
+    env->SetLongField(library_info_obj, relro_size_id, relro_size);
+    env->SetIntField(library_info_obj, relro_fd_id, relro_fd);
+  }
+
+  // Use this instance to convert a RelroInfo reference into
+  // a crazy_library_info_t.
+  void GetRelroInfo(JNIEnv* env,
+                    jobject library_info_obj,
+                    size_t* relro_start,
+                    size_t* relro_size,
+                    int* relro_fd) {
+    if (relro_start) {
+      *relro_start = static_cast<size_t>(
+          env->GetLongField(library_info_obj, relro_start_id));
+    }
+
+    if (relro_size) {
+      *relro_size = static_cast<size_t>(
+          env->GetLongField(library_info_obj, relro_size_id));
+    }
+
+    if (relro_fd) {
+      *relro_fd = env->GetIntField(library_info_obj, relro_fd_id);
+    }
+  }
+};
+
+// Variable containing LibInfo for the loaded library.
+LibInfo_class s_lib_info_fields;
+
+// Return true iff |address| is a valid address for the target CPU.
+inline bool IsValidAddress(jlong address) {
+  return static_cast<jlong>(static_cast<size_t>(address)) == address;
+}
+
+// The linker uses a single crazy_context_t object created on demand.
+// There is no need to protect this against concurrent access, locking
+// is already handled on the Java side.
+crazy_context_t* GetCrazyContext() {
+  static crazy_context_t* s_crazy_context = nullptr;
+
+  if (!s_crazy_context) {
+    // Create new context.
+    s_crazy_context = crazy_context_create();
+
+    // Ensure libraries located in the same directory as the linker
+    // can be loaded before system ones.
+    crazy_context_add_search_path_for_address(
+        s_crazy_context, reinterpret_cast<void*>(&GetCrazyContext));
+  }
+
+  return s_crazy_context;
+}
+
+// A scoped crazy_library_t that automatically closes the handle
+// on scope exit, unless Release() has been called.
+class ScopedLibrary {
+ public:
+  ScopedLibrary() : lib_(nullptr) {}
+
+  ~ScopedLibrary() {
+    if (lib_)
+      crazy_library_close_with_context(lib_, GetCrazyContext());
+  }
+
+  crazy_library_t* Get() { return lib_; }
+
+  crazy_library_t** GetPtr() { return &lib_; }
+
+  crazy_library_t* Release() {
+    crazy_library_t* ret = lib_;
+    lib_ = nullptr;
+    return ret;
+  }
+
+ private:
+  crazy_library_t* lib_;
+};
+
+// Retrieve the SDK build version and pass it into the crazy linker. This
+// needs to be done early in initialization, before any other crazy linker
+// code is run.
+// |env| is the current JNI environment handle.
+// On success, return true.
+bool InitSDKVersionInfo(JNIEnv* env) {
+  jint value = 0;
+  if (!InitStaticInt(env, "android/os/Build$VERSION", "SDK_INT", &value))
+    return false;
+
+  crazy_set_sdk_build_version(static_cast<int>(value));
+  LOG_INFO("Set SDK build version to %d", static_cast<int>(value));
+
+  return true;
+}
+
+}  // namespace
+
+// Use Android ASLR to create a random address into which we expect to be
+// able to load libraries. Note that this is probabilistic; we unmap the
+// address we get from mmap and assume we can re-map into it later. This
+// works the majority of the time. If it doesn't, client code backs out and
+// then loads the library normally at any available address.
+// |env| is the current JNI environment handle, and |clazz| a class.
+// Returns the address selected by ASLR, or 0 on error.
+JNI_GENERATOR_EXPORT jlong
+Java_org_chromium_base_library_1loader_Linker_nativeGetRandomBaseLoadAddress(
+    JNIEnv* env,
+    jclass clazz) {
+  size_t bytes = kAddressSpaceReservationSize;
+
+#if RESERVE_BREAKPAD_GUARD_REGION
+  // Pad the requested address space size for a Breakpad guard region.
+  bytes += kBreakpadGuardRegionBytes;
+#endif
+
+  void* address =
+      mmap(nullptr, bytes, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+  if (address == MAP_FAILED) {
+    LOG_INFO("Random base load address not determinable");
+    return 0;
+  }
+  munmap(address, bytes);
+
+#if RESERVE_BREAKPAD_GUARD_REGION
+  // Allow for a Breakpad guard region ahead of the returned address.
+  address = reinterpret_cast<void*>(
+      reinterpret_cast<uintptr_t>(address) + kBreakpadGuardRegionBytes);
+#endif
+
+  LOG_INFO("Random base load address is %p", address);
+  return static_cast<jlong>(reinterpret_cast<uintptr_t>(address));
+}
+
+// We identify the abi tag for which the linker is running. This allows
+// us to select the library which matches the abi of the linker.
+
+#if defined(__arm__) && defined(__ARM_ARCH_7A__)
+#define CURRENT_ABI "armeabi-v7a"
+#elif defined(__arm__)
+#define CURRENT_ABI "armeabi"
+#elif defined(__i386__)
+#define CURRENT_ABI "x86"
+#elif defined(__mips__)
+#define CURRENT_ABI "mips"
+#elif defined(__x86_64__)
+#define CURRENT_ABI "x86_64"
+#elif defined(__aarch64__)
+#define CURRENT_ABI "arm64-v8a"
+#else
+#error "Unsupported target abi"
+#endif
+
+// Add a zip archive file path to the context's current search path
+// list. Making it possible to load libraries directly from it.
+JNI_GENERATOR_EXPORT bool
+Java_org_chromium_base_library_1loader_Linker_nativeAddZipArchivePath(
+    JNIEnv* env,
+    jclass clazz,
+    jstring apk_path_obj) {
+  String apk_path(env, apk_path_obj);
+
+  char search_path[512];
+  snprintf(search_path, sizeof(search_path), "%s!lib/" CURRENT_ABI "/",
+           apk_path.c_str());
+
+  crazy_context_t* context = GetCrazyContext();
+  crazy_context_add_search_path(context, search_path);
+  return true;
+}
+
+// Load a library with the chromium linker. This will also call its
+// JNI_OnLoad() method, which shall register its methods. Note that
+// lazy native method resolution will _not_ work after this, because
+// Dalvik uses the system's dlsym() which won't see the new library,
+// so explicit registration is mandatory.
+//
+// |env| is the current JNI environment handle.
+// |clazz| is the static class handle for org.chromium.base.Linker,
+// and is ignored here.
+// |library_name| is the library name (e.g. libfoo.so).
+// |load_address| is an explicit load address.
+// |lib_info_obj| is a LibInfo handle used to communicate information
+// with the Java side.
+// Return true on success.
+JNI_GENERATOR_EXPORT bool
+Java_org_chromium_base_library_1loader_Linker_nativeLoadLibrary(
+    JNIEnv* env,
+    jclass clazz,
+    jstring lib_name_obj,
+    jlong load_address,
+    jobject lib_info_obj) {
+  String library_name(env, lib_name_obj);
+  LOG_INFO("Called for %s, at address 0x%llx", library_name, load_address);
+  crazy_context_t* context = GetCrazyContext();
+
+  if (!IsValidAddress(load_address)) {
+    LOG_ERROR("Invalid address 0x%llx",
+              static_cast<unsigned long long>(load_address));
+    return false;
+  }
+
+  // Set the desired load address (0 means randomize it).
+  crazy_context_set_load_address(context, static_cast<size_t>(load_address));
+
+  ScopedLibrary library;
+  if (!crazy_library_open(library.GetPtr(), library_name.c_str(), context)) {
+    return false;
+  }
+
+  crazy_library_info_t info;
+  if (!crazy_library_get_info(library.Get(), context, &info)) {
+    LOG_ERROR("Could not get library information for %s: %s",
+              library_name.c_str(), crazy_context_get_error(context));
+    return false;
+  }
+
+  // Release library object to keep it alive after the function returns.
+  library.Release();
+
+  s_lib_info_fields.SetLoadInfo(env, lib_info_obj, info.load_address,
+                                info.load_size);
+  LOG_INFO("Success loading library %s", library_name.c_str());
+  return true;
+}
+
+// Class holding the Java class and method ID for the Java side Linker
+// postCallbackOnMainThread method.
+struct JavaCallbackBindings_class {
+  jclass clazz;
+  jmethodID method_id;
+
+  // Initialize an instance.
+  bool Init(JNIEnv* env, jclass linker_class) {
+    clazz = reinterpret_cast<jclass>(env->NewGlobalRef(linker_class));
+    return InitStaticMethodId(env, linker_class, "postCallbackOnMainThread",
+                              "(J)V", &method_id);
+  }
+};
+
+static JavaCallbackBindings_class s_java_callback_bindings;
+
+// Designated receiver function for callbacks from Java. Its name is known
+// to the Java side.
+// |env| is the current JNI environment handle and is ignored here.
+// |clazz| is the static class handle for org.chromium.base.Linker,
+// and is ignored here.
+// |arg| is a pointer to an allocated crazy_callback_t, deleted after use.
+JNI_GENERATOR_EXPORT void
+Java_org_chromium_base_library_1loader_Linker_nativeRunCallbackOnUiThread(
+    JNIEnv* env,
+    jclass clazz,
+    jlong arg) {
+  crazy_callback_t* callback = reinterpret_cast<crazy_callback_t*>(arg);
+
+  LOG_INFO("Called back from java with handler %p, opaque %p",
+           callback->handler, callback->opaque);
+
+  crazy_callback_run(callback);
+  delete callback;
+}
+
+// Request a callback from Java. The supplied crazy_callback_t is valid only
+// for the duration of this call, so we copy it to a newly allocated
+// crazy_callback_t and then call the Java side's postCallbackOnMainThread.
+// This will call back to to our RunCallbackOnUiThread some time
+// later on the UI thread.
+// |callback_request| is a crazy_callback_t.
+// |poster_opaque| is unused.
+// Returns true if the callback request succeeds.
+static bool PostForLaterExecution(crazy_callback_t* callback_request,
+                                  void* poster_opaque UNUSED) {
+  crazy_context_t* context = GetCrazyContext();
+
+  JavaVM* vm;
+  int minimum_jni_version;
+  crazy_context_get_java_vm(context, reinterpret_cast<void**>(&vm),
+                            &minimum_jni_version);
+
+  // Do not reuse JNIEnv from JNI_OnLoad, but retrieve our own.
+  JNIEnv* env;
+  if (JNI_OK !=
+      vm->GetEnv(reinterpret_cast<void**>(&env), minimum_jni_version)) {
+    LOG_ERROR("Could not create JNIEnv");
+    return false;
+  }
+
+  // Copy the callback; the one passed as an argument may be temporary.
+  crazy_callback_t* callback = new crazy_callback_t();
+  *callback = *callback_request;
+
+  LOG_INFO("Calling back to java with handler %p, opaque %p", callback->handler,
+           callback->opaque);
+
+  jlong arg = static_cast<jlong>(reinterpret_cast<uintptr_t>(callback));
+
+  env->CallStaticVoidMethod(s_java_callback_bindings.clazz,
+                            s_java_callback_bindings.method_id, arg);
+
+  // Back out and return false if we encounter a JNI exception.
+  if (env->ExceptionCheck() == JNI_TRUE) {
+    env->ExceptionDescribe();
+    env->ExceptionClear();
+    delete callback;
+    return false;
+  }
+
+  return true;
+}
+
+JNI_GENERATOR_EXPORT jboolean
+Java_org_chromium_base_library_1loader_Linker_nativeCreateSharedRelro(
+    JNIEnv* env,
+    jclass clazz,
+    jstring library_name,
+    jlong load_address,
+    jobject lib_info_obj) {
+  String lib_name(env, library_name);
+
+  LOG_INFO("Called for %s", lib_name.c_str());
+
+  if (!IsValidAddress(load_address)) {
+    LOG_ERROR("Invalid address 0x%llx",
+              static_cast<unsigned long long>(load_address));
+    return false;
+  }
+
+  ScopedLibrary library;
+  if (!crazy_library_find_by_name(lib_name.c_str(), library.GetPtr())) {
+    LOG_ERROR("Could not find %s", lib_name.c_str());
+    return false;
+  }
+
+  crazy_context_t* context = GetCrazyContext();
+  size_t relro_start = 0;
+  size_t relro_size = 0;
+  int relro_fd = -1;
+
+  if (!crazy_library_create_shared_relro(
+          library.Get(), context, static_cast<size_t>(load_address),
+          &relro_start, &relro_size, &relro_fd)) {
+    LOG_ERROR("Could not create shared RELRO sharing for %s: %s\n",
+              lib_name.c_str(), crazy_context_get_error(context));
+    return false;
+  }
+
+  s_lib_info_fields.SetRelroInfo(env, lib_info_obj, relro_start, relro_size,
+                                 relro_fd);
+  return true;
+}
+
+JNI_GENERATOR_EXPORT jboolean
+Java_org_chromium_base_library_1loader_Linker_nativeUseSharedRelro(
+    JNIEnv* env,
+    jclass clazz,
+    jstring library_name,
+    jobject lib_info_obj) {
+  String lib_name(env, library_name);
+
+  LOG_INFO("Called for %s, lib_info_ref=%p", lib_name.c_str(), lib_info_obj);
+
+  ScopedLibrary library;
+  if (!crazy_library_find_by_name(lib_name.c_str(), library.GetPtr())) {
+    LOG_ERROR("Could not find %s", lib_name.c_str());
+    return false;
+  }
+
+  crazy_context_t* context = GetCrazyContext();
+  size_t relro_start = 0;
+  size_t relro_size = 0;
+  int relro_fd = -1;
+  s_lib_info_fields.GetRelroInfo(env, lib_info_obj, &relro_start, &relro_size,
+                                 &relro_fd);
+
+  LOG_INFO("library=%s relro start=%p size=%p fd=%d", lib_name.c_str(),
+           (void*)relro_start, (void*)relro_size, relro_fd);
+
+  if (!crazy_library_use_shared_relro(library.Get(), context, relro_start,
+                                      relro_size, relro_fd)) {
+    LOG_ERROR("Could not use shared RELRO for %s: %s", lib_name.c_str(),
+              crazy_context_get_error(context));
+    return false;
+  }
+
+  LOG_INFO("Library %s using shared RELRO section!", lib_name.c_str());
+
+  return true;
+}
+
+static bool LinkerJNIInit(JavaVM* vm, JNIEnv* env) {
+  LOG_INFO("Entering");
+
+  // Initialize SDK version info.
+  LOG_INFO("Retrieving SDK version info");
+  if (!InitSDKVersionInfo(env))
+    return false;
+
+  // Find LibInfo field ids.
+  LOG_INFO("Caching field IDs");
+  if (!s_lib_info_fields.Init(env)) {
+    return false;
+  }
+
+  // Register native methods.
+  jclass linker_class;
+  if (!InitClassReference(env, "org/chromium/base/library_loader/Linker",
+                          &linker_class))
+    return false;
+
+  // Resolve and save the Java side Linker callback class and method.
+  LOG_INFO("Resolving callback bindings");
+  if (!s_java_callback_bindings.Init(env, linker_class)) {
+    return false;
+  }
+
+  // Save JavaVM* handle into context.
+  crazy_context_t* context = GetCrazyContext();
+  crazy_context_set_java_vm(context, vm, JNI_VERSION_1_4);
+
+  // Register the function that the crazy linker can call to post code
+  // for later execution.
+  crazy_context_set_callback_poster(context, &PostForLaterExecution, nullptr);
+
+  return true;
+}
+
+// JNI_OnLoad() hook called when the linker library is loaded through
+// the regular System.LoadLibrary) API. This shall save the Java VM
+// handle and initialize LibInfo fields.
+jint JNI_OnLoad(JavaVM* vm, void* reserved) {
+  LOG_INFO("Entering");
+  // Get new JNIEnv
+  JNIEnv* env;
+  if (JNI_OK != vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_4)) {
+    LOG_ERROR("Could not create JNIEnv");
+    return -1;
+  }
+
+  // Initialize linker base and implementations.
+  if (!LinkerJNIInit(vm, env)) {
+    return -1;
+  }
+
+  LOG_INFO("Done");
+  return JNI_VERSION_1_4;
+}
+
+}  // namespace chromium_android_linker
+
+jint JNI_OnLoad(JavaVM* vm, void* reserved) {
+  return chromium_android_linker::JNI_OnLoad(vm, reserved);
+}
diff --git a/base/android/locale_utils.cc b/base/android/locale_utils.cc
new file mode 100644
index 0000000..b3a2346
--- /dev/null
+++ b/base/android/locale_utils.cc
@@ -0,0 +1,27 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/android/locale_utils.h"
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "jni/LocaleUtils_jni.h"
+
+namespace base {
+namespace android {
+
+std::string GetDefaultCountryCode() {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  return ConvertJavaStringToUTF8(Java_LocaleUtils_getDefaultCountryCode(env));
+}
+
+std::string GetDefaultLocaleString() {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  ScopedJavaLocalRef<jstring> locale =
+      Java_LocaleUtils_getDefaultLocaleString(env);
+  return ConvertJavaStringToUTF8(locale);
+}
+
+}  // namespace android
+}  // namespace base
diff --git a/base/android/locale_utils.h b/base/android/locale_utils.h
new file mode 100644
index 0000000..be68890
--- /dev/null
+++ b/base/android/locale_utils.h
@@ -0,0 +1,25 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_ANDROID_LOCALE_UTILS_H_
+#define BASE_ANDROID_LOCALE_UTILS_H_
+
+#include <jni.h>
+
+#include <string>
+
+#include "base/base_export.h"
+
+namespace base {
+namespace android {
+
+BASE_EXPORT std::string GetDefaultCountryCode();
+
+// Return the current default locale of the device as string.
+BASE_EXPORT std::string GetDefaultLocaleString();
+
+}  // namespace android
+}  // namespace base
+
+#endif  // BASE_ANDROID_LOCALE_UTILS_H_
diff --git a/base/android/memory_pressure_listener_android.cc b/base/android/memory_pressure_listener_android.cc
new file mode 100644
index 0000000..cab66e1
--- /dev/null
+++ b/base/android/memory_pressure_listener_android.cc
@@ -0,0 +1,30 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/android/memory_pressure_listener_android.h"
+
+#include "base/memory/memory_pressure_listener.h"
+#include "jni/MemoryPressureListener_jni.h"
+
+using base::android::JavaParamRef;
+
+// Defined and called by JNI.
+static void JNI_MemoryPressureListener_OnMemoryPressure(
+    JNIEnv* env,
+    const JavaParamRef<jclass>& clazz,
+    jint memory_pressure_level) {
+  base::MemoryPressureListener::NotifyMemoryPressure(
+      static_cast<base::MemoryPressureListener::MemoryPressureLevel>(
+          memory_pressure_level));
+}
+
+namespace base {
+namespace android {
+
+void MemoryPressureListenerAndroid::Initialize(JNIEnv* env) {
+  Java_MemoryPressureListener_addNativeCallback(env);
+}
+
+}  // namespace android
+}  // namespace base
diff --git a/base/android/memory_pressure_listener_android.h b/base/android/memory_pressure_listener_android.h
new file mode 100644
index 0000000..9edfd42
--- /dev/null
+++ b/base/android/memory_pressure_listener_android.h
@@ -0,0 +1,29 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_ANDROID_MEMORY_PRESSURE_LISTENER_ANDROID_H_
+#define BASE_ANDROID_MEMORY_PRESSURE_LISTENER_ANDROID_H_
+
+#include "base/android/jni_android.h"
+#include "base/macros.h"
+
+namespace base {
+namespace android {
+
+// Implements the C++ counter part of MemoryPressureListener.java
+class BASE_EXPORT MemoryPressureListenerAndroid {
+ public:
+  static void Initialize(JNIEnv* env);
+
+  // Called by JNI.
+  static void OnMemoryPressure(int memory_pressure_type);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MemoryPressureListenerAndroid);
+};
+
+}  // namespace android
+}  // namespace base
+
+#endif  // BASE_ANDROID_MEMORY_PRESSURE_LISTENER_ANDROID_H_
diff --git a/base/android/orderfile/BUILD.gn b/base/android/orderfile/BUILD.gn
deleted file mode 100644
index ff0bfff..0000000
--- a/base/android/orderfile/BUILD.gn
+++ /dev/null
@@ -1,34 +0,0 @@
-# Copyright 2018 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//build/config/android/config.gni")
-
-if (use_order_profiling && target_cpu == "arm") {
-  static_library("orderfile_instrumentation") {
-    sources = [
-      "orderfile_instrumentation.cc",
-      "orderfile_instrumentation.h",
-    ]
-    deps = [
-      "//base",
-    ]
-  }
-
-  executable("orderfile_instrumentation_perftest") {
-    testonly = true
-
-    sources = [
-      "orderfile_instrumentation_perftest.cc",
-    ]
-
-    deps = [
-      ":orderfile_instrumentation",
-      "//base",
-      "//testing/gtest",
-      "//testing/perf",
-    ]
-
-    configs -= [ "//build/config/android:default_orderfile_instrumentation" ]
-  }
-}
diff --git a/base/android/path_service_android.cc b/base/android/path_service_android.cc
new file mode 100644
index 0000000..51be530
--- /dev/null
+++ b/base/android/path_service_android.cc
@@ -0,0 +1,23 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "base/files/file_path.h"
+#include "base/path_service.h"
+#include "jni/PathService_jni.h"
+
+namespace base {
+namespace android {
+
+void JNI_PathService_Override(JNIEnv* env,
+                              const JavaParamRef<jclass>& clazz,
+                              jint what,
+                              const JavaParamRef<jstring>& path) {
+  FilePath file_path(ConvertJavaStringToUTF8(env, path));
+  PathService::Override(what, file_path);
+}
+
+}  // namespace android
+}  // namespace base
diff --git a/base/android/path_utils.cc b/base/android/path_utils.cc
new file mode 100644
index 0000000..d1f6d43
--- /dev/null
+++ b/base/android/path_utils.cc
@@ -0,0 +1,82 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/android/path_utils.h"
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_array.h"
+#include "base/android/jni_string.h"
+#include "base/android/scoped_java_ref.h"
+#include "base/files/file_path.h"
+
+#include "jni/PathUtils_jni.h"
+
+namespace base {
+namespace android {
+
+bool GetDataDirectory(FilePath* result) {
+  JNIEnv* env = AttachCurrentThread();
+  ScopedJavaLocalRef<jstring> path = Java_PathUtils_getDataDirectory(env);
+  FilePath data_path(ConvertJavaStringToUTF8(path));
+  *result = data_path;
+  return true;
+}
+
+bool GetCacheDirectory(FilePath* result) {
+  JNIEnv* env = AttachCurrentThread();
+  ScopedJavaLocalRef<jstring> path = Java_PathUtils_getCacheDirectory(env);
+  FilePath cache_path(ConvertJavaStringToUTF8(path));
+  *result = cache_path;
+  return true;
+}
+
+bool GetThumbnailCacheDirectory(FilePath* result) {
+  JNIEnv* env = AttachCurrentThread();
+  ScopedJavaLocalRef<jstring> path =
+      Java_PathUtils_getThumbnailCacheDirectory(env);
+  FilePath thumbnail_cache_path(ConvertJavaStringToUTF8(path));
+  *result = thumbnail_cache_path;
+  return true;
+}
+
+bool GetDownloadsDirectory(FilePath* result) {
+  JNIEnv* env = AttachCurrentThread();
+  ScopedJavaLocalRef<jstring> path = Java_PathUtils_getDownloadsDirectory(env);
+  FilePath downloads_path(ConvertJavaStringToUTF8(path));
+  *result = downloads_path;
+  return true;
+}
+
+std::vector<FilePath> GetAllPrivateDownloadsDirectories() {
+  std::vector<std::string> dirs;
+  JNIEnv* env = AttachCurrentThread();
+  auto jarray = Java_PathUtils_getAllPrivateDownloadsDirectories(env);
+  base::android::AppendJavaStringArrayToStringVector(env, jarray.obj(), &dirs);
+
+  std::vector<base::FilePath> file_paths;
+  for (const auto& dir : dirs)
+    file_paths.emplace_back(dir);
+  return file_paths;
+}
+
+bool GetNativeLibraryDirectory(FilePath* result) {
+  JNIEnv* env = AttachCurrentThread();
+  ScopedJavaLocalRef<jstring> path =
+      Java_PathUtils_getNativeLibraryDirectory(env);
+  FilePath library_path(ConvertJavaStringToUTF8(path));
+  *result = library_path;
+  return true;
+}
+
+bool GetExternalStorageDirectory(FilePath* result) {
+  JNIEnv* env = AttachCurrentThread();
+  ScopedJavaLocalRef<jstring> path =
+      Java_PathUtils_getExternalStorageDirectory(env);
+  FilePath storage_path(ConvertJavaStringToUTF8(path));
+  *result = storage_path;
+  return true;
+}
+
+}  // namespace android
+}  // namespace base
diff --git a/base/android/path_utils.h b/base/android/path_utils.h
new file mode 100644
index 0000000..650999d
--- /dev/null
+++ b/base/android/path_utils.h
@@ -0,0 +1,55 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_ANDROID_PATH_UTILS_H_
+#define BASE_ANDROID_PATH_UTILS_H_
+
+#include <jni.h>
+#include <vector>
+
+#include "base/base_export.h"
+
+namespace base {
+
+class FilePath;
+
+namespace android {
+
+// Retrieves the absolute path to the data directory of the current
+// application. The result is placed in the FilePath pointed to by 'result'.
+// This method is dedicated for base_paths_android.c, Using
+// PathService::Get(base::DIR_ANDROID_APP_DATA, ...) gets the data dir.
+BASE_EXPORT bool GetDataDirectory(FilePath* result);
+
+// Retrieves the absolute path to the cache directory. The result is placed in
+// the FilePath pointed to by 'result'. This method is dedicated for
+// base_paths_android.c, Using PathService::Get(base::DIR_CACHE, ...) gets the
+// cache dir.
+BASE_EXPORT bool GetCacheDirectory(FilePath* result);
+
+// Retrieves the path to the thumbnail cache directory. The result is placed
+// in the FilePath pointed to by 'result'.
+BASE_EXPORT bool GetThumbnailCacheDirectory(FilePath* result);
+
+// Retrieves the path to the public downloads directory. The result is placed
+// in the FilePath pointed to by 'result'.
+BASE_EXPORT bool GetDownloadsDirectory(FilePath* result);
+
+// Retrieves the paths to all download directories, including default storage
+// directory, and a private directory on external SD card.
+BASE_EXPORT std::vector<FilePath> GetAllPrivateDownloadsDirectories();
+
+// Retrieves the path to the native JNI libraries via
+// ApplicationInfo.nativeLibraryDir on the Java side. The result is placed in
+// the FilePath pointed to by 'result'.
+BASE_EXPORT bool GetNativeLibraryDirectory(FilePath* result);
+
+// Retrieves the absolute path to the external storage directory. The result
+// is placed in the FilePath pointed to by 'result'.
+BASE_EXPORT bool GetExternalStorageDirectory(FilePath* result);
+
+}  // namespace android
+}  // namespace base
+
+#endif  // BASE_ANDROID_PATH_UTILS_H_
diff --git a/base/android/path_utils_unittest.cc b/base/android/path_utils_unittest.cc
new file mode 100644
index 0000000..dca8ca1
--- /dev/null
+++ b/base/android/path_utils_unittest.cc
@@ -0,0 +1,65 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/android/path_utils.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/strings/string_util.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace android {
+
+typedef testing::Test PathUtilsTest;
+
+namespace {
+void ExpectEither(const std::string& expected1,
+                  const std::string& expected2,
+                  const std::string& actual) {
+  EXPECT_TRUE(expected1 == actual || expected2 == actual)
+      << "Value of: " << actual << std::endl
+      << "Expected either: " << expected1 << std::endl
+      << "or: " << expected2;
+}
+}  // namespace
+
+TEST_F(PathUtilsTest, TestGetDataDirectory) {
+  // The string comes from the Java side and depends on the APK
+  // we are running in. Assumes that we are packaged in
+  // org.chromium.native_test
+  FilePath path;
+  GetDataDirectory(&path);
+
+  ExpectEither("/data/data/org.chromium.native_test/app_chrome",
+               "/data/user/0/org.chromium.native_test/app_chrome",
+               path.value());
+}
+
+TEST_F(PathUtilsTest, TestGetCacheDirectory) {
+  // The string comes from the Java side and depends on the APK
+  // we are running in. Assumes that we are packaged in
+  // org.chromium.native_test
+  FilePath path;
+  GetCacheDirectory(&path);
+  ExpectEither("/data/data/org.chromium.native_test/cache",
+               "/data/user/0/org.chromium.native_test/cache",
+               path.value());
+}
+
+TEST_F(PathUtilsTest, TestGetNativeLibraryDirectory) {
+  // The string comes from the Java side and depends on the APK
+  // we are running in. Assumes that the directory contains
+  // the base tests shared object.
+  FilePath path;
+  GetNativeLibraryDirectory(&path);
+  EXPECT_TRUE(
+      base::PathExists(path.Append("libbase_unittests.so")) ||
+      base::PathExists(path.Append("libbase_unittests.cr.so")) ||
+      base::PathExists(path.Append("lib_base_unittests__library.so")) ||
+      base::PathExists(path.Append("lib_base_unittests__library.cr.so")));
+}
+
+}  // namespace android
+}  // namespace base
diff --git a/base/android/proguard/chromium_apk.flags b/base/android/proguard/chromium_apk.flags
new file mode 100644
index 0000000..ac3d7f8
--- /dev/null
+++ b/base/android/proguard/chromium_apk.flags
@@ -0,0 +1,65 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Contains flags that we'd like all Chromium .apks to use.
+
+# Not needed for Android and saves a bit of processing time.
+-dontpreverify
+
+# Keep line number information, useful for stack traces.
+-keepattributes SourceFile,LineNumberTable
+
+# Keep all CREATOR fields within Parcelable that are kept.
+-keepclassmembers class * implements android.os.Parcelable {
+  public static *** CREATOR;
+}
+
+# Don't obfuscate Parcelables as they might be marshalled outside Chrome.
+# If we annotated all Parcelables that get put into Bundles other than
+# for saveInstanceState (e.g. PendingIntents), then we could actually keep the
+# names of just those ones. For now, we'll just keep them all.
+-keepnames class * implements android.os.Parcelable
+
+# Keep all enum values and valueOf methods. See
+# http://proguard.sourceforge.net/index.html#manual/examples.html
+# for the reason for this. Also, see http://crbug.com/248037.
+-keepclassmembers enum * {
+    public static **[] values();
+}
+
+# Keep classes implementing ParameterProvider -- these will be instantiated
+# via reflection.
+-keep class * implements org.chromium.base.test.params.ParameterProvider
+
+# Allows Proguard freedom in removing these log related calls. We ask for debug
+# and verbose logs to be stripped out in base.Log, so we are just ensuring we
+# get rid of all other debug/verbose logs.
+-assumenosideeffects class android.util.Log {
+  static *** d(...);
+  static *** v(...);
+  static *** isLoggable(...);
+}
+
+# The following chart was created on July 20, 2016, to decide on 3 optimization
+# passes for Chrome.
+# optimization passes | time | .dex size | dirty memory per process
+# -----------------------------------------------------------------
+#          1          | 0:48 |  5805676  |         488972
+#          2          | 1:07 |  5777376  |         487092
+#          3          | 1:24 |  5772192  |         486596
+#          4          | 1:42 |  5771124  |         486484
+#          5          | 1:56 |  5770504  |         486432
+-optimizationpasses 3
+
+# Horizontal class merging marginally increases dex size (as of Mar 2018).
+-optimizations !class/merging/horizontal
+
+# Allowing Proguard to change modifiers. This change shrinks the .dex size by
+# ~1%, and reduces the method count by ~4%.
+-allowaccessmodification
+
+# The support library contains references to newer platform versions.
+# Don't warn about those in case this app is linking against an older
+# platform version.  We know about them, and they are safe.
+-dontwarn android.support.**
diff --git a/base/android/proguard/chromium_code.flags b/base/android/proguard/chromium_code.flags
new file mode 100644
index 0000000..8a3ec58
--- /dev/null
+++ b/base/android/proguard/chromium_code.flags
@@ -0,0 +1,75 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Contains flags that can be safely shared with Cronet, and thus would be
+# appropriate for third-party apps to include.
+
+# Keep all annotation related attributes that can affect runtime
+-keepattributes RuntimeVisible*Annotations
+-keepattributes AnnotationDefault
+
+# Keep the annotations, because if we don't, the ProGuard rules that use them
+# will not be respected. These classes then show up in our final dex, which we
+# do not want - see crbug.com/628226.
+-keep @interface org.chromium.base.annotations.AccessedByNative
+-keep @interface org.chromium.base.annotations.CalledByNative
+-keep @interface org.chromium.base.annotations.CalledByNativeUnchecked
+-keep @interface org.chromium.base.annotations.DoNotInline
+-keep @interface org.chromium.base.annotations.RemovableInRelease
+-keep @interface org.chromium.base.annotations.UsedByReflection
+
+# Keeps for class level annotations.
+-keep @org.chromium.base.annotations.UsedByReflection class *
+
+# Keeps for method level annotations.
+-keepclasseswithmembers class * {
+  @org.chromium.base.annotations.AccessedByNative <fields>;
+}
+-keepclasseswithmembers,includedescriptorclasses class * {
+  @org.chromium.base.annotations.CalledByNative <methods>;
+}
+-keepclasseswithmembers,includedescriptorclasses class * {
+  @org.chromium.base.annotations.CalledByNativeUnchecked <methods>;
+}
+-keepclasseswithmembers class * {
+  @org.chromium.base.annotations.UsedByReflection <methods>;
+}
+-keepclasseswithmembers class * {
+  @org.chromium.base.annotations.UsedByReflection <fields>;
+}
+-keepclasseswithmembers,includedescriptorclasses class * {
+  native <methods>;
+}
+
+# Remove methods annotated with this if their return value is unused.
+-assumenosideeffects class ** {
+  @org.chromium.base.annotations.RemovableInRelease <methods>;
+}
+
+# Never inline classes or methods with this annotation, but allow shrinking and
+# obfuscation.
+-keepnames,allowobfuscation @org.chromium.base.annotations.DoNotInline class * {
+  *;
+}
+-keepclassmembernames,allowobfuscation class * {
+  @org.chromium.base.annotations.DoNotInline <methods>;
+}
+
+# Keep all CREATOR fields within Parcelable that are kept.
+-keepclassmembers class org.chromium.** implements android.os.Parcelable {
+  public static *** CREATOR;
+}
+
+# Don't obfuscate Parcelables as they might be marshalled outside Chrome.
+# If we annotated all Parcelables that get put into Bundles other than
+# for saveInstanceState (e.g. PendingIntents), then we could actually keep the
+# names of just those ones. For now, we'll just keep them all.
+-keepnames class org.chromium.** implements android.os.Parcelable
+
+# Keep all enum values and valueOf methods. See
+# http://proguard.sourceforge.net/index.html#manual/examples.html
+# for the reason for this. Also, see http://crbug.com/248037.
+-keepclassmembers enum org.chromium.** {
+    public static **[] values();
+}
diff --git a/base/android/record_histogram.cc b/base/android/record_histogram.cc
new file mode 100644
index 0000000..f41ec99
--- /dev/null
+++ b/base/android/record_histogram.cc
@@ -0,0 +1,343 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdint.h>
+
+#include <map>
+#include <string>
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "base/lazy_instance.h"
+#include "base/macros.h"
+#include "base/metrics/histogram.h"
+#include "base/metrics/sparse_histogram.h"
+#include "base/metrics/statistics_recorder.h"
+#include "base/strings/stringprintf.h"
+#include "base/synchronization/lock.h"
+#include "base/time/time.h"
+#include "jni/RecordHistogram_jni.h"
+
+namespace base {
+namespace android {
+namespace {
+
+// Simple thread-safe wrapper for caching histograms. This avoids
+// relatively expensive JNI string translation for each recording.
+class HistogramCache {
+ public:
+  HistogramCache() {}
+
+  std::string HistogramConstructionParamsToString(HistogramBase* histogram) {
+    std::string params_str = histogram->histogram_name();
+    switch (histogram->GetHistogramType()) {
+      case HISTOGRAM:
+      case LINEAR_HISTOGRAM:
+      case BOOLEAN_HISTOGRAM:
+      case CUSTOM_HISTOGRAM: {
+        Histogram* hist = static_cast<Histogram*>(histogram);
+        params_str += StringPrintf("/%d/%d/%d", hist->declared_min(),
+                                   hist->declared_max(), hist->bucket_count());
+        break;
+      }
+      case SPARSE_HISTOGRAM:
+      case DUMMY_HISTOGRAM:
+        break;
+    }
+    return params_str;
+  }
+
+  void JNI_RecordHistogram_CheckHistogramArgs(JNIEnv* env,
+                                              jstring j_histogram_name,
+                                              int32_t expected_min,
+                                              int32_t expected_max,
+                                              uint32_t expected_bucket_count,
+                                              HistogramBase* histogram) {
+    std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name);
+    bool valid_arguments = Histogram::InspectConstructionArguments(
+        histogram_name, &expected_min, &expected_max, &expected_bucket_count);
+    DCHECK(valid_arguments);
+    DCHECK(histogram->HasConstructionArguments(expected_min, expected_max,
+                                               expected_bucket_count))
+        << histogram_name << "/" << expected_min << "/" << expected_max << "/"
+        << expected_bucket_count << " vs. "
+        << HistogramConstructionParamsToString(histogram);
+  }
+
+  HistogramBase* JNI_RecordHistogram_BooleanHistogram(JNIEnv* env,
+                                                      jstring j_histogram_name,
+                                                      jlong j_histogram_key) {
+    DCHECK(j_histogram_name);
+    HistogramBase* histogram = HistogramFromKey(j_histogram_key);
+    if (histogram)
+      return histogram;
+
+    std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name);
+    histogram = BooleanHistogram::FactoryGet(
+        histogram_name, HistogramBase::kUmaTargetedHistogramFlag);
+    return histogram;
+  }
+
+  HistogramBase* JNI_RecordHistogram_EnumeratedHistogram(
+      JNIEnv* env,
+      jstring j_histogram_name,
+      jlong j_histogram_key,
+      jint j_boundary) {
+    DCHECK(j_histogram_name);
+    HistogramBase* histogram = HistogramFromKey(j_histogram_key);
+    int32_t boundary = static_cast<int32_t>(j_boundary);
+    if (histogram) {
+      JNI_RecordHistogram_CheckHistogramArgs(env, j_histogram_name, 1, boundary,
+                                             boundary + 1, histogram);
+      return histogram;
+    }
+
+    std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name);
+    histogram =
+        LinearHistogram::FactoryGet(histogram_name, 1, boundary, boundary + 1,
+                                    HistogramBase::kUmaTargetedHistogramFlag);
+    return histogram;
+  }
+
+  HistogramBase* JNI_RecordHistogram_CustomCountHistogram(
+      JNIEnv* env,
+      jstring j_histogram_name,
+      jlong j_histogram_key,
+      jint j_min,
+      jint j_max,
+      jint j_num_buckets) {
+    DCHECK(j_histogram_name);
+    int32_t min = static_cast<int32_t>(j_min);
+    int32_t max = static_cast<int32_t>(j_max);
+    int32_t num_buckets = static_cast<int32_t>(j_num_buckets);
+    HistogramBase* histogram = HistogramFromKey(j_histogram_key);
+    if (histogram) {
+      JNI_RecordHistogram_CheckHistogramArgs(env, j_histogram_name, min, max,
+                                             num_buckets, histogram);
+      return histogram;
+    }
+
+    DCHECK_GE(min, 1) << "The min expected sample must be >= 1";
+
+    std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name);
+    histogram =
+        Histogram::FactoryGet(histogram_name, min, max, num_buckets,
+                              HistogramBase::kUmaTargetedHistogramFlag);
+    return histogram;
+  }
+
+  HistogramBase* JNI_RecordHistogram_LinearCountHistogram(
+      JNIEnv* env,
+      jstring j_histogram_name,
+      jlong j_histogram_key,
+      jint j_min,
+      jint j_max,
+      jint j_num_buckets) {
+    DCHECK(j_histogram_name);
+    int32_t min = static_cast<int32_t>(j_min);
+    int32_t max = static_cast<int32_t>(j_max);
+    int32_t num_buckets = static_cast<int32_t>(j_num_buckets);
+    HistogramBase* histogram = HistogramFromKey(j_histogram_key);
+    if (histogram) {
+      JNI_RecordHistogram_CheckHistogramArgs(env, j_histogram_name, min, max,
+                                             num_buckets, histogram);
+      return histogram;
+    }
+
+    std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name);
+    histogram =
+        LinearHistogram::FactoryGet(histogram_name, min, max, num_buckets,
+                                    HistogramBase::kUmaTargetedHistogramFlag);
+    return histogram;
+  }
+
+  HistogramBase* JNI_RecordHistogram_SparseHistogram(JNIEnv* env,
+                                                     jstring j_histogram_name,
+                                                     jlong j_histogram_key) {
+    DCHECK(j_histogram_name);
+    HistogramBase* histogram = HistogramFromKey(j_histogram_key);
+    if (histogram)
+      return histogram;
+
+    std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name);
+    histogram = SparseHistogram::FactoryGet(
+        histogram_name, HistogramBase::kUmaTargetedHistogramFlag);
+    return histogram;
+  }
+
+  HistogramBase* JNI_RecordHistogram_CustomTimesHistogram(
+      JNIEnv* env,
+      jstring j_histogram_name,
+      jlong j_histogram_key,
+      jint j_min,
+      jint j_max,
+      jint j_bucket_count) {
+    DCHECK(j_histogram_name);
+    HistogramBase* histogram = HistogramFromKey(j_histogram_key);
+    int32_t min = static_cast<int32_t>(j_min);
+    int32_t max = static_cast<int32_t>(j_max);
+    int32_t bucket_count = static_cast<int32_t>(j_bucket_count);
+    if (histogram) {
+      JNI_RecordHistogram_CheckHistogramArgs(env, j_histogram_name, min, max,
+                                             bucket_count, histogram);
+      return histogram;
+    }
+
+    std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name);
+    // This intentionally uses FactoryGet and not FactoryTimeGet. FactoryTimeGet
+    // is just a convenience for constructing the underlying Histogram with
+    // TimeDelta arguments.
+    histogram = Histogram::FactoryGet(histogram_name, min, max, bucket_count,
+                                      HistogramBase::kUmaTargetedHistogramFlag);
+    return histogram;
+  }
+
+ private:
+  // Convert a jlong |histogram_key| from Java to a HistogramBase* via a cast.
+  // The Java side caches these in a map (see RecordHistogram.java), which is
+  // safe to do since C++ Histogram objects are never freed.
+  static HistogramBase* HistogramFromKey(jlong j_histogram_key) {
+    return reinterpret_cast<HistogramBase*>(j_histogram_key);
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(HistogramCache);
+};
+
+LazyInstance<HistogramCache>::Leaky g_histograms;
+
+}  // namespace
+
+jlong JNI_RecordHistogram_RecordBooleanHistogram(
+    JNIEnv* env,
+    const JavaParamRef<jclass>& clazz,
+    const JavaParamRef<jstring>& j_histogram_name,
+    jlong j_histogram_key,
+    jboolean j_sample) {
+  bool sample = static_cast<bool>(j_sample);
+  HistogramBase* histogram =
+      g_histograms.Get().JNI_RecordHistogram_BooleanHistogram(
+          env, j_histogram_name, j_histogram_key);
+  histogram->AddBoolean(sample);
+  return reinterpret_cast<jlong>(histogram);
+}
+
+jlong JNI_RecordHistogram_RecordEnumeratedHistogram(
+    JNIEnv* env,
+    const JavaParamRef<jclass>& clazz,
+    const JavaParamRef<jstring>& j_histogram_name,
+    jlong j_histogram_key,
+    jint j_sample,
+    jint j_boundary) {
+  int sample = static_cast<int>(j_sample);
+
+  HistogramBase* histogram =
+      g_histograms.Get().JNI_RecordHistogram_EnumeratedHistogram(
+          env, j_histogram_name, j_histogram_key, j_boundary);
+  histogram->Add(sample);
+  return reinterpret_cast<jlong>(histogram);
+}
+
+jlong JNI_RecordHistogram_RecordCustomCountHistogram(
+    JNIEnv* env,
+    const JavaParamRef<jclass>& clazz,
+    const JavaParamRef<jstring>& j_histogram_name,
+    jlong j_histogram_key,
+    jint j_sample,
+    jint j_min,
+    jint j_max,
+    jint j_num_buckets) {
+  int sample = static_cast<int>(j_sample);
+
+  HistogramBase* histogram =
+      g_histograms.Get().JNI_RecordHistogram_CustomCountHistogram(
+          env, j_histogram_name, j_histogram_key, j_min, j_max, j_num_buckets);
+  histogram->Add(sample);
+  return reinterpret_cast<jlong>(histogram);
+}
+
+jlong JNI_RecordHistogram_RecordLinearCountHistogram(
+    JNIEnv* env,
+    const JavaParamRef<jclass>& clazz,
+    const JavaParamRef<jstring>& j_histogram_name,
+    jlong j_histogram_key,
+    jint j_sample,
+    jint j_min,
+    jint j_max,
+    jint j_num_buckets) {
+  int sample = static_cast<int>(j_sample);
+
+  HistogramBase* histogram =
+      g_histograms.Get().JNI_RecordHistogram_LinearCountHistogram(
+          env, j_histogram_name, j_histogram_key, j_min, j_max, j_num_buckets);
+  histogram->Add(sample);
+  return reinterpret_cast<jlong>(histogram);
+}
+
+jlong JNI_RecordHistogram_RecordSparseHistogram(
+    JNIEnv* env,
+    const JavaParamRef<jclass>& clazz,
+    const JavaParamRef<jstring>& j_histogram_name,
+    jlong j_histogram_key,
+    jint j_sample) {
+  int sample = static_cast<int>(j_sample);
+  HistogramBase* histogram =
+      g_histograms.Get().JNI_RecordHistogram_SparseHistogram(
+          env, j_histogram_name, j_histogram_key);
+  histogram->Add(sample);
+  return reinterpret_cast<jlong>(histogram);
+}
+
+jlong JNI_RecordHistogram_RecordCustomTimesHistogramMilliseconds(
+    JNIEnv* env,
+    const JavaParamRef<jclass>& clazz,
+    const JavaParamRef<jstring>& j_histogram_name,
+    jlong j_histogram_key,
+    jint j_duration,
+    jint j_min,
+    jint j_max,
+    jint j_num_buckets) {
+  HistogramBase* histogram =
+      g_histograms.Get().JNI_RecordHistogram_CustomTimesHistogram(
+          env, j_histogram_name, j_histogram_key, j_min, j_max, j_num_buckets);
+  histogram->AddTime(
+      TimeDelta::FromMilliseconds(static_cast<int64_t>(j_duration)));
+  return reinterpret_cast<jlong>(histogram);
+}
+
+// This backs a Java test util for testing histograms -
+// MetricsUtils.HistogramDelta. It should live in a test-specific file, but we
+// currently can't have test-specific native code packaged in test-specific Java
+// targets - see http://crbug.com/415945.
+jint JNI_RecordHistogram_GetHistogramValueCountForTesting(
+    JNIEnv* env,
+    const JavaParamRef<jclass>& clazz,
+    const JavaParamRef<jstring>& histogram_name,
+    jint sample) {
+  HistogramBase* histogram = StatisticsRecorder::FindHistogram(
+      android::ConvertJavaStringToUTF8(env, histogram_name));
+  if (histogram == nullptr) {
+    // No samples have been recorded for this histogram (yet?).
+    return 0;
+  }
+
+  std::unique_ptr<HistogramSamples> samples = histogram->SnapshotSamples();
+  return samples->GetCount(static_cast<int>(sample));
+}
+
+jint JNI_RecordHistogram_GetHistogramTotalCountForTesting(
+    JNIEnv* env,
+    const JavaParamRef<jclass>& clazz,
+    const JavaParamRef<jstring>& histogram_name) {
+  HistogramBase* histogram = StatisticsRecorder::FindHistogram(
+      android::ConvertJavaStringToUTF8(env, histogram_name));
+  if (histogram == nullptr) {
+    // No samples have been recorded for this histogram.
+    return 0;
+  }
+
+  return histogram->SnapshotSamples()->TotalCount();
+}
+
+}  // namespace android
+}  // namespace base
diff --git a/base/android/record_user_action.cc b/base/android/record_user_action.cc
new file mode 100644
index 0000000..683add6
--- /dev/null
+++ b/base/android/record_user_action.cc
@@ -0,0 +1,59 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/android/jni_string.h"
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/metrics/user_metrics.h"
+#include "jni/RecordUserAction_jni.h"
+
+namespace {
+
+struct ActionCallbackWrapper {
+  base::ActionCallback action_callback;
+};
+
+}  // namespace
+
+namespace base {
+namespace android {
+
+static void JNI_RecordUserAction_RecordUserAction(
+    JNIEnv* env,
+    const JavaParamRef<jclass>& clazz,
+    const JavaParamRef<jstring>& j_action) {
+  RecordComputedAction(ConvertJavaStringToUTF8(env, j_action));
+}
+
+static void OnActionRecorded(const JavaRef<jobject>& callback,
+                             const std::string& action) {
+  JNIEnv* env = AttachCurrentThread();
+  Java_UserActionCallback_onActionRecorded(
+      env, callback, ConvertUTF8ToJavaString(env, action));
+}
+
+static jlong JNI_RecordUserAction_AddActionCallbackForTesting(
+    JNIEnv* env,
+    const JavaParamRef<jclass>& clazz,
+    const JavaParamRef<jobject>& callback) {
+  // Create a wrapper for the ActionCallback, so it can life on the heap until
+  // RemoveActionCallbackForTesting() is called.
+  auto* wrapper = new ActionCallbackWrapper{base::Bind(
+      &OnActionRecorded, ScopedJavaGlobalRef<jobject>(env, callback))};
+  base::AddActionCallback(wrapper->action_callback);
+  return reinterpret_cast<intptr_t>(wrapper);
+}
+
+static void JNI_RecordUserAction_RemoveActionCallbackForTesting(
+    JNIEnv* env,
+    const JavaParamRef<jclass>& clazz,
+    jlong callback_id) {
+  DCHECK(callback_id);
+  auto* wrapper = reinterpret_cast<ActionCallbackWrapper*>(callback_id);
+  base::RemoveActionCallback(wrapper->action_callback);
+  delete wrapper;
+}
+
+}  // namespace android
+}  // namespace base
diff --git a/base/android/statistics_recorder_android.cc b/base/android/statistics_recorder_android.cc
new file mode 100644
index 0000000..346a7c7
--- /dev/null
+++ b/base/android/statistics_recorder_android.cc
@@ -0,0 +1,29 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <string>
+
+#include "base/android/jni_string.h"
+#include "base/metrics/histogram_base.h"
+#include "base/metrics/statistics_recorder.h"
+#include "base/sys_info.h"
+#include "jni/StatisticsRecorderAndroid_jni.h"
+
+using base::android::JavaParamRef;
+using base::android::ConvertUTF8ToJavaString;
+
+namespace base {
+namespace android {
+
+static ScopedJavaLocalRef<jstring> JNI_StatisticsRecorderAndroid_ToJson(
+    JNIEnv* env,
+    const JavaParamRef<jclass>& clazz,
+    jint verbosityLevel) {
+  return ConvertUTF8ToJavaString(
+      env, base::StatisticsRecorder::ToJSON(
+               static_cast<JSONVerbosityLevel>(verbosityLevel)));
+}
+
+}  // namespace android
+}  // namespace base
diff --git a/base/android/sys_utils.cc b/base/android/sys_utils.cc
new file mode 100644
index 0000000..7872b2f
--- /dev/null
+++ b/base/android/sys_utils.cc
@@ -0,0 +1,51 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/android/sys_utils.h"
+
+#include <memory>
+
+#include "base/android/build_info.h"
+#include "base/process/process_metrics.h"
+#include "base/sys_info.h"
+#include "base/trace_event/trace_event.h"
+#include "jni/SysUtils_jni.h"
+
+namespace base {
+namespace android {
+
+bool SysUtils::IsLowEndDeviceFromJni() {
+  JNIEnv* env = AttachCurrentThread();
+  return Java_SysUtils_isLowEndDevice(env);
+}
+
+bool SysUtils::IsCurrentlyLowMemory() {
+  JNIEnv* env = AttachCurrentThread();
+  return Java_SysUtils_isCurrentlyLowMemory(env);
+}
+
+// Logs the number of minor / major page faults to tracing (and also the time to
+// collect) the metrics. Does nothing if tracing is not enabled.
+static void JNI_SysUtils_LogPageFaultCountToTracing(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jclass>& jcaller) {
+  // This is racy, but we are OK losing data, and collecting it is potentially
+  // expensive (reading and parsing a file).
+  bool enabled;
+  TRACE_EVENT_CATEGORY_GROUP_ENABLED("startup", &enabled);
+  if (!enabled)
+    return;
+  TRACE_EVENT_BEGIN2("memory", "CollectPageFaultCount", "minor", 0, "major", 0);
+  std::unique_ptr<base::ProcessMetrics> process_metrics(
+      base::ProcessMetrics::CreateProcessMetrics(
+          base::GetCurrentProcessHandle()));
+  base::PageFaultCounts counts;
+  process_metrics->GetPageFaultCounts(&counts);
+  TRACE_EVENT_END2("memory", "CollectPageFaults", "minor", counts.minor,
+                   "major", counts.major);
+}
+
+}  // namespace android
+
+}  // namespace base
diff --git a/base/android/sys_utils.h b/base/android/sys_utils.h
new file mode 100644
index 0000000..b1e368b
--- /dev/null
+++ b/base/android/sys_utils.h
@@ -0,0 +1,24 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_ANDROID_SYS_UTILS_H_
+#define BASE_ANDROID_SYS_UTILS_H_
+
+#include "base/android/jni_android.h"
+
+namespace base {
+namespace android {
+
+class BASE_EXPORT SysUtils {
+ public:
+  // Returns true iff this is a low-end device.
+  static bool IsLowEndDeviceFromJni();
+  // Returns true if system has low available memory.
+  static bool IsCurrentlyLowMemory();
+};
+
+}  // namespace android
+}  // namespace base
+
+#endif  // BASE_ANDROID_SYS_UTILS_H_
diff --git a/base/android/sys_utils_unittest.cc b/base/android/sys_utils_unittest.cc
new file mode 100644
index 0000000..d16e236
--- /dev/null
+++ b/base/android/sys_utils_unittest.cc
@@ -0,0 +1,24 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+#include <unistd.h>
+
+#include "base/sys_info.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace android {
+
+TEST(SysUtils, AmountOfPhysicalMemory) {
+  // Check that the RAM size reported by sysconf() matches the one
+  // computed by base::SysInfo::AmountOfPhysicalMemory().
+  size_t sys_ram_size =
+      static_cast<size_t>(sysconf(_SC_PHYS_PAGES) * PAGE_SIZE);
+  EXPECT_EQ(sys_ram_size,
+            static_cast<size_t>(SysInfo::AmountOfPhysicalMemory()));
+}
+
+}  // namespace android
+}  // namespace base
diff --git a/base/android/throw_uncaught_exception.cc b/base/android/throw_uncaught_exception.cc
new file mode 100644
index 0000000..68627cc
--- /dev/null
+++ b/base/android/throw_uncaught_exception.cc
@@ -0,0 +1,19 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/android/throw_uncaught_exception.h"
+
+#include "base/android/jni_android.h"
+
+#include "jni/ThrowUncaughtException_jni.h"
+
+namespace base {
+namespace android {
+
+void ThrowUncaughtException() {
+  Java_ThrowUncaughtException_post(AttachCurrentThread());
+}
+
+}  // namespace android
+}  // namespace base
diff --git a/base/android/throw_uncaught_exception.h b/base/android/throw_uncaught_exception.h
new file mode 100644
index 0000000..57bd908
--- /dev/null
+++ b/base/android/throw_uncaught_exception.h
@@ -0,0 +1,20 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_ANDROID_THROW_UNCAUGHT_EXCEPTION_H_
+#define BASE_ANDROID_THROW_UNCAUGHT_EXCEPTION_H_
+
+#include "base/base_export.h"
+
+namespace base {
+namespace android {
+
+// Throw that completely unwinds the java stack. In particular, this will not
+// trigger a jni CheckException crash.
+BASE_EXPORT void ThrowUncaughtException();
+
+}  // namespace android
+}  // namespace base
+
+#endif  // BASE_ANDROID_THROW_UNCAUGHT_EXCEPTION_H_
diff --git a/base/android/time_utils.cc b/base/android/time_utils.cc
new file mode 100644
index 0000000..632dfb7
--- /dev/null
+++ b/base/android/time_utils.cc
@@ -0,0 +1,20 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdint.h>
+
+#include "base/time/time.h"
+#include "jni/TimeUtils_jni.h"
+
+namespace base {
+namespace android {
+
+static jlong JNI_TimeUtils_GetTimeTicksNowUs(
+    JNIEnv* env,
+    const JavaParamRef<jclass>& clazz) {
+  return (TimeTicks::Now() - TimeTicks()).InMicroseconds();
+}
+
+}  // namespace android
+}  // namespace base
diff --git a/base/android/trace_event_binding.cc b/base/android/trace_event_binding.cc
new file mode 100644
index 0000000..623ca75
--- /dev/null
+++ b/base/android/trace_event_binding.cc
@@ -0,0 +1,155 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <jni.h>
+
+#include <set>
+
+#include "base/android/jni_string.h"
+#include "base/lazy_instance.h"
+#include "base/macros.h"
+#include "base/trace_event/trace_event.h"
+#include "base/trace_event/trace_event_impl.h"
+#include "jni/TraceEvent_jni.h"
+
+namespace base {
+namespace android {
+
+namespace {
+
+const char kJavaCategory[] = "Java";
+const char kToplevelCategory[] = "toplevel";
+const char kLooperDispatchMessage[] = "Looper.dispatchMessage";
+
+// Boilerplate for safely converting Java data to TRACE_EVENT data.
+class TraceEventDataConverter {
+ public:
+  TraceEventDataConverter(JNIEnv* env, jstring jname, jstring jarg)
+      : name_(ConvertJavaStringToUTF8(env, jname)),
+        has_arg_(jarg != nullptr),
+        arg_(jarg ? ConvertJavaStringToUTF8(env, jarg) : "") {}
+  ~TraceEventDataConverter() {
+  }
+
+  // Return saves values to pass to TRACE_EVENT macros.
+  const char* name() { return name_.c_str(); }
+  const char* arg_name() { return has_arg_ ? "arg" : nullptr; }
+  const char* arg() { return has_arg_ ? arg_.c_str() : nullptr; }
+
+ private:
+  std::string name_;
+  bool has_arg_;
+  std::string arg_;
+
+  DISALLOW_COPY_AND_ASSIGN(TraceEventDataConverter);
+};
+
+class TraceEnabledObserver
+    : public trace_event::TraceLog::EnabledStateObserver {
+  public:
+   void OnTraceLogEnabled() override {
+      JNIEnv* env = base::android::AttachCurrentThread();
+      base::android::Java_TraceEvent_setEnabled(env, true);
+    }
+    void OnTraceLogDisabled() override {
+      JNIEnv* env = base::android::AttachCurrentThread();
+      base::android::Java_TraceEvent_setEnabled(env, false);
+    }
+};
+
+base::LazyInstance<TraceEnabledObserver>::Leaky g_trace_enabled_state_observer_;
+
+}  // namespace
+
+static void JNI_TraceEvent_RegisterEnabledObserver(
+    JNIEnv* env,
+    const JavaParamRef<jclass>& clazz) {
+  bool enabled = trace_event::TraceLog::GetInstance()->IsEnabled();
+  base::android::Java_TraceEvent_setEnabled(env, enabled);
+  trace_event::TraceLog::GetInstance()->AddEnabledStateObserver(
+      g_trace_enabled_state_observer_.Pointer());
+}
+
+static void JNI_TraceEvent_StartATrace(JNIEnv* env,
+                                       const JavaParamRef<jclass>& clazz) {
+  base::trace_event::TraceLog::GetInstance()->StartATrace();
+}
+
+static void JNI_TraceEvent_StopATrace(JNIEnv* env,
+                                      const JavaParamRef<jclass>& clazz) {
+  base::trace_event::TraceLog::GetInstance()->StopATrace();
+}
+
+static void JNI_TraceEvent_Instant(JNIEnv* env,
+                                   const JavaParamRef<jclass>& clazz,
+                                   const JavaParamRef<jstring>& jname,
+                                   const JavaParamRef<jstring>& jarg) {
+  TraceEventDataConverter converter(env, jname, jarg);
+  if (converter.arg()) {
+    TRACE_EVENT_COPY_INSTANT1(kJavaCategory, converter.name(),
+                              TRACE_EVENT_SCOPE_THREAD,
+                              converter.arg_name(), converter.arg());
+  } else {
+    TRACE_EVENT_COPY_INSTANT0(kJavaCategory, converter.name(),
+                              TRACE_EVENT_SCOPE_THREAD);
+  }
+}
+
+static void JNI_TraceEvent_Begin(JNIEnv* env,
+                                 const JavaParamRef<jclass>& clazz,
+                                 const JavaParamRef<jstring>& jname,
+                                 const JavaParamRef<jstring>& jarg) {
+  TraceEventDataConverter converter(env, jname, jarg);
+  if (converter.arg()) {
+    TRACE_EVENT_COPY_BEGIN1(kJavaCategory, converter.name(),
+                       converter.arg_name(), converter.arg());
+  } else {
+    TRACE_EVENT_COPY_BEGIN0(kJavaCategory, converter.name());
+  }
+}
+
+static void JNI_TraceEvent_End(JNIEnv* env,
+                               const JavaParamRef<jclass>& clazz,
+                               const JavaParamRef<jstring>& jname,
+                               const JavaParamRef<jstring>& jarg) {
+  TraceEventDataConverter converter(env, jname, jarg);
+  if (converter.arg()) {
+    TRACE_EVENT_COPY_END1(kJavaCategory, converter.name(),
+                     converter.arg_name(), converter.arg());
+  } else {
+    TRACE_EVENT_COPY_END0(kJavaCategory, converter.name());
+  }
+}
+
+static void JNI_TraceEvent_BeginToplevel(JNIEnv* env,
+                                         const JavaParamRef<jclass>& clazz,
+                                         const JavaParamRef<jstring>& jtarget) {
+  std::string target = ConvertJavaStringToUTF8(env, jtarget);
+  TRACE_EVENT_BEGIN1(kToplevelCategory, kLooperDispatchMessage, "target",
+                     target);
+}
+
+static void JNI_TraceEvent_EndToplevel(JNIEnv* env,
+                                       const JavaParamRef<jclass>& clazz) {
+  TRACE_EVENT_END0(kToplevelCategory, kLooperDispatchMessage);
+}
+
+static void JNI_TraceEvent_StartAsync(JNIEnv* env,
+                                      const JavaParamRef<jclass>& clazz,
+                                      const JavaParamRef<jstring>& jname,
+                                      jlong jid) {
+  TraceEventDataConverter converter(env, jname, nullptr);
+  TRACE_EVENT_COPY_ASYNC_BEGIN0(kJavaCategory, converter.name(), jid);
+}
+
+static void JNI_TraceEvent_FinishAsync(JNIEnv* env,
+                                       const JavaParamRef<jclass>& clazz,
+                                       const JavaParamRef<jstring>& jname,
+                                       jlong jid) {
+  TraceEventDataConverter converter(env, jname, nullptr);
+  TRACE_EVENT_COPY_ASYNC_END0(kJavaCategory, converter.name(), jid);
+}
+
+}  // namespace android
+}  // namespace base
diff --git a/base/android/unguessable_token_android.cc b/base/android/unguessable_token_android.cc
new file mode 100644
index 0000000..d041557
--- /dev/null
+++ b/base/android/unguessable_token_android.cc
@@ -0,0 +1,41 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/android/unguessable_token_android.h"
+
+#include "jni/UnguessableToken_jni.h"
+
+namespace base {
+namespace android {
+
+ScopedJavaLocalRef<jobject> UnguessableTokenAndroid::Create(
+    JNIEnv* env,
+    const base::UnguessableToken& token) {
+  const uint64_t high = token.GetHighForSerialization();
+  const uint64_t low = token.GetLowForSerialization();
+  DCHECK(high);
+  DCHECK(low);
+  return Java_UnguessableToken_create(env, high, low);
+}
+
+base::UnguessableToken UnguessableTokenAndroid::FromJavaUnguessableToken(
+    JNIEnv* env,
+    const JavaRef<jobject>& token) {
+  const uint64_t high =
+      Java_UnguessableToken_getHighForSerialization(env, token);
+  const uint64_t low = Java_UnguessableToken_getLowForSerialization(env, token);
+  DCHECK(high);
+  DCHECK(low);
+  return base::UnguessableToken::Deserialize(high, low);
+}
+
+ScopedJavaLocalRef<jobject>
+UnguessableTokenAndroid::ParcelAndUnparcelForTesting(
+    JNIEnv* env,
+    const JavaRef<jobject>& token) {
+  return Java_UnguessableToken_parcelAndUnparcelForTesting(env, token);
+}
+
+}  // namespace android
+}  // namespace base
diff --git a/base/android/unguessable_token_android.h b/base/android/unguessable_token_android.h
new file mode 100644
index 0000000..bb91f0e
--- /dev/null
+++ b/base/android/unguessable_token_android.h
@@ -0,0 +1,43 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_ANDROID_UNGUESSABLE_TOKEN_ANDROID_H_
+#define BASE_ANDROID_UNGUESSABLE_TOKEN_ANDROID_H_
+
+#include <jni.h>
+
+#include "base/android/scoped_java_ref.h"
+#include "base/base_export.h"
+#include "base/unguessable_token.h"
+
+namespace base {
+namespace android {
+
+class BASE_EXPORT UnguessableTokenAndroid {
+ public:
+  // Create a Java UnguessableToken with the same value as |token|.
+  static ScopedJavaLocalRef<jobject> Create(
+      JNIEnv* env,
+      const base::UnguessableToken& token);
+
+  // Create a native UnguessableToken from Java UnguessableToken |token|.
+  static base::UnguessableToken FromJavaUnguessableToken(
+      JNIEnv* env,
+      const JavaRef<jobject>& token);
+
+  // Parcel UnguessableToken |token| and unparcel it, and return the result.
+  // While this method is intended for facilitating unit tests, it results only
+  // in a clone of |token|.
+  static ScopedJavaLocalRef<jobject> ParcelAndUnparcelForTesting(
+      JNIEnv* env,
+      const JavaRef<jobject>& token);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(UnguessableTokenAndroid);
+};
+
+}  // namespace android
+}  // namespace base
+
+#endif  // BASE_ANDROID_UNGUESSABLE_TOKEN_ANDROID_H_
diff --git a/base/android/unguessable_token_android_unittest.cc b/base/android/unguessable_token_android_unittest.cc
new file mode 100644
index 0000000..bdad746
--- /dev/null
+++ b/base/android/unguessable_token_android_unittest.cc
@@ -0,0 +1,42 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/android/unguessable_token_android.h"
+
+#include "base/android/jni_android.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace android {
+
+TEST(UnguessableTokenAndroid, BasicCreateToken) {
+  JNIEnv* env = AttachCurrentThread();
+  uint64_t high = 0x1234567812345678;
+  uint64_t low = 0x0583503029282304;
+  base::UnguessableToken token = base::UnguessableToken::Deserialize(high, low);
+  ScopedJavaLocalRef<jobject> jtoken =
+      UnguessableTokenAndroid::Create(env, token);
+  base::UnguessableToken result =
+      UnguessableTokenAndroid::FromJavaUnguessableToken(env, jtoken);
+
+  EXPECT_EQ(token, result);
+}
+
+TEST(UnguessableTokenAndroid, ParcelAndUnparcel) {
+  JNIEnv* env = AttachCurrentThread();
+  uint64_t high = 0x1234567812345678;
+  uint64_t low = 0x0583503029282304;
+  base::UnguessableToken token = base::UnguessableToken::Deserialize(high, low);
+  ScopedJavaLocalRef<jobject> jtoken =
+      UnguessableTokenAndroid::Create(env, token);
+  ScopedJavaLocalRef<jobject> jtoken_clone =
+      UnguessableTokenAndroid::ParcelAndUnparcelForTesting(env, jtoken);
+  base::UnguessableToken token_clone =
+      UnguessableTokenAndroid::FromJavaUnguessableToken(env, jtoken_clone);
+
+  EXPECT_EQ(token, token_clone);
+}
+
+}  // namespace android
+}  // namespace base
diff --git a/base/barrier_closure_unittest.cc b/base/barrier_closure_unittest.cc
new file mode 100644
index 0000000..819f6ac
--- /dev/null
+++ b/base/barrier_closure_unittest.cc
@@ -0,0 +1,81 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/barrier_closure.h"
+
+#include "base/bind.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+void Increment(int* count) { (*count)++; }
+
+TEST(BarrierClosureTest, RunImmediatelyForZeroClosures) {
+  int count = 0;
+  base::Closure done_closure(base::Bind(&Increment, base::Unretained(&count)));
+
+  base::Closure barrier_closure = base::BarrierClosure(0, done_closure);
+  EXPECT_EQ(1, count);
+}
+
+TEST(BarrierClosureTest, RunAfterNumClosures) {
+  int count = 0;
+  base::Closure done_closure(base::Bind(&Increment, base::Unretained(&count)));
+
+  base::Closure barrier_closure = base::BarrierClosure(2, done_closure);
+  EXPECT_EQ(0, count);
+
+  barrier_closure.Run();
+  EXPECT_EQ(0, count);
+
+  barrier_closure.Run();
+  EXPECT_EQ(1, count);
+}
+
+class DestructionIndicator {
+ public:
+  // Sets |*destructed| to true in destructor.
+  DestructionIndicator(bool* destructed) : destructed_(destructed) {
+    *destructed_ = false;
+  }
+
+  ~DestructionIndicator() { *destructed_ = true; }
+
+  void DoNothing() {}
+
+ private:
+  bool* destructed_;
+};
+
+TEST(BarrierClosureTest, ReleasesDoneClosureWhenDone) {
+  bool done_destructed = false;
+  base::Closure barrier_closure = base::BarrierClosure(
+      1,
+      base::BindOnce(&DestructionIndicator::DoNothing,
+                     base::Owned(new DestructionIndicator(&done_destructed))));
+  EXPECT_FALSE(done_destructed);
+  barrier_closure.Run();
+  EXPECT_TRUE(done_destructed);
+}
+
+void ResetBarrierClosure(base::Closure* closure) {
+  *closure = base::Closure();
+}
+
+// Tests a case when |done_closure| resets a |barrier_closure|.
+// |barrier_closure| is a Closure holding the |done_closure|. |done_closure|
+// holds a pointer back to the |barrier_closure|. When |barrier_closure| is
+// Run() it calls ResetBarrierClosure() which erases the |barrier_closure| while
+// still inside of its Run(). The Run() implementation (in base::BarrierClosure)
+// must not try use itself after executing ResetBarrierClosure() or this test
+// would crash inside Run().
+TEST(BarrierClosureTest, KeepingClosureAliveUntilDone) {
+  base::Closure barrier_closure;
+  base::Closure done_closure =
+      base::Bind(ResetBarrierClosure, &barrier_closure);
+  barrier_closure = base::BarrierClosure(1, done_closure);
+  barrier_closure.Run();
+}
+
+}  // namespace
diff --git a/base/base_paths_android.cc b/base/base_paths_android.cc
new file mode 100644
index 0000000..078f565
--- /dev/null
+++ b/base/base_paths_android.cc
@@ -0,0 +1,66 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Defines base::PathProviderAndroid which replaces base::PathProviderPosix for
+// Android in base/path_service.cc.
+
+#include <limits.h>
+#include <unistd.h>
+
+#include "base/android/jni_android.h"
+#include "base/android/path_utils.h"
+#include "base/base_paths.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/process/process_metrics.h"
+
+namespace base {
+
+bool PathProviderAndroid(int key, FilePath* result) {
+  switch (key) {
+    case base::FILE_EXE: {
+      FilePath bin_dir;
+      if (!ReadSymbolicLink(FilePath(kProcSelfExe), &bin_dir)) {
+        NOTREACHED() << "Unable to resolve " << kProcSelfExe << ".";
+        return false;
+      }
+      *result = bin_dir;
+      return true;
+    }
+    case base::FILE_MODULE:
+      // dladdr didn't work in Android as only the file name was returned.
+      NOTIMPLEMENTED();
+      return false;
+    case base::DIR_MODULE:
+      return base::android::GetNativeLibraryDirectory(result);
+    case base::DIR_SOURCE_ROOT:
+      // Used only by tests.
+      // In that context, hooked up via base/test/test_support_android.cc.
+      NOTIMPLEMENTED();
+      return false;
+    case base::DIR_USER_DESKTOP:
+      // Android doesn't support GetUserDesktop.
+      NOTIMPLEMENTED();
+      return false;
+    case base::DIR_CACHE:
+      return base::android::GetCacheDirectory(result);
+    case base::DIR_ASSETS:
+      // On Android assets are normally loaded from the APK using
+      // base::android::OpenApkAsset(). In tests, since the assets are no
+      // packaged, DIR_ASSETS is overridden to point to the build directory.
+      return false;
+    case base::DIR_ANDROID_APP_DATA:
+      return base::android::GetDataDirectory(result);
+    case base::DIR_ANDROID_EXTERNAL_STORAGE:
+      return base::android::GetExternalStorageDirectory(result);
+    default:
+      // Note: the path system expects this function to override the default
+      // behavior. So no need to log an error if we don't support a given
+      // path. The system will just use the default.
+      return false;
+  }
+}
+
+}  // namespace base
diff --git a/base/base_paths_android.h b/base/base_paths_android.h
new file mode 100644
index 0000000..7a9ac4a
--- /dev/null
+++ b/base/base_paths_android.h
@@ -0,0 +1,25 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_BASE_PATHS_ANDROID_H_
+#define BASE_BASE_PATHS_ANDROID_H_
+
+// This file declares Android-specific path keys for the base module.
+// These can be used with the PathService to access various special
+// directories and files.
+
+namespace base {
+
+enum {
+  PATH_ANDROID_START = 300,
+
+  DIR_ANDROID_APP_DATA,  // Directory where to put Android app's data.
+  DIR_ANDROID_EXTERNAL_STORAGE,  // Android external storage directory.
+
+  PATH_ANDROID_END
+};
+
+}  // namespace base
+
+#endif  // BASE_BASE_PATHS_ANDROID_H_
diff --git a/base/bind_unittest.nc b/base/bind_unittest.nc
new file mode 100644
index 0000000..d549d2e8
--- /dev/null
+++ b/base/bind_unittest.nc
@@ -0,0 +1,322 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This is a "No Compile Test" suite.
+// http://dev.chromium.org/developers/testing/no-compile-tests
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/test/bind_test_util.h"
+
+namespace base {
+
+// Do not put everything inside an anonymous namespace.  If you do, many of the
+// helper function declarations will generate unused definition warnings.
+
+static const int kParentValue = 1;
+static const int kChildValue = 2;
+
+class NoRef {
+ public:
+  void VoidMethod0() {}
+  void VoidConstMethod0() const {}
+  int IntMethod0() { return 1; }
+};
+
+class HasRef : public NoRef, public base::RefCounted<HasRef> {
+};
+
+class Parent {
+ public:
+  void AddRef() const {}
+  void Release() const {}
+  virtual void VirtualSet() { value = kParentValue; }
+  void NonVirtualSet() { value = kParentValue; }
+  int value;
+};
+
+class Child : public Parent {
+ public:
+  virtual void VirtualSet() { value = kChildValue; }
+  void NonVirtualSet() { value = kChildValue; }
+};
+
+class NoRefParent {
+ public:
+  virtual void VirtualSet() { value = kParentValue; }
+  void NonVirtualSet() { value = kParentValue; }
+  int value;
+};
+
+class NoRefChild : public NoRefParent {
+  virtual void VirtualSet() { value = kChildValue; }
+  void NonVirtualSet() { value = kChildValue; }
+};
+
+template <typename T>
+T PolymorphicIdentity(T t) {
+  return t;
+}
+
+int UnwrapParentRef(Parent& p) {
+  return p.value;
+}
+
+template <typename T>
+void VoidPolymorphic1(T t) {
+}
+
+void TakesMoveOnly(std::unique_ptr<int>) {
+}
+
+struct NonEmptyFunctor {
+  int x;
+  void operator()() const {}
+};
+
+// TODO(hans): Remove .* and update the static_assert expectations once we roll
+// past Clang r313315. https://crbug.com/765692.
+
+#if defined(NCTEST_METHOD_ON_CONST_OBJECT)  // [r"fatal error: static_assert failed .*\"Bound argument \|i\| of type \|Arg\| cannot be forwarded as \|Unwrapped\| to the bound functor, which declares it as \|Param\|\.\""]
+
+// Method bound to const-object.
+//
+// Only const methods should be allowed to work with const objects.
+void WontCompile() {
+  HasRef has_ref;
+  const HasRef* const_has_ref_ptr_ = &has_ref;
+  Callback<void()> method_to_const_cb =
+      Bind(&HasRef::VoidMethod0, const_has_ref_ptr_);
+  method_to_const_cb.Run();
+}
+
+#elif defined(NCTEST_METHOD_BIND_NEEDS_REFCOUNTED_OBJECT)  // [r"fatal error: static_assert failed \"Receivers may not be raw pointers\."]
+
+
+// Method bound to non-refcounted object.
+//
+// We require refcounts unless you have Unretained().
+void WontCompile() {
+  NoRef no_ref;
+  Callback<void()> no_ref_cb =
+      Bind(&NoRef::VoidMethod0, &no_ref);
+  no_ref_cb.Run();
+}
+
+#elif defined(NCTEST_CONST_METHOD_NEEDS_REFCOUNTED_OBJECT)  // [r"fatal error: static_assert failed \"Receivers may not be raw pointers\."]
+
+// Const Method bound to non-refcounted object.
+//
+// We require refcounts unless you have Unretained().
+void WontCompile() {
+  NoRef no_ref;
+  Callback<void()> no_ref_const_cb =
+      Bind(&NoRef::VoidConstMethod0, &no_ref);
+  no_ref_const_cb.Run();
+}
+
+#elif defined(NCTEST_CONST_POINTER)  // [r"fatal error: static_assert failed .*\"Bound argument \|i\| of type \|Arg\| cannot be forwarded as \|Unwrapped\| to the bound functor, which declares it as \|Param\|\.\""]
+
+// Const argument used with non-const pointer parameter of same type.
+//
+// This is just a const-correctness check.
+void WontCompile() {
+  const NoRef* const_no_ref_ptr;
+  Callback<NoRef*()> pointer_same_cb =
+      Bind(&PolymorphicIdentity<NoRef*>, const_no_ref_ptr);
+  pointer_same_cb.Run();
+}
+
+#elif defined(NCTEST_CONST_POINTER_SUBTYPE)  // [r"fatal error: static_assert failed .*\"Bound argument \|i\| of type \|Arg\| cannot be forwarded as \|Unwrapped\| to the bound functor, which declares it as \|Param\|\.\""]
+
+// Const argument used with non-const pointer parameter of super type.
+//
+// This is just a const-correctness check.
+void WontCompile() {
+  const NoRefChild* const_child_ptr;
+  Callback<NoRefParent*()> pointer_super_cb =
+    Bind(&PolymorphicIdentity<NoRefParent*>, const_child_ptr);
+  pointer_super_cb.Run();
+}
+
+#elif defined(DISABLED_NCTEST_DISALLOW_NON_CONST_REF_PARAM)  // [r"fatal error: no member named 'AddRef' in 'base::NoRef'"]
+// TODO(dcheng): I think there's a type safety promotion issue here where we can
+// pass a const ref to a non const-ref function, or vice versa accidentally. Or
+// we make a copy accidentally. Check.
+
+// Functions with reference parameters, unsupported.
+//
+// First, non-const reference parameters are disallowed by the Google
+// style guide. Second, since we are doing argument forwarding it becomes
+// very tricky to avoid copies, maintain const correctness, and not
+// accidentally have the function be modifying a temporary, or a copy.
+void WontCompile() {
+  Parent p;
+  Callback<int(Parent&)> ref_arg_cb = Bind(&UnwrapParentRef);
+  ref_arg_cb.Run(p);
+}
+
+#elif defined(NCTEST_DISALLOW_BIND_TO_NON_CONST_REF_PARAM)  // [r"fatal error: static_assert failed .*\"Bound argument \|i\| of type \|Arg\| cannot be forwarded as \|Unwrapped\| to the bound functor, which declares it as \|Param\|\.\""]
+
+// Binding functions with reference parameters, unsupported.
+//
+// See comment in NCTEST_DISALLOW_NON_CONST_REF_PARAM
+void WontCompile() {
+  Parent p;
+  Callback<int()> ref_cb = Bind(&UnwrapParentRef, p);
+  ref_cb.Run();
+}
+
+#elif defined(NCTEST_NO_IMPLICIT_ARRAY_PTR_CONVERSION)  // [r"fatal error: static_assert failed .*\"First bound argument to a method cannot be an array\.\""]
+
+// A method should not be bindable with an array of objects.
+//
+// This is likely not wanted behavior. We specifically check for it though
+// because it is possible, depending on how you implement prebinding, to
+// implicitly convert an array type to a pointer type.
+void WontCompile() {
+  HasRef p[10];
+  Callback<void()> method_bound_to_array_cb =
+      Bind(&HasRef::VoidMethod0, p);
+  method_bound_to_array_cb.Run();
+}
+
+#elif defined(NCTEST_NO_RVALUE_RAW_PTR_FOR_REFCOUNTED_TYPES)  // [r"fatal error: static_assert failed .*\"A parameter is a refcounted type and needs scoped_refptr\.\""]
+
+// Refcounted types should not be bound as a raw pointer.
+void WontCompile() {
+  HasRef for_raw_ptr;
+  int a;
+  Callback<void()> ref_count_as_raw_ptr_a =
+      Bind(&VoidPolymorphic1<int*>, &a);
+  Callback<void()> ref_count_as_raw_ptr =
+      Bind(&VoidPolymorphic1<HasRef*>, &for_raw_ptr);
+}
+
+#elif defined(NCTEST_NO_LVALUE_RAW_PTR_FOR_REFCOUNTED_TYPES)  // [r"fatal error: static_assert failed .*\"A parameter is a refcounted type and needs scoped_refptr\.\""]
+
+// Refcounted types should not be bound as a raw pointer.
+void WontCompile() {
+  HasRef* for_raw_ptr = nullptr;
+  Callback<void()> ref_count_as_raw_ptr =
+      Bind(&VoidPolymorphic1<HasRef*>, for_raw_ptr);
+}
+
+#elif defined(NCTEST_NO_RVALUE_CONST_RAW_PTR_FOR_REFCOUNTED_TYPES)  // [r"fatal error: static_assert failed .*\"A parameter is a refcounted type and needs scoped_refptr\.\""]
+
+// Refcounted types should not be bound as a raw pointer.
+void WontCompile() {
+  const HasRef for_raw_ptr;
+  Callback<void()> ref_count_as_raw_ptr =
+      Bind(&VoidPolymorphic1<const HasRef*>, &for_raw_ptr);
+}
+
+#elif defined(NCTEST_NO_LVALUE_CONST_RAW_PTR_FOR_REFCOUNTED_TYPES)  // [r"fatal error: static_assert failed .*\"A parameter is a refcounted type and needs scoped_refptr\.\""]
+
+// Refcounted types should not be bound as a raw pointer.
+void WontCompile() {
+  const HasRef* for_raw_ptr = nullptr;
+  Callback<void()> ref_count_as_raw_ptr =
+      Bind(&VoidPolymorphic1<const HasRef*>, for_raw_ptr);
+}
+
+#elif defined(NCTEST_WEAKPTR_BIND_MUST_RETURN_VOID)  // [r"fatal error: static_assert failed .*\"weak_ptrs can only bind to methods without return values\""]
+
+// WeakPtrs cannot be bound to methods with return types.
+void WontCompile() {
+  NoRef no_ref;
+  WeakPtrFactory<NoRef> weak_factory(&no_ref);
+  Callback<int()> weak_ptr_with_non_void_return_type =
+      Bind(&NoRef::IntMethod0, weak_factory.GetWeakPtr());
+  weak_ptr_with_non_void_return_type.Run();
+}
+
+#elif defined(NCTEST_DISALLOW_ASSIGN_DIFFERENT_TYPES)  // [r"fatal error: no viable conversion from 'Callback<MakeUnboundRunType<void \(\*\)\(int\)>>' to 'Callback<void \(\)>'"]
+
+// Bind result cannot be assigned to Callbacks with a mismatching type.
+void WontCompile() {
+  Closure callback_mismatches_bind_type = Bind(&VoidPolymorphic1<int>);
+}
+
+#elif defined(NCTEST_DISALLOW_CAPTURING_LAMBDA)  // [r"fatal error: implicit instantiation of undefined template 'base::internal::FunctorTraits<\(lambda at (\.\./)+base/bind_unittest.nc:[0-9]+:[0-9]+\), void>'"]
+
+void WontCompile() {
+  int i = 0, j = 0;
+  Bind([i,&j]() {j = i;});
+}
+
+#elif defined(NCTEST_DISALLOW_ONCECALLBACK_RUN_ON_LVALUE)  // [r"static_assert failed .*\"OnceCallback::Run\(\) may only be invoked on a non-const rvalue, i\.e\. std::move\(callback\)\.Run\(\)\.\""]
+
+void WontCompile() {
+  OnceClosure cb = Bind([] {});
+  cb.Run();
+}
+
+#elif defined(NCTEST_DISALLOW_ONCECALLBACK_RUN_ON_CONST_LVALUE)  // [r"static_assert failed .*\"OnceCallback::Run\(\) may only be invoked on a non-const rvalue, i\.e\. std::move\(callback\)\.Run\(\)\.\""]
+
+void WontCompile() {
+  const OnceClosure cb = Bind([] {});
+  cb.Run();
+}
+
+#elif defined(NCTEST_DISALLOW_ONCECALLBACK_RUN_ON_CONST_RVALUE)  // [r"static_assert failed .*\"OnceCallback::Run\(\) may only be invoked on a non-const rvalue, i\.e\. std::move\(callback\)\.Run\(\)\.\""]
+
+void WontCompile() {
+  const OnceClosure cb = Bind([] {});
+  std::move(cb).Run();
+}
+
+#elif defined(NCTEST_DISALLOW_BIND_ONCECALLBACK)  // [r"fatal error: static_assert failed .*\"BindRepeating cannot bind OnceCallback. Use BindOnce with std::move\(\)\.\""]
+
+void WontCompile() {
+  Bind(BindOnce([](int) {}), 42);
+}
+
+#elif defined(NCTEST_DISALLOW_BINDONCE_LVALUE_ONCECALLBACK)  // [r"fatal error: static_assert failed .*\"BindOnce requires non-const rvalue for OnceCallback binding\."]
+void WontCompile() {
+  auto cb = BindOnce([](int) {});
+  BindOnce(cb, 42);
+}
+
+#elif defined(NCTEST_DISALLOW_BINDONCE_RVALUE_CONST_ONCECALLBACK)  // [r"fatal error: static_assert failed .*\"BindOnce requires non-const rvalue for OnceCallback binding\."]
+
+void WontCompile() {
+  const auto cb = BindOnce([](int) {});
+  BindOnce(std::move(cb), 42);
+}
+
+#elif defined(NCTEST_BINDONCE_MOVEONLY_TYPE_BY_VALUE)  // [r"fatal error: static_assert failed .*\"Bound argument \|i\| is move-only but will be bound by copy\. Ensure \|Arg\| is mutable and bound using std::move\(\)\.\""]
+
+void WontCompile() {
+  std::unique_ptr<int> x;
+  BindOnce(&TakesMoveOnly, x);
+}
+
+#elif defined(NCTEST_BIND_MOVEONLY_TYPE_BY_VALUE)  // [r"Bound argument \|i\| is move-only but will be forwarded by copy\. Ensure \|Arg\| is bound using base::Passed\(\), not std::move\(\)."]
+
+void WontCompile() {
+  std::unique_ptr<int> x;
+  Bind(&TakesMoveOnly, x);
+}
+
+#elif defined(NCTEST_BIND_MOVEONLY_TYPE_WITH_STDMOVE)  // [r"Bound argument \|i\| is move-only but will be forwarded by copy\. Ensure \|Arg\| is bound using base::Passed\(\), not std::move\(\)."]
+
+void WontCompile() {
+  std::unique_ptr<int> x;
+  Bind(&TakesMoveOnly, std::move(x));
+}
+
+#elif defined(NCTEST_BIND_NON_EMPTY_FUNCTOR)  // [r"fatal error: implicit instantiation of undefined template 'base::internal::FunctorTraits<base::NonEmptyFunctor, void>'"]
+
+void WontCompile() {
+  Bind(NonEmptyFunctor());
+}
+
+#endif
+
+}  // namespace base
diff --git a/base/bit_cast_unittest.cc b/base/bit_cast_unittest.cc
new file mode 100644
index 0000000..f36d3fe
--- /dev/null
+++ b/base/bit_cast_unittest.cc
@@ -0,0 +1,31 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+
+#include "base/bit_cast.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace {
+
+TEST(BitCastTest, FloatIntFloat) {
+  float f = 3.1415926f;
+  int i = bit_cast<int32_t>(f);
+  float f2 = bit_cast<float>(i);
+  EXPECT_EQ(f, f2);
+}
+
+struct A {
+  int x;
+};
+
+TEST(BitCastTest, StructureInt) {
+  A a = { 1 };
+  int b = bit_cast<int>(a);
+  EXPECT_EQ(1, b);
+}
+
+}  // namespace
+}  // namespace base
diff --git a/base/callback_list_unittest.nc b/base/callback_list_unittest.nc
new file mode 100644
index 0000000..7347f76
--- /dev/null
+++ b/base/callback_list_unittest.nc
@@ -0,0 +1,56 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This is a "No Compile Test" suite.
+// http://dev.chromium.org/developers/testing/no-compile-tests
+
+#include "base/callback_list.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/macros.h"
+
+namespace base {
+
+class Foo {
+ public:
+  Foo() {}
+  ~Foo() {}
+};
+
+class FooListener {
+ public:
+  FooListener() {}
+
+  void GotAScopedFoo(std::unique_ptr<Foo> f) { foo_ = std::move(f); }
+
+  std::unique_ptr<Foo> foo_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(FooListener);
+};
+
+
+#if defined(NCTEST_MOVE_ONLY_TYPE_PARAMETER)  // [r"fatal error: call to (implicitly-)?deleted( copy)? constructor"]
+
+// Callbacks run with a move-only typed parameter.
+//
+// CallbackList does not support move-only typed parameters. Notify() is
+// designed to take zero or more parameters, and run each registered callback
+// with them. With move-only types, the parameter will be set to NULL after the
+// first callback has been run.
+void WontCompile() {
+  FooListener f;
+  CallbackList<void(std::unique_ptr<Foo>)> c1;
+  std::unique_ptr<CallbackList<void(std::unique_ptr<Foo>)>::Subscription> sub =
+      c1.Add(Bind(&FooListener::GotAScopedFoo, Unretained(&f)));
+  c1.Notify(std::unique_ptr<Foo>(new Foo()));
+}
+
+#endif
+
+}  // namespace base
diff --git a/base/callback_unittest.nc b/base/callback_unittest.nc
new file mode 100644
index 0000000..3261529
--- /dev/null
+++ b/base/callback_unittest.nc
@@ -0,0 +1,53 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This is a "No Compile Test" suite.
+// http://dev.chromium.org/developers/testing/no-compile-tests
+
+#include "base/callback.h"
+
+namespace base {
+
+class Parent {
+};
+
+class Child : Parent {
+};
+
+#if defined(NCTEST_EQUALS_REQUIRES_SAMETYPE)  // [r"fatal error: no viable conversion from 'RepeatingCallback<int \(\)>' to 'const RepeatingCallback<void \(\)>'"]
+
+// Attempting to call comparison function on two callbacks of different type.
+//
+// This should be a compile time failure because each callback type should be
+// considered distinct.
+void WontCompile() {
+  Closure c1;
+  Callback<int()> c2;
+  c1.Equals(c2);
+}
+
+#elif defined(NCTEST_CONSTRUCTION_FROM_SUBTYPE)  // [r"fatal error: no viable conversion from 'Callback<base::Parent \(\)>' to 'Callback<base::Child \(\)>'"]
+
+// Construction of Callback<A> from Callback<B> if A is supertype of B.
+//
+// While this is technically safe, most people aren't used to it when coding
+// C++ so if this is happening, it is almost certainly an error.
+void WontCompile() {
+  Callback<Parent()> cb_a;
+  Callback<Child()> cb_b = cb_a;
+}
+
+#elif defined(NCTEST_ASSIGNMENT_FROM_SUBTYPE)  // [r"fatal error: no viable overloaded '='"]
+
+// Assignment of Callback<A> from Callback<B> if A is supertype of B.
+// See explanation for NCTEST_CONSTRUCTION_FROM_SUBTYPE
+void WontCompile() {
+  Callback<Parent()> cb_a;
+  Callback<Child()> cb_b;
+  cb_a = cb_b;
+}
+
+#endif
+
+}  // namespace base
diff --git a/base/check_example.cc b/base/check_example.cc
new file mode 100644
index 0000000..7b9d8e6
--- /dev/null
+++ b/base/check_example.cc
@@ -0,0 +1,37 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file is meant for analyzing the code generated by the CHECK
+// macros in a small executable file that's easy to disassemble.
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+
+// An official build shouldn't generate code to print out messages for
+// the CHECK* macros, nor should it have the strings in the
+// executable. It is also important that the CHECK() function collapse to the
+// same implementation as RELEASE_ASSERT(), in particular on Windows x86.
+// Historically, the stream eating caused additional unnecessary instructions.
+// See https://crbug.com/672699.
+
+#define BLINK_RELEASE_ASSERT_EQUIVALENT(assertion) \
+  (UNLIKELY(!(assertion)) ? (IMMEDIATE_CRASH()) : (void)0)
+
+void DoCheck(bool b) {
+  CHECK(b) << "DoCheck " << b;
+}
+
+void DoBlinkReleaseAssert(bool b) {
+  BLINK_RELEASE_ASSERT_EQUIVALENT(b);
+}
+
+void DoCheckEq(int x, int y) {
+  CHECK_EQ(x, y);
+}
+
+int main(int argc, const char* argv[]) {
+  DoCheck(argc > 1);
+  DoCheckEq(argc, 1);
+  DoBlinkReleaseAssert(argc > 1);
+}
diff --git a/base/containers/adapters_unittest.cc b/base/containers/adapters_unittest.cc
new file mode 100644
index 0000000..92554b7
--- /dev/null
+++ b/base/containers/adapters_unittest.cc
@@ -0,0 +1,52 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/containers/adapters.h"
+
+#include <vector>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+TEST(AdaptersTest, Reversed) {
+  std::vector<int> v;
+  v.push_back(3);
+  v.push_back(2);
+  v.push_back(1);
+  int j = 0;
+  for (int& i : base::Reversed(v)) {
+    EXPECT_EQ(++j, i);
+    i += 100;
+  }
+  EXPECT_EQ(103, v[0]);
+  EXPECT_EQ(102, v[1]);
+  EXPECT_EQ(101, v[2]);
+}
+
+TEST(AdaptersTest, ReversedArray) {
+  int v[3] = {3, 2, 1};
+  int j = 0;
+  for (int& i : base::Reversed(v)) {
+    EXPECT_EQ(++j, i);
+    i += 100;
+  }
+  EXPECT_EQ(103, v[0]);
+  EXPECT_EQ(102, v[1]);
+  EXPECT_EQ(101, v[2]);
+}
+
+TEST(AdaptersTest, ReversedConst) {
+  std::vector<int> v;
+  v.push_back(3);
+  v.push_back(2);
+  v.push_back(1);
+  const std::vector<int>& cv = v;
+  int j = 0;
+  for (int i : base::Reversed(cv)) {
+    EXPECT_EQ(++j, i);
+  }
+}
+
+}  // namespace
diff --git a/base/containers/flat_map_unittest.cc b/base/containers/flat_map_unittest.cc
new file mode 100644
index 0000000..87958bd
--- /dev/null
+++ b/base/containers/flat_map_unittest.cc
@@ -0,0 +1,369 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/containers/flat_map.h"
+
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/test/move_only_int.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// A flat_map is basically a interface to flat_tree. So several basic
+// operations are tested to make sure things are set up properly, but the bulk
+// of the tests are in flat_tree_unittests.cc.
+
+using ::testing::ElementsAre;
+
+namespace base {
+
+TEST(FlatMap, IncompleteType) {
+  struct A {
+    using Map = flat_map<A, A>;
+    int data;
+    Map set_with_incomplete_type;
+    Map::iterator it;
+    Map::const_iterator cit;
+
+    // We do not declare operator< because clang complains that it's unused.
+  };
+
+  A a;
+}
+
+TEST(FlatMap, RangeConstructor) {
+  flat_map<int, int>::value_type input_vals[] = {
+      {1, 1}, {1, 2}, {1, 3}, {2, 1}, {2, 2}, {2, 3}, {3, 1}, {3, 2}, {3, 3}};
+
+  flat_map<int, int> first(std::begin(input_vals), std::end(input_vals));
+  EXPECT_THAT(first, ElementsAre(std::make_pair(1, 1), std::make_pair(2, 1),
+                                 std::make_pair(3, 1)));
+
+  flat_map<int, int> last(std::begin(input_vals), std::end(input_vals),
+                          KEEP_LAST_OF_DUPES);
+  EXPECT_THAT(last, ElementsAre(std::make_pair(1, 3), std::make_pair(2, 3),
+                                std::make_pair(3, 3)));
+}
+
+TEST(FlatMap, MoveConstructor) {
+  using pair = std::pair<MoveOnlyInt, MoveOnlyInt>;
+
+  flat_map<MoveOnlyInt, MoveOnlyInt> original;
+  original.insert(pair(MoveOnlyInt(1), MoveOnlyInt(1)));
+  original.insert(pair(MoveOnlyInt(2), MoveOnlyInt(2)));
+  original.insert(pair(MoveOnlyInt(3), MoveOnlyInt(3)));
+  original.insert(pair(MoveOnlyInt(4), MoveOnlyInt(4)));
+
+  flat_map<MoveOnlyInt, MoveOnlyInt> moved(std::move(original));
+
+  EXPECT_EQ(1U, moved.count(MoveOnlyInt(1)));
+  EXPECT_EQ(1U, moved.count(MoveOnlyInt(2)));
+  EXPECT_EQ(1U, moved.count(MoveOnlyInt(3)));
+  EXPECT_EQ(1U, moved.count(MoveOnlyInt(4)));
+}
+
+TEST(FlatMap, VectorConstructor) {
+  using IntPair = std::pair<int, int>;
+  using IntMap = flat_map<int, int>;
+  {
+    std::vector<IntPair> vect{{1, 1}, {1, 2}, {2, 1}};
+    IntMap map(std::move(vect));
+    EXPECT_THAT(map, ElementsAre(IntPair(1, 1), IntPair(2, 1)));
+  }
+  {
+    std::vector<IntPair> vect{{1, 1}, {1, 2}, {2, 1}};
+    IntMap map(std::move(vect), KEEP_LAST_OF_DUPES);
+    EXPECT_THAT(map, ElementsAre(IntPair(1, 2), IntPair(2, 1)));
+  }
+}
+
+TEST(FlatMap, InitializerListConstructor) {
+  {
+    flat_map<int, int> cont(
+        {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}, {1, 2}, {10, 10}, {8, 8}});
+    EXPECT_THAT(cont, ElementsAre(std::make_pair(1, 1), std::make_pair(2, 2),
+                                  std::make_pair(3, 3), std::make_pair(4, 4),
+                                  std::make_pair(5, 5), std::make_pair(8, 8),
+                                  std::make_pair(10, 10)));
+  }
+  {
+    flat_map<int, int> cont(
+        {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}, {1, 2}, {10, 10}, {8, 8}},
+        KEEP_LAST_OF_DUPES);
+    EXPECT_THAT(cont, ElementsAre(std::make_pair(1, 2), std::make_pair(2, 2),
+                                  std::make_pair(3, 3), std::make_pair(4, 4),
+                                  std::make_pair(5, 5), std::make_pair(8, 8),
+                                  std::make_pair(10, 10)));
+  }
+}
+
+TEST(FlatMap, InitializerListAssignment) {
+  flat_map<int, int> cont;
+  cont = {{1, 1}, {2, 2}};
+  EXPECT_THAT(cont, ElementsAre(std::make_pair(1, 1), std::make_pair(2, 2)));
+}
+
+TEST(FlatMap, InsertFindSize) {
+  base::flat_map<int, int> s;
+  s.insert(std::make_pair(1, 1));
+  s.insert(std::make_pair(1, 1));
+  s.insert(std::make_pair(2, 2));
+
+  EXPECT_EQ(2u, s.size());
+  EXPECT_EQ(std::make_pair(1, 1), *s.find(1));
+  EXPECT_EQ(std::make_pair(2, 2), *s.find(2));
+  EXPECT_EQ(s.end(), s.find(7));
+}
+
+TEST(FlatMap, CopySwap) {
+  base::flat_map<int, int> original;
+  original.insert({1, 1});
+  original.insert({2, 2});
+  EXPECT_THAT(original,
+              ElementsAre(std::make_pair(1, 1), std::make_pair(2, 2)));
+
+  base::flat_map<int, int> copy(original);
+  EXPECT_THAT(copy, ElementsAre(std::make_pair(1, 1), std::make_pair(2, 2)));
+
+  copy.erase(copy.begin());
+  copy.insert({10, 10});
+  EXPECT_THAT(copy, ElementsAre(std::make_pair(2, 2), std::make_pair(10, 10)));
+
+  original.swap(copy);
+  EXPECT_THAT(original,
+              ElementsAre(std::make_pair(2, 2), std::make_pair(10, 10)));
+  EXPECT_THAT(copy, ElementsAre(std::make_pair(1, 1), std::make_pair(2, 2)));
+}
+
+// operator[](const Key&)
+TEST(FlatMap, SubscriptConstKey) {
+  base::flat_map<std::string, int> m;
+
+  // Default construct elements that don't exist yet.
+  int& s = m["a"];
+  EXPECT_EQ(0, s);
+  EXPECT_EQ(1u, m.size());
+
+  // The returned mapped reference should refer into the map.
+  s = 22;
+  EXPECT_EQ(22, m["a"]);
+
+  // Overwrite existing elements.
+  m["a"] = 44;
+  EXPECT_EQ(44, m["a"]);
+}
+
+// operator[](Key&&)
+TEST(FlatMap, SubscriptMoveOnlyKey) {
+  base::flat_map<MoveOnlyInt, int> m;
+
+  // Default construct elements that don't exist yet.
+  int& s = m[MoveOnlyInt(1)];
+  EXPECT_EQ(0, s);
+  EXPECT_EQ(1u, m.size());
+
+  // The returned mapped reference should refer into the map.
+  s = 22;
+  EXPECT_EQ(22, m[MoveOnlyInt(1)]);
+
+  // Overwrite existing elements.
+  m[MoveOnlyInt(1)] = 44;
+  EXPECT_EQ(44, m[MoveOnlyInt(1)]);
+}
+
+// insert_or_assign(K&&, M&&)
+TEST(FlatMap, InsertOrAssignMoveOnlyKey) {
+  base::flat_map<MoveOnlyInt, MoveOnlyInt> m;
+
+  // Initial insertion should return an iterator to the element and set the
+  // second pair member to |true|. The inserted key and value should be moved
+  // from.
+  MoveOnlyInt key(1);
+  MoveOnlyInt val(22);
+  auto result = m.insert_or_assign(std::move(key), std::move(val));
+  EXPECT_EQ(1, result.first->first.data());
+  EXPECT_EQ(22, result.first->second.data());
+  EXPECT_TRUE(result.second);
+  EXPECT_EQ(1u, m.size());
+  EXPECT_EQ(0, key.data());  // moved from
+  EXPECT_EQ(0, val.data());  // moved from
+
+  // Second call with same key should result in an assignment, overwriting the
+  // old value. Assignment should be indicated by setting the second pair member
+  // to |false|. Only the inserted value should be moved from, the key should be
+  // left intact.
+  key = MoveOnlyInt(1);
+  val = MoveOnlyInt(44);
+  result = m.insert_or_assign(std::move(key), std::move(val));
+  EXPECT_EQ(1, result.first->first.data());
+  EXPECT_EQ(44, result.first->second.data());
+  EXPECT_FALSE(result.second);
+  EXPECT_EQ(1u, m.size());
+  EXPECT_EQ(1, key.data());  // not moved from
+  EXPECT_EQ(0, val.data());  // moved from
+
+  // Check that random insertion results in sorted range.
+  base::flat_map<MoveOnlyInt, int> map;
+  for (int i : {3, 1, 5, 6, 8, 7, 0, 9, 4, 2}) {
+    map.insert_or_assign(MoveOnlyInt(i), i);
+    EXPECT_TRUE(std::is_sorted(map.begin(), map.end()));
+  }
+}
+
+// insert_or_assign(const_iterator hint, K&&, M&&)
+TEST(FlatMap, InsertOrAssignMoveOnlyKeyWithHint) {
+  base::flat_map<MoveOnlyInt, MoveOnlyInt> m;
+
+  // Initial insertion should return an iterator to the element. The inserted
+  // key and value should be moved from.
+  MoveOnlyInt key(1);
+  MoveOnlyInt val(22);
+  auto result = m.insert_or_assign(m.end(), std::move(key), std::move(val));
+  EXPECT_EQ(1, result->first.data());
+  EXPECT_EQ(22, result->second.data());
+  EXPECT_EQ(1u, m.size());
+  EXPECT_EQ(0, key.data());  // moved from
+  EXPECT_EQ(0, val.data());  // moved from
+
+  // Second call with same key should result in an assignment, overwriting the
+  // old value. Only the inserted value should be moved from, the key should be
+  // left intact.
+  key = MoveOnlyInt(1);
+  val = MoveOnlyInt(44);
+  result = m.insert_or_assign(m.end(), std::move(key), std::move(val));
+  EXPECT_EQ(1, result->first.data());
+  EXPECT_EQ(44, result->second.data());
+  EXPECT_EQ(1u, m.size());
+  EXPECT_EQ(1, key.data());  // not moved from
+  EXPECT_EQ(0, val.data());  // moved from
+
+  // Check that random insertion results in sorted range.
+  base::flat_map<MoveOnlyInt, int> map;
+  for (int i : {3, 1, 5, 6, 8, 7, 0, 9, 4, 2}) {
+    map.insert_or_assign(map.end(), MoveOnlyInt(i), i);
+    EXPECT_TRUE(std::is_sorted(map.begin(), map.end()));
+  }
+}
+
+// try_emplace(K&&, Args&&...)
+TEST(FlatMap, TryEmplaceMoveOnlyKey) {
+  base::flat_map<MoveOnlyInt, std::pair<MoveOnlyInt, MoveOnlyInt>> m;
+
+  // Trying to emplace into an empty map should succeed. Insertion should return
+  // an iterator to the element and set the second pair member to |true|. The
+  // inserted key and value should be moved from.
+  MoveOnlyInt key(1);
+  MoveOnlyInt val1(22);
+  MoveOnlyInt val2(44);
+  // Test piecewise construction of mapped_type.
+  auto result = m.try_emplace(std::move(key), std::move(val1), std::move(val2));
+  EXPECT_EQ(1, result.first->first.data());
+  EXPECT_EQ(22, result.first->second.first.data());
+  EXPECT_EQ(44, result.first->second.second.data());
+  EXPECT_TRUE(result.second);
+  EXPECT_EQ(1u, m.size());
+  EXPECT_EQ(0, key.data());   // moved from
+  EXPECT_EQ(0, val1.data());  // moved from
+  EXPECT_EQ(0, val2.data());  // moved from
+
+  // Second call with same key should result in a no-op, returning an iterator
+  // to the existing element and returning false as the second pair member.
+  // Key and values that were attempted to be inserted should be left intact.
+  key = MoveOnlyInt(1);
+  auto paired_val = std::make_pair(MoveOnlyInt(33), MoveOnlyInt(55));
+  // Test construction of mapped_type from pair.
+  result = m.try_emplace(std::move(key), std::move(paired_val));
+  EXPECT_EQ(1, result.first->first.data());
+  EXPECT_EQ(22, result.first->second.first.data());
+  EXPECT_EQ(44, result.first->second.second.data());
+  EXPECT_FALSE(result.second);
+  EXPECT_EQ(1u, m.size());
+  EXPECT_EQ(1, key.data());                 // not moved from
+  EXPECT_EQ(33, paired_val.first.data());   // not moved from
+  EXPECT_EQ(55, paired_val.second.data());  // not moved from
+
+  // Check that random insertion results in sorted range.
+  base::flat_map<MoveOnlyInt, int> map;
+  for (int i : {3, 1, 5, 6, 8, 7, 0, 9, 4, 2}) {
+    map.try_emplace(MoveOnlyInt(i), i);
+    EXPECT_TRUE(std::is_sorted(map.begin(), map.end()));
+  }
+}
+
+// try_emplace(const_iterator hint, K&&, Args&&...)
+TEST(FlatMap, TryEmplaceMoveOnlyKeyWithHint) {
+  base::flat_map<MoveOnlyInt, std::pair<MoveOnlyInt, MoveOnlyInt>> m;
+
+  // Trying to emplace into an empty map should succeed. Insertion should return
+  // an iterator to the element. The inserted key and value should be moved
+  // from.
+  MoveOnlyInt key(1);
+  MoveOnlyInt val1(22);
+  MoveOnlyInt val2(44);
+  // Test piecewise construction of mapped_type.
+  auto result =
+      m.try_emplace(m.end(), std::move(key), std::move(val1), std::move(val2));
+  EXPECT_EQ(1, result->first.data());
+  EXPECT_EQ(22, result->second.first.data());
+  EXPECT_EQ(44, result->second.second.data());
+  EXPECT_EQ(1u, m.size());
+  EXPECT_EQ(0, key.data());   // moved from
+  EXPECT_EQ(0, val1.data());  // moved from
+  EXPECT_EQ(0, val2.data());  // moved from
+
+  // Second call with same key should result in a no-op, returning an iterator
+  // to the existing element. Key and values that were attempted to be inserted
+  // should be left intact.
+  key = MoveOnlyInt(1);
+  val1 = MoveOnlyInt(33);
+  val2 = MoveOnlyInt(55);
+  auto paired_val = std::make_pair(MoveOnlyInt(33), MoveOnlyInt(55));
+  // Test construction of mapped_type from pair.
+  result = m.try_emplace(m.end(), std::move(key), std::move(paired_val));
+  EXPECT_EQ(1, result->first.data());
+  EXPECT_EQ(22, result->second.first.data());
+  EXPECT_EQ(44, result->second.second.data());
+  EXPECT_EQ(1u, m.size());
+  EXPECT_EQ(1, key.data());                 // not moved from
+  EXPECT_EQ(33, paired_val.first.data());   // not moved from
+  EXPECT_EQ(55, paired_val.second.data());  // not moved from
+
+  // Check that random insertion results in sorted range.
+  base::flat_map<MoveOnlyInt, int> map;
+  for (int i : {3, 1, 5, 6, 8, 7, 0, 9, 4, 2}) {
+    map.try_emplace(map.end(), MoveOnlyInt(i), i);
+    EXPECT_TRUE(std::is_sorted(map.begin(), map.end()));
+  }
+}
+
+TEST(FlatMap, UsingTransparentCompare) {
+  using ExplicitInt = base::MoveOnlyInt;
+  base::flat_map<ExplicitInt, int> m;
+  const auto& m1 = m;
+  int x = 0;
+
+  // Check if we can use lookup functions without converting to key_type.
+  // Correctness is checked in flat_tree tests.
+  m.count(x);
+  m1.count(x);
+  m.find(x);
+  m1.find(x);
+  m.equal_range(x);
+  m1.equal_range(x);
+  m.lower_bound(x);
+  m1.lower_bound(x);
+  m.upper_bound(x);
+  m1.upper_bound(x);
+  m.erase(x);
+
+  // Check if we broke overload resolution.
+  m.emplace(ExplicitInt(0), 0);
+  m.emplace(ExplicitInt(1), 0);
+  m.erase(m.begin());
+  m.erase(m.cbegin());
+}
+
+}  // namespace base
diff --git a/base/containers/flat_set_unittest.cc b/base/containers/flat_set_unittest.cc
new file mode 100644
index 0000000..4596975
--- /dev/null
+++ b/base/containers/flat_set_unittest.cc
@@ -0,0 +1,121 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/containers/flat_set.h"
+
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/test/move_only_int.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// A flat_set is basically a interface to flat_tree. So several basic
+// operations are tested to make sure things are set up properly, but the bulk
+// of the tests are in flat_tree_unittests.cc.
+
+using ::testing::ElementsAre;
+
+namespace base {
+
+TEST(FlatSet, IncompleteType) {
+  struct A {
+    using Set = flat_set<A>;
+    int data;
+    Set set_with_incomplete_type;
+    Set::iterator it;
+    Set::const_iterator cit;
+
+    // We do not declare operator< because clang complains that it's unused.
+  };
+
+  A a;
+}
+
+TEST(FlatSet, RangeConstructor) {
+  flat_set<int>::value_type input_vals[] = {1, 1, 1, 2, 2, 2, 3, 3, 3};
+
+  flat_set<int> cont(std::begin(input_vals), std::end(input_vals),
+                     base::KEEP_FIRST_OF_DUPES);
+  EXPECT_THAT(cont, ElementsAre(1, 2, 3));
+}
+
+TEST(FlatSet, MoveConstructor) {
+  int input_range[] = {1, 2, 3, 4};
+
+  flat_set<MoveOnlyInt> original(std::begin(input_range), std::end(input_range),
+                                 base::KEEP_FIRST_OF_DUPES);
+  flat_set<MoveOnlyInt> moved(std::move(original));
+
+  EXPECT_EQ(1U, moved.count(MoveOnlyInt(1)));
+  EXPECT_EQ(1U, moved.count(MoveOnlyInt(2)));
+  EXPECT_EQ(1U, moved.count(MoveOnlyInt(3)));
+  EXPECT_EQ(1U, moved.count(MoveOnlyInt(4)));
+}
+
+TEST(FlatSet, InitializerListConstructor) {
+  flat_set<int> cont({1, 2, 3, 4, 5, 6, 10, 8}, KEEP_FIRST_OF_DUPES);
+  EXPECT_THAT(cont, ElementsAre(1, 2, 3, 4, 5, 6, 8, 10));
+}
+
+TEST(FlatSet, InsertFindSize) {
+  base::flat_set<int> s;
+  s.insert(1);
+  s.insert(1);
+  s.insert(2);
+
+  EXPECT_EQ(2u, s.size());
+  EXPECT_EQ(1, *s.find(1));
+  EXPECT_EQ(2, *s.find(2));
+  EXPECT_EQ(s.end(), s.find(7));
+}
+
+TEST(FlatSet, CopySwap) {
+  base::flat_set<int> original;
+  original.insert(1);
+  original.insert(2);
+  EXPECT_THAT(original, ElementsAre(1, 2));
+
+  base::flat_set<int> copy(original);
+  EXPECT_THAT(copy, ElementsAre(1, 2));
+
+  copy.erase(copy.begin());
+  copy.insert(10);
+  EXPECT_THAT(copy, ElementsAre(2, 10));
+
+  original.swap(copy);
+  EXPECT_THAT(original, ElementsAre(2, 10));
+  EXPECT_THAT(copy, ElementsAre(1, 2));
+}
+
+TEST(FlatSet, UsingTransparentCompare) {
+  using ExplicitInt = base::MoveOnlyInt;
+  base::flat_set<ExplicitInt> s;
+  const auto& s1 = s;
+  int x = 0;
+
+  // Check if we can use lookup functions without converting to key_type.
+  // Correctness is checked in flat_tree tests.
+  s.count(x);
+  s1.count(x);
+  s.find(x);
+  s1.find(x);
+  s.equal_range(x);
+  s1.equal_range(x);
+  s.lower_bound(x);
+  s1.lower_bound(x);
+  s.upper_bound(x);
+  s1.upper_bound(x);
+  s.erase(x);
+
+  // Check if we broke overload resolution.
+  s.emplace(0);
+  s.emplace(1);
+  s.erase(s.begin());
+  s.erase(s.cbegin());
+}
+
+}  // namespace base
diff --git a/base/containers/flat_tree_unittest.cc b/base/containers/flat_tree_unittest.cc
new file mode 100644
index 0000000..5b788d5
--- /dev/null
+++ b/base/containers/flat_tree_unittest.cc
@@ -0,0 +1,1385 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/containers/flat_tree.h"
+
+// Following tests are ported and extended tests from libcpp for std::set.
+// They can be found here:
+// https://github.com/llvm-mirror/libcxx/tree/master/test/std/containers/associative/set
+//
+// Not ported tests:
+// * No tests with PrivateConstructor and std::less<> changed to std::less<T>
+//   These tests have to do with C++14 std::less<>
+//   http://en.cppreference.com/w/cpp/utility/functional/less_void
+//   and add support for templated versions of lookup functions.
+//   Because we use same implementation, we figured that it's OK just to check
+//   compilation and this is what we do in flat_set_unittest/flat_map_unittest.
+// * No tests for max_size()
+//   Has to do with allocator support.
+// * No tests with DefaultOnly.
+//   Standard containers allocate each element in the separate node on the heap
+//   and then manipulate these nodes. Flat containers store their elements in
+//   contiguous memory and move them around, type is required to be movable.
+// * No tests for N3644.
+//   This proposal suggests that all default constructed iterators compare
+//   equal. Currently we use std::vector iterators and they don't implement
+//   this.
+// * No tests with min_allocator and no tests counting allocations.
+//   Flat sets currently don't support allocators.
+
+#include <forward_list>
+#include <functional>
+#include <iterator>
+#include <list>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/template_util.h"
+#include "base/test/move_only_int.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace internal {
+
+namespace {
+
+template <class It>
+class InputIterator {
+ public:
+  using iterator_category = std::input_iterator_tag;
+  using value_type = typename std::iterator_traits<It>::value_type;
+  using difference_type = typename std::iterator_traits<It>::difference_type;
+  using pointer = It;
+  using reference = typename std::iterator_traits<It>::reference;
+
+  InputIterator() : it_() {}
+  explicit InputIterator(It it) : it_(it) {}
+
+  reference operator*() const { return *it_; }
+  pointer operator->() const { return it_; }
+
+  InputIterator& operator++() {
+    ++it_;
+    return *this;
+  }
+  InputIterator operator++(int) {
+    InputIterator tmp(*this);
+    ++(*this);
+    return tmp;
+  }
+
+  friend bool operator==(const InputIterator& lhs, const InputIterator& rhs) {
+    return lhs.it_ == rhs.it_;
+  }
+  friend bool operator!=(const InputIterator& lhs, const InputIterator& rhs) {
+    return !(lhs == rhs);
+  }
+
+ private:
+  It it_;
+};
+
+template <typename It>
+InputIterator<It> MakeInputIterator(It it) {
+  return InputIterator<It>(it);
+}
+
+class Emplaceable {
+ public:
+  Emplaceable() : Emplaceable(0, 0.0) {}
+  Emplaceable(int i, double d) : int_(i), double_(d) {}
+  Emplaceable(Emplaceable&& other) : int_(other.int_), double_(other.double_) {
+    other.int_ = 0;
+    other.double_ = 0.0;
+  }
+
+  Emplaceable& operator=(Emplaceable&& other) {
+    int_ = other.int_;
+    other.int_ = 0;
+    double_ = other.double_;
+    other.double_ = 0.0;
+    return *this;
+  }
+
+  friend bool operator==(const Emplaceable& lhs, const Emplaceable& rhs) {
+    return std::tie(lhs.int_, lhs.double_) == std::tie(rhs.int_, rhs.double_);
+  }
+
+  friend bool operator<(const Emplaceable& lhs, const Emplaceable& rhs) {
+    return std::tie(lhs.int_, lhs.double_) < std::tie(rhs.int_, rhs.double_);
+  }
+
+ private:
+  int int_;
+  double double_;
+
+  DISALLOW_COPY_AND_ASSIGN(Emplaceable);
+};
+
+struct TemplateConstructor {
+  template <typename T>
+  TemplateConstructor(const T&) {}
+
+  friend bool operator<(const TemplateConstructor&,
+                        const TemplateConstructor&) {
+    return false;
+  }
+};
+
+class NonDefaultConstructibleCompare {
+ public:
+  explicit NonDefaultConstructibleCompare(int) {}
+
+  template <typename T>
+  bool operator()(const T& lhs, const T& rhs) const {
+    return std::less<T>()(lhs, rhs);
+  }
+};
+
+template <class PairType>
+struct LessByFirst {
+  bool operator()(const PairType& lhs, const PairType& rhs) const {
+    return lhs.first < rhs.first;
+  }
+};
+
+// Common test trees.
+using IntTree =
+    flat_tree<int, int, GetKeyFromValueIdentity<int>, std::less<int>>;
+using IntPair = std::pair<int, int>;
+using IntPairTree = flat_tree<IntPair,
+                              IntPair,
+                              GetKeyFromValueIdentity<IntPair>,
+                              LessByFirst<IntPair>>;
+using MoveOnlyTree = flat_tree<MoveOnlyInt,
+                               MoveOnlyInt,
+                               GetKeyFromValueIdentity<MoveOnlyInt>,
+                               std::less<MoveOnlyInt>>;
+using EmplaceableTree = flat_tree<Emplaceable,
+                                  Emplaceable,
+                                  GetKeyFromValueIdentity<Emplaceable>,
+                                  std::less<Emplaceable>>;
+using ReversedTree =
+    flat_tree<int, int, GetKeyFromValueIdentity<int>, std::greater<int>>;
+
+using TreeWithStrangeCompare = flat_tree<int,
+                                         int,
+                                         GetKeyFromValueIdentity<int>,
+                                         NonDefaultConstructibleCompare>;
+
+using ::testing::ElementsAre;
+
+}  // namespace
+
+TEST(FlatTree, IsMultipass) {
+  static_assert(!is_multipass<std::istream_iterator<int>>(),
+                "InputIterator is not multipass");
+  static_assert(!is_multipass<std::ostream_iterator<int>>(),
+                "OutputIterator is not multipass");
+
+  static_assert(is_multipass<std::forward_list<int>::iterator>(),
+                "ForwardIterator is multipass");
+  static_assert(is_multipass<std::list<int>::iterator>(),
+                "BidirectionalIterator is multipass");
+  static_assert(is_multipass<std::vector<int>::iterator>(),
+                "RandomAccessIterator is multipass");
+}
+
+TEST(FlatTree, LastUnique) {
+  using Pair = std::pair<int, int>;
+  using Vect = std::vector<Pair>;
+
+  auto cmp = [](const Pair& lhs, const Pair& rhs) {
+    return lhs.first == rhs.first;
+  };
+
+  // Empty case.
+  Vect empty;
+  EXPECT_EQ(empty.end(), LastUnique(empty.begin(), empty.end(), cmp));
+
+  // Single element.
+  Vect one;
+  one.push_back(Pair(1, 1));
+  EXPECT_EQ(one.end(), LastUnique(one.begin(), one.end(), cmp));
+  ASSERT_EQ(1u, one.size());
+  EXPECT_THAT(one, ElementsAre(Pair(1, 1)));
+
+  // Two elements, already unique.
+  Vect two_u;
+  two_u.push_back(Pair(1, 1));
+  two_u.push_back(Pair(2, 2));
+  EXPECT_EQ(two_u.end(), LastUnique(two_u.begin(), two_u.end(), cmp));
+  EXPECT_THAT(two_u, ElementsAre(Pair(1, 1), Pair(2, 2)));
+
+  // Two elements, dupes.
+  Vect two_d;
+  two_d.push_back(Pair(1, 1));
+  two_d.push_back(Pair(1, 2));
+  auto last = LastUnique(two_d.begin(), two_d.end(), cmp);
+  EXPECT_EQ(two_d.begin() + 1, last);
+  two_d.erase(last, two_d.end());
+  EXPECT_THAT(two_d, ElementsAre(Pair(1, 2)));
+
+  // Non-dupes, dupes, non-dupes.
+  Vect ndn;
+  ndn.push_back(Pair(1, 1));
+  ndn.push_back(Pair(2, 1));
+  ndn.push_back(Pair(2, 2));
+  ndn.push_back(Pair(2, 3));
+  ndn.push_back(Pair(3, 1));
+  last = LastUnique(ndn.begin(), ndn.end(), cmp);
+  EXPECT_EQ(ndn.begin() + 3, last);
+  ndn.erase(last, ndn.end());
+  EXPECT_THAT(ndn, ElementsAre(Pair(1, 1), Pair(2, 3), Pair(3, 1)));
+
+  // Dupes, non-dupes, dupes.
+  Vect dnd;
+  dnd.push_back(Pair(1, 1));
+  dnd.push_back(Pair(1, 2));
+  dnd.push_back(Pair(1, 3));
+  dnd.push_back(Pair(2, 1));
+  dnd.push_back(Pair(3, 1));
+  dnd.push_back(Pair(3, 2));
+  dnd.push_back(Pair(3, 3));
+  last = LastUnique(dnd.begin(), dnd.end(), cmp);
+  EXPECT_EQ(dnd.begin() + 3, last);
+  dnd.erase(last, dnd.end());
+  EXPECT_THAT(dnd, ElementsAre(Pair(1, 3), Pair(2, 1), Pair(3, 3)));
+}
+
+// ----------------------------------------------------------------------------
+// Class.
+
+// Check that flat_tree and its iterators can be instantiated with an
+// incomplete type.
+
+TEST(FlatTree, IncompleteType) {
+  struct A {
+    using Tree = flat_tree<A, A, GetKeyFromValueIdentity<A>, std::less<A>>;
+    int data;
+    Tree set_with_incomplete_type;
+    Tree::iterator it;
+    Tree::const_iterator cit;
+
+    // We do not declare operator< because clang complains that it's unused.
+  };
+
+  A a;
+}
+
+TEST(FlatTree, Stability) {
+  using Pair = std::pair<int, int>;
+
+  using Tree =
+      flat_tree<Pair, Pair, GetKeyFromValueIdentity<Pair>, LessByFirst<Pair>>;
+
+  // Constructors are stable.
+  Tree cont({{0, 0}, {1, 0}, {0, 1}, {2, 0}, {0, 2}, {1, 1}});
+
+  auto AllOfSecondsAreZero = [&cont] {
+    return std::all_of(cont.begin(), cont.end(),
+                       [](const Pair& elem) { return elem.second == 0; });
+  };
+
+  EXPECT_TRUE(AllOfSecondsAreZero()) << "constructor should be stable";
+
+  // Should not replace existing.
+  cont.insert(Pair(0, 2));
+  cont.insert(Pair(1, 2));
+  cont.insert(Pair(2, 2));
+
+  EXPECT_TRUE(AllOfSecondsAreZero()) << "insert should be stable";
+
+  cont.insert(Pair(3, 0));
+  cont.insert(Pair(3, 2));
+
+  EXPECT_TRUE(AllOfSecondsAreZero()) << "insert should be stable";
+}
+
+// ----------------------------------------------------------------------------
+// Types.
+
+// key_type
+// key_compare
+// value_type
+// value_compare
+// pointer
+// const_pointer
+// reference
+// const_reference
+// size_type
+// difference_type
+// iterator
+// const_iterator
+// reverse_iterator
+// const_reverse_iterator
+
+TEST(FlatTree, Types) {
+  // These are guaranteed to be portable.
+  static_assert((std::is_same<int, IntTree::key_type>::value), "");
+  static_assert((std::is_same<int, IntTree::value_type>::value), "");
+  static_assert((std::is_same<std::less<int>, IntTree::key_compare>::value),
+                "");
+  static_assert((std::is_same<int&, IntTree::reference>::value), "");
+  static_assert((std::is_same<const int&, IntTree::const_reference>::value),
+                "");
+  static_assert((std::is_same<int*, IntTree::pointer>::value), "");
+  static_assert((std::is_same<const int*, IntTree::const_pointer>::value), "");
+}
+
+// ----------------------------------------------------------------------------
+// Lifetime.
+
+// flat_tree()
+// flat_tree(const Compare& comp)
+
+TEST(FlatTree, DefaultConstructor) {
+  {
+    IntTree cont;
+    EXPECT_THAT(cont, ElementsAre());
+  }
+
+  {
+    TreeWithStrangeCompare cont(NonDefaultConstructibleCompare(0));
+    EXPECT_THAT(cont, ElementsAre());
+  }
+}
+
+// flat_tree(InputIterator first,
+//           InputIterator last,
+//           FlatContainerDupes dupe_handling,
+//           const Compare& comp = Compare())
+
+TEST(FlatTree, RangeConstructor) {
+  {
+    IntPair input_vals[] = {{1, 1}, {1, 2}, {2, 1}, {2, 2}, {1, 3},
+                            {2, 3}, {3, 1}, {3, 2}, {3, 3}};
+
+    IntPairTree first_of(MakeInputIterator(std::begin(input_vals)),
+                         MakeInputIterator(std::end(input_vals)));
+    EXPECT_THAT(first_of,
+                ElementsAre(IntPair(1, 1), IntPair(2, 1), IntPair(3, 1)));
+
+    IntPairTree last_of(MakeInputIterator(std::begin(input_vals)),
+                        MakeInputIterator(std::end(input_vals)),
+                        KEEP_LAST_OF_DUPES);
+    EXPECT_THAT(last_of,
+                ElementsAre(IntPair(1, 3), IntPair(2, 3), IntPair(3, 3)));
+  }
+  {
+    TreeWithStrangeCompare::value_type input_vals[] = {1, 1, 1, 2, 2,
+                                                       2, 3, 3, 3};
+
+    TreeWithStrangeCompare cont(MakeInputIterator(std::begin(input_vals)),
+                                MakeInputIterator(std::end(input_vals)),
+                                KEEP_FIRST_OF_DUPES,
+                                NonDefaultConstructibleCompare(0));
+    EXPECT_THAT(cont, ElementsAre(1, 2, 3));
+  }
+}
+
+// flat_tree(const flat_tree& x)
+
+TEST(FlatTree, CopyConstructor) {
+  IntTree original({1, 2, 3, 4});
+  IntTree copied(original);
+
+  EXPECT_THAT(copied, ElementsAre(1, 2, 3, 4));
+
+  EXPECT_THAT(copied, ElementsAre(1, 2, 3, 4));
+  EXPECT_THAT(original, ElementsAre(1, 2, 3, 4));
+  EXPECT_EQ(original, copied);
+}
+
+// flat_tree(flat_tree&& x)
+
+TEST(FlatTree, MoveConstructor) {
+  int input_range[] = {1, 2, 3, 4};
+
+  MoveOnlyTree original(std::begin(input_range), std::end(input_range));
+  MoveOnlyTree moved(std::move(original));
+
+  EXPECT_EQ(1U, moved.count(MoveOnlyInt(1)));
+  EXPECT_EQ(1U, moved.count(MoveOnlyInt(2)));
+  EXPECT_EQ(1U, moved.count(MoveOnlyInt(3)));
+  EXPECT_EQ(1U, moved.count(MoveOnlyInt(4)));
+}
+
+// flat_tree(std::vector<value_type>, FlatContainerDupes dupe_handling)
+
+TEST(FlatTree, VectorConstructor) {
+  using Pair = std::pair<int, MoveOnlyInt>;
+
+  // Construct an unsorted vector with a duplicate item in it. Sorted by the
+  // first item, the second allows us to test for stability. Using a move
+  // only type to ensure the vector is not copied.
+  std::vector<Pair> storage;
+  storage.push_back(Pair(2, MoveOnlyInt(0)));
+  storage.push_back(Pair(1, MoveOnlyInt(0)));
+  storage.push_back(Pair(2, MoveOnlyInt(1)));
+
+  using Tree =
+      flat_tree<Pair, Pair, GetKeyFromValueIdentity<Pair>, LessByFirst<Pair>>;
+  Tree tree(std::move(storage));
+
+  // The list should be two items long, with only the first "2" saved.
+  ASSERT_EQ(2u, tree.size());
+  const Pair& zeroth = *tree.begin();
+  ASSERT_EQ(1, zeroth.first);
+  ASSERT_EQ(0, zeroth.second.data());
+
+  const Pair& first = *(tree.begin() + 1);
+  ASSERT_EQ(2, first.first);
+  ASSERT_EQ(0, first.second.data());
+
+  // Test KEEP_LAST_OF_DUPES with a simple vector constructor.
+  std::vector<IntPair> int_storage{{1, 1}, {1, 2}, {2, 1}};
+  IntPairTree int_tree(std::move(int_storage), KEEP_LAST_OF_DUPES);
+  EXPECT_THAT(int_tree, ElementsAre(IntPair(1, 2), IntPair(2, 1)));
+}
+
+// flat_tree(std::initializer_list<value_type> ilist,
+//           FlatContainerDupes dupe_handling,
+//           const Compare& comp = Compare())
+
+TEST(FlatTree, InitializerListConstructor) {
+  {
+    IntTree cont({1, 2, 3, 4, 5, 6, 10, 8});
+    EXPECT_THAT(cont, ElementsAre(1, 2, 3, 4, 5, 6, 8, 10));
+  }
+  {
+    IntTree cont({1, 2, 3, 4, 5, 6, 10, 8});
+    EXPECT_THAT(cont, ElementsAre(1, 2, 3, 4, 5, 6, 8, 10));
+  }
+  {
+    TreeWithStrangeCompare cont({1, 2, 3, 4, 5, 6, 10, 8}, KEEP_FIRST_OF_DUPES,
+                                NonDefaultConstructibleCompare(0));
+    EXPECT_THAT(cont, ElementsAre(1, 2, 3, 4, 5, 6, 8, 10));
+  }
+  {
+    IntPairTree first_of({{1, 1}, {2, 1}, {1, 2}});
+    EXPECT_THAT(first_of, ElementsAre(IntPair(1, 1), IntPair(2, 1)));
+  }
+  {
+    IntPairTree last_of({{1, 1}, {2, 1}, {1, 2}}, KEEP_LAST_OF_DUPES);
+    EXPECT_THAT(last_of, ElementsAre(IntPair(1, 2), IntPair(2, 1)));
+  }
+}
+
+// ----------------------------------------------------------------------------
+// Assignments.
+
+// flat_tree& operator=(const flat_tree&)
+
+TEST(FlatTree, CopyAssignable) {
+  IntTree original({1, 2, 3, 4});
+  IntTree copied;
+  copied = original;
+
+  EXPECT_THAT(copied, ElementsAre(1, 2, 3, 4));
+  EXPECT_THAT(original, ElementsAre(1, 2, 3, 4));
+  EXPECT_EQ(original, copied);
+}
+
+// flat_tree& operator=(flat_tree&&)
+
+TEST(FlatTree, MoveAssignable) {
+  int input_range[] = {1, 2, 3, 4};
+
+  MoveOnlyTree original(std::begin(input_range), std::end(input_range));
+  MoveOnlyTree moved;
+  moved = std::move(original);
+
+  EXPECT_EQ(1U, moved.count(MoveOnlyInt(1)));
+  EXPECT_EQ(1U, moved.count(MoveOnlyInt(2)));
+  EXPECT_EQ(1U, moved.count(MoveOnlyInt(3)));
+  EXPECT_EQ(1U, moved.count(MoveOnlyInt(4)));
+}
+
+// flat_tree& operator=(std::initializer_list<value_type> ilist)
+
+TEST(FlatTree, InitializerListAssignable) {
+  IntTree cont({0});
+  cont = {1, 2, 3, 4, 5, 6, 10, 8};
+
+  EXPECT_EQ(0U, cont.count(0));
+  EXPECT_THAT(cont, ElementsAre(1, 2, 3, 4, 5, 6, 8, 10));
+}
+
+// --------------------------------------------------------------------------
+// Memory management.
+
+// void reserve(size_type new_capacity)
+
+TEST(FlatTree, Reserve) {
+  IntTree cont({1, 2, 3});
+
+  cont.reserve(5);
+  EXPECT_LE(5U, cont.capacity());
+}
+
+// size_type capacity() const
+
+TEST(FlatTree, Capacity) {
+  IntTree cont({1, 2, 3});
+
+  EXPECT_LE(cont.size(), cont.capacity());
+  cont.reserve(5);
+  EXPECT_LE(cont.size(), cont.capacity());
+}
+
+// void shrink_to_fit()
+
+TEST(FlatTree, ShrinkToFit) {
+  IntTree cont({1, 2, 3});
+
+  IntTree::size_type capacity_before = cont.capacity();
+  cont.shrink_to_fit();
+  EXPECT_GE(capacity_before, cont.capacity());
+}
+
+// ----------------------------------------------------------------------------
+// Size management.
+
+// void clear()
+
+TEST(FlatTree, Clear) {
+  IntTree cont({1, 2, 3, 4, 5, 6, 7, 8});
+  cont.clear();
+  EXPECT_THAT(cont, ElementsAre());
+}
+
+// size_type size() const
+
+TEST(FlatTree, Size) {
+  IntTree cont;
+
+  EXPECT_EQ(0U, cont.size());
+  cont.insert(2);
+  EXPECT_EQ(1U, cont.size());
+  cont.insert(1);
+  EXPECT_EQ(2U, cont.size());
+  cont.insert(3);
+  EXPECT_EQ(3U, cont.size());
+  cont.erase(cont.begin());
+  EXPECT_EQ(2U, cont.size());
+  cont.erase(cont.begin());
+  EXPECT_EQ(1U, cont.size());
+  cont.erase(cont.begin());
+  EXPECT_EQ(0U, cont.size());
+}
+
+// bool empty() const
+
+TEST(FlatTree, Empty) {
+  IntTree cont;
+
+  EXPECT_TRUE(cont.empty());
+  cont.insert(1);
+  EXPECT_FALSE(cont.empty());
+  cont.clear();
+  EXPECT_TRUE(cont.empty());
+}
+
+// ----------------------------------------------------------------------------
+// Iterators.
+
+// iterator begin()
+// const_iterator begin() const
+// iterator end()
+// const_iterator end() const
+//
+// reverse_iterator rbegin()
+// const_reverse_iterator rbegin() const
+// reverse_iterator rend()
+// const_reverse_iterator rend() const
+//
+// const_iterator cbegin() const
+// const_iterator cend() const
+// const_reverse_iterator crbegin() const
+// const_reverse_iterator crend() const
+
+TEST(FlatTree, Iterators) {
+  IntTree cont({1, 2, 3, 4, 5, 6, 7, 8});
+
+  auto size = static_cast<IntTree::difference_type>(cont.size());
+
+  EXPECT_EQ(size, std::distance(cont.begin(), cont.end()));
+  EXPECT_EQ(size, std::distance(cont.cbegin(), cont.cend()));
+  EXPECT_EQ(size, std::distance(cont.rbegin(), cont.rend()));
+  EXPECT_EQ(size, std::distance(cont.crbegin(), cont.crend()));
+
+  {
+    IntTree::iterator it = cont.begin();
+    IntTree::const_iterator c_it = cont.cbegin();
+    EXPECT_EQ(it, c_it);
+    for (int j = 1; it != cont.end(); ++it, ++c_it, ++j) {
+      EXPECT_EQ(j, *it);
+      EXPECT_EQ(j, *c_it);
+    }
+  }
+  {
+    IntTree::reverse_iterator rit = cont.rbegin();
+    IntTree::const_reverse_iterator c_rit = cont.crbegin();
+    EXPECT_EQ(rit, c_rit);
+    for (int j = static_cast<int>(size); rit != cont.rend();
+         ++rit, ++c_rit, --j) {
+      EXPECT_EQ(j, *rit);
+      EXPECT_EQ(j, *c_rit);
+    }
+  }
+}
+
+// ----------------------------------------------------------------------------
+// Insert operations.
+
+// pair<iterator, bool> insert(const value_type& val)
+
+TEST(FlatTree, InsertLValue) {
+  IntTree cont;
+
+  int value = 2;
+  std::pair<IntTree::iterator, bool> result = cont.insert(value);
+  EXPECT_TRUE(result.second);
+  EXPECT_EQ(cont.begin(), result.first);
+  EXPECT_EQ(1U, cont.size());
+  EXPECT_EQ(2, *result.first);
+
+  value = 1;
+  result = cont.insert(value);
+  EXPECT_TRUE(result.second);
+  EXPECT_EQ(cont.begin(), result.first);
+  EXPECT_EQ(2U, cont.size());
+  EXPECT_EQ(1, *result.first);
+
+  value = 3;
+  result = cont.insert(value);
+  EXPECT_TRUE(result.second);
+  EXPECT_EQ(std::prev(cont.end()), result.first);
+  EXPECT_EQ(3U, cont.size());
+  EXPECT_EQ(3, *result.first);
+
+  value = 3;
+  result = cont.insert(value);
+  EXPECT_FALSE(result.second);
+  EXPECT_EQ(std::prev(cont.end()), result.first);
+  EXPECT_EQ(3U, cont.size());
+  EXPECT_EQ(3, *result.first);
+}
+
+// pair<iterator, bool> insert(value_type&& val)
+
+TEST(FlatTree, InsertRValue) {
+  MoveOnlyTree cont;
+
+  std::pair<MoveOnlyTree::iterator, bool> result = cont.insert(MoveOnlyInt(2));
+  EXPECT_TRUE(result.second);
+  EXPECT_EQ(cont.begin(), result.first);
+  EXPECT_EQ(1U, cont.size());
+  EXPECT_EQ(2, result.first->data());
+
+  result = cont.insert(MoveOnlyInt(1));
+  EXPECT_TRUE(result.second);
+  EXPECT_EQ(cont.begin(), result.first);
+  EXPECT_EQ(2U, cont.size());
+  EXPECT_EQ(1, result.first->data());
+
+  result = cont.insert(MoveOnlyInt(3));
+  EXPECT_TRUE(result.second);
+  EXPECT_EQ(std::prev(cont.end()), result.first);
+  EXPECT_EQ(3U, cont.size());
+  EXPECT_EQ(3, result.first->data());
+
+  result = cont.insert(MoveOnlyInt(3));
+  EXPECT_FALSE(result.second);
+  EXPECT_EQ(std::prev(cont.end()), result.first);
+  EXPECT_EQ(3U, cont.size());
+  EXPECT_EQ(3, result.first->data());
+}
+
+// iterator insert(const_iterator position_hint, const value_type& val)
+
+TEST(FlatTree, InsertPositionLValue) {
+  IntTree cont;
+
+  IntTree::iterator result = cont.insert(cont.cend(), 2);
+  EXPECT_EQ(cont.begin(), result);
+  EXPECT_EQ(1U, cont.size());
+  EXPECT_EQ(2, *result);
+
+  result = cont.insert(cont.cend(), 1);
+  EXPECT_EQ(cont.begin(), result);
+  EXPECT_EQ(2U, cont.size());
+  EXPECT_EQ(1, *result);
+
+  result = cont.insert(cont.cend(), 3);
+  EXPECT_EQ(std::prev(cont.end()), result);
+  EXPECT_EQ(3U, cont.size());
+  EXPECT_EQ(3, *result);
+
+  result = cont.insert(cont.cend(), 3);
+  EXPECT_EQ(std::prev(cont.end()), result);
+  EXPECT_EQ(3U, cont.size());
+  EXPECT_EQ(3, *result);
+}
+
+// iterator insert(const_iterator position_hint, value_type&& val)
+
+TEST(FlatTree, InsertPositionRValue) {
+  MoveOnlyTree cont;
+
+  MoveOnlyTree::iterator result = cont.insert(cont.cend(), MoveOnlyInt(2));
+  EXPECT_EQ(cont.begin(), result);
+  EXPECT_EQ(1U, cont.size());
+  EXPECT_EQ(2, result->data());
+
+  result = cont.insert(cont.cend(), MoveOnlyInt(1));
+  EXPECT_EQ(cont.begin(), result);
+  EXPECT_EQ(2U, cont.size());
+  EXPECT_EQ(1, result->data());
+
+  result = cont.insert(cont.cend(), MoveOnlyInt(3));
+  EXPECT_EQ(std::prev(cont.end()), result);
+  EXPECT_EQ(3U, cont.size());
+  EXPECT_EQ(3, result->data());
+
+  result = cont.insert(cont.cend(), MoveOnlyInt(3));
+  EXPECT_EQ(std::prev(cont.end()), result);
+  EXPECT_EQ(3U, cont.size());
+  EXPECT_EQ(3, result->data());
+}
+
+// template <class InputIterator>
+//   void insert(InputIterator first, InputIterator last);
+
+TEST(FlatTree, InsertIterIter) {
+  struct GetKeyFromIntIntPair {
+    const int& operator()(const std::pair<int, int>& p) const {
+      return p.first;
+    }
+  };
+
+  using IntIntMap =
+      flat_tree<int, IntPair, GetKeyFromIntIntPair, std::less<int>>;
+
+  {
+    IntIntMap cont;
+    IntPair int_pairs[] = {{3, 1}, {1, 1}, {4, 1}, {2, 1}};
+    cont.insert(std::begin(int_pairs), std::end(int_pairs));
+    EXPECT_THAT(cont, ElementsAre(IntPair(1, 1), IntPair(2, 1), IntPair(3, 1),
+                                  IntPair(4, 1)));
+  }
+
+  {
+    IntIntMap cont({{1, 1}, {2, 1}, {3, 1}, {4, 1}});
+    std::vector<IntPair> int_pairs;
+    cont.insert(std::begin(int_pairs), std::end(int_pairs));
+    EXPECT_THAT(cont, ElementsAre(IntPair(1, 1), IntPair(2, 1), IntPair(3, 1),
+                                  IntPair(4, 1)));
+  }
+
+  {
+    IntIntMap cont({{1, 1}, {2, 1}, {3, 1}, {4, 1}});
+    IntPair int_pairs[] = {{1, 1}};
+    cont.insert(std::begin(int_pairs), std::end(int_pairs));
+    EXPECT_THAT(cont, ElementsAre(IntPair(1, 1), IntPair(2, 1), IntPair(3, 1),
+                                  IntPair(4, 1)));
+  }
+
+  {
+    IntIntMap cont({{1, 1}, {2, 1}, {3, 1}, {4, 1}});
+    IntPair int_pairs[] = {{1, 2}};
+    cont.insert(std::begin(int_pairs), std::end(int_pairs), KEEP_LAST_OF_DUPES);
+    EXPECT_THAT(cont, ElementsAre(IntPair(1, 2), IntPair(2, 1), IntPair(3, 1),
+                                  IntPair(4, 1)));
+  }
+
+  {
+    IntIntMap cont({{1, 1}, {2, 1}, {3, 1}, {4, 1}});
+    IntPair int_pairs[] = {{5, 1}};
+    cont.insert(std::begin(int_pairs), std::end(int_pairs));
+    EXPECT_THAT(cont, ElementsAre(IntPair(1, 1), IntPair(2, 1), IntPair(3, 1),
+                                  IntPair(4, 1), IntPair(5, 1)));
+  }
+
+  {
+    IntIntMap cont({{1, 1}, {2, 1}, {3, 1}, {4, 1}});
+    IntPair int_pairs[] = {{5, 1}};
+    cont.insert(std::begin(int_pairs), std::end(int_pairs), KEEP_LAST_OF_DUPES);
+    EXPECT_THAT(cont, ElementsAre(IntPair(1, 1), IntPair(2, 1), IntPair(3, 1),
+                                  IntPair(4, 1), IntPair(5, 1)));
+  }
+
+  {
+    IntIntMap cont({{1, 1}, {2, 1}, {3, 1}, {4, 1}});
+    IntPair int_pairs[] = {{3, 2}, {1, 2}, {4, 2}, {2, 2}};
+    cont.insert(std::begin(int_pairs), std::end(int_pairs));
+    EXPECT_THAT(cont, ElementsAre(IntPair(1, 1), IntPair(2, 1), IntPair(3, 1),
+                                  IntPair(4, 1)));
+  }
+
+  {
+    IntIntMap cont({{1, 1}, {2, 1}, {3, 1}, {4, 1}});
+    IntPair int_pairs[] = {{3, 2}, {1, 2}, {4, 2}, {2, 2}};
+    cont.insert(std::begin(int_pairs), std::end(int_pairs), KEEP_LAST_OF_DUPES);
+    EXPECT_THAT(cont, ElementsAre(IntPair(1, 2), IntPair(2, 2), IntPair(3, 2),
+                                  IntPair(4, 2)));
+  }
+
+  {
+    IntIntMap cont({{1, 1}, {2, 1}, {3, 1}, {4, 1}});
+    IntPair int_pairs[] = {{3, 2}, {1, 2}, {4, 2}, {2, 2}, {7, 2}, {6, 2},
+                           {8, 2}, {5, 2}, {5, 3}, {6, 3}, {7, 3}, {8, 3}};
+    cont.insert(std::begin(int_pairs), std::end(int_pairs));
+    EXPECT_THAT(cont, ElementsAre(IntPair(1, 1), IntPair(2, 1), IntPair(3, 1),
+                                  IntPair(4, 1), IntPair(5, 2), IntPair(6, 2),
+                                  IntPair(7, 2), IntPair(8, 2)));
+  }
+
+  {
+    IntIntMap cont({{1, 1}, {2, 1}, {3, 1}, {4, 1}});
+    IntPair int_pairs[] = {{3, 2}, {1, 2}, {4, 2}, {2, 2}, {7, 2}, {6, 2},
+                           {8, 2}, {5, 2}, {5, 3}, {6, 3}, {7, 3}, {8, 3}};
+    cont.insert(std::begin(int_pairs), std::end(int_pairs), KEEP_LAST_OF_DUPES);
+    EXPECT_THAT(cont, ElementsAre(IntPair(1, 2), IntPair(2, 2), IntPair(3, 2),
+                                  IntPair(4, 2), IntPair(5, 3), IntPair(6, 3),
+                                  IntPair(7, 3), IntPair(8, 3)));
+  }
+}
+
+// template <class... Args>
+// pair<iterator, bool> emplace(Args&&... args)
+
+TEST(FlatTree, Emplace) {
+  {
+    EmplaceableTree cont;
+
+    std::pair<EmplaceableTree::iterator, bool> result = cont.emplace();
+    EXPECT_TRUE(result.second);
+    EXPECT_EQ(cont.begin(), result.first);
+    EXPECT_EQ(1U, cont.size());
+    EXPECT_EQ(Emplaceable(), *cont.begin());
+
+    result = cont.emplace(2, 3.5);
+    EXPECT_TRUE(result.second);
+    EXPECT_EQ(std::next(cont.begin()), result.first);
+    EXPECT_EQ(2U, cont.size());
+    EXPECT_EQ(Emplaceable(2, 3.5), *result.first);
+
+    result = cont.emplace(2, 3.5);
+    EXPECT_FALSE(result.second);
+    EXPECT_EQ(std::next(cont.begin()), result.first);
+    EXPECT_EQ(2U, cont.size());
+    EXPECT_EQ(Emplaceable(2, 3.5), *result.first);
+  }
+  {
+    IntTree cont;
+
+    std::pair<IntTree::iterator, bool> result = cont.emplace(2);
+    EXPECT_TRUE(result.second);
+    EXPECT_EQ(cont.begin(), result.first);
+    EXPECT_EQ(1U, cont.size());
+    EXPECT_EQ(2, *result.first);
+  }
+}
+
+// template <class... Args>
+// iterator emplace_hint(const_iterator position_hint, Args&&... args)
+
+TEST(FlatTree, EmplacePosition) {
+  {
+    EmplaceableTree cont;
+
+    EmplaceableTree::iterator result = cont.emplace_hint(cont.cend());
+    EXPECT_EQ(cont.begin(), result);
+    EXPECT_EQ(1U, cont.size());
+    EXPECT_EQ(Emplaceable(), *cont.begin());
+
+    result = cont.emplace_hint(cont.cend(), 2, 3.5);
+    EXPECT_EQ(std::next(cont.begin()), result);
+    EXPECT_EQ(2U, cont.size());
+    EXPECT_EQ(Emplaceable(2, 3.5), *result);
+
+    result = cont.emplace_hint(cont.cbegin(), 2, 3.5);
+    EXPECT_EQ(std::next(cont.begin()), result);
+    EXPECT_EQ(2U, cont.size());
+    EXPECT_EQ(Emplaceable(2, 3.5), *result);
+  }
+  {
+    IntTree cont;
+
+    IntTree::iterator result = cont.emplace_hint(cont.cend(), 2);
+    EXPECT_EQ(cont.begin(), result);
+    EXPECT_EQ(1U, cont.size());
+    EXPECT_EQ(2, *result);
+  }
+}
+
+// ----------------------------------------------------------------------------
+// Erase operations.
+
+// iterator erase(const_iterator position_hint)
+
+TEST(FlatTree, ErasePosition) {
+  {
+    IntTree cont({1, 2, 3, 4, 5, 6, 7, 8});
+
+    IntTree::iterator it = cont.erase(std::next(cont.cbegin(), 3));
+    EXPECT_EQ(std::next(cont.begin(), 3), it);
+    EXPECT_THAT(cont, ElementsAre(1, 2, 3, 5, 6, 7, 8));
+
+    it = cont.erase(std::next(cont.cbegin(), 0));
+    EXPECT_EQ(cont.begin(), it);
+    EXPECT_THAT(cont, ElementsAre(2, 3, 5, 6, 7, 8));
+
+    it = cont.erase(std::next(cont.cbegin(), 5));
+    EXPECT_EQ(cont.end(), it);
+    EXPECT_THAT(cont, ElementsAre(2, 3, 5, 6, 7));
+
+    it = cont.erase(std::next(cont.cbegin(), 1));
+    EXPECT_EQ(std::next(cont.begin()), it);
+    EXPECT_THAT(cont, ElementsAre(2, 5, 6, 7));
+
+    it = cont.erase(std::next(cont.cbegin(), 2));
+    EXPECT_EQ(std::next(cont.begin(), 2), it);
+    EXPECT_THAT(cont, ElementsAre(2, 5, 7));
+
+    it = cont.erase(std::next(cont.cbegin(), 2));
+    EXPECT_EQ(std::next(cont.begin(), 2), it);
+    EXPECT_THAT(cont, ElementsAre(2, 5));
+
+    it = cont.erase(std::next(cont.cbegin(), 0));
+    EXPECT_EQ(std::next(cont.begin(), 0), it);
+    EXPECT_THAT(cont, ElementsAre(5));
+
+    it = cont.erase(cont.cbegin());
+    EXPECT_EQ(cont.begin(), it);
+    EXPECT_EQ(cont.end(), it);
+  }
+  //  This is LWG #2059.
+  //  There is a potential ambiguity between erase with an iterator and erase
+  //  with a key, if key has a templated constructor.
+  {
+    using T = TemplateConstructor;
+
+    flat_tree<T, T, GetKeyFromValueIdentity<T>, std::less<>> cont;
+    T v(0);
+
+    auto it = cont.find(v);
+    if (it != cont.end())
+      cont.erase(it);
+  }
+}
+
+// iterator erase(const_iterator first, const_iterator last)
+
+TEST(FlatTree, EraseRange) {
+  IntTree cont({1, 2, 3, 4, 5, 6, 7, 8});
+
+  IntTree::iterator it =
+      cont.erase(std::next(cont.cbegin(), 5), std::next(cont.cbegin(), 5));
+  EXPECT_EQ(std::next(cont.begin(), 5), it);
+  EXPECT_THAT(cont, ElementsAre(1, 2, 3, 4, 5, 6, 7, 8));
+
+  it = cont.erase(std::next(cont.cbegin(), 3), std::next(cont.cbegin(), 4));
+  EXPECT_EQ(std::next(cont.begin(), 3), it);
+  EXPECT_THAT(cont, ElementsAre(1, 2, 3, 5, 6, 7, 8));
+
+  it = cont.erase(std::next(cont.cbegin(), 2), std::next(cont.cbegin(), 5));
+  EXPECT_EQ(std::next(cont.begin(), 2), it);
+  EXPECT_THAT(cont, ElementsAre(1, 2, 7, 8));
+
+  it = cont.erase(std::next(cont.cbegin(), 0), std::next(cont.cbegin(), 2));
+  EXPECT_EQ(std::next(cont.begin(), 0), it);
+  EXPECT_THAT(cont, ElementsAre(7, 8));
+
+  it = cont.erase(cont.cbegin(), cont.cend());
+  EXPECT_EQ(cont.begin(), it);
+  EXPECT_EQ(cont.end(), it);
+}
+
+// size_type erase(const key_type& key)
+
+TEST(FlatTree, EraseKey) {
+  IntTree cont({1, 2, 3, 4, 5, 6, 7, 8});
+
+  EXPECT_EQ(0U, cont.erase(9));
+  EXPECT_THAT(cont, ElementsAre(1, 2, 3, 4, 5, 6, 7, 8));
+
+  EXPECT_EQ(1U, cont.erase(4));
+  EXPECT_THAT(cont, ElementsAre(1, 2, 3, 5, 6, 7, 8));
+
+  EXPECT_EQ(1U, cont.erase(1));
+  EXPECT_THAT(cont, ElementsAre(2, 3, 5, 6, 7, 8));
+
+  EXPECT_EQ(1U, cont.erase(8));
+  EXPECT_THAT(cont, ElementsAre(2, 3, 5, 6, 7));
+
+  EXPECT_EQ(1U, cont.erase(3));
+  EXPECT_THAT(cont, ElementsAre(2, 5, 6, 7));
+
+  EXPECT_EQ(1U, cont.erase(6));
+  EXPECT_THAT(cont, ElementsAre(2, 5, 7));
+
+  EXPECT_EQ(1U, cont.erase(7));
+  EXPECT_THAT(cont, ElementsAre(2, 5));
+
+  EXPECT_EQ(1U, cont.erase(2));
+  EXPECT_THAT(cont, ElementsAre(5));
+
+  EXPECT_EQ(1U, cont.erase(5));
+  EXPECT_THAT(cont, ElementsAre());
+}
+
+// ----------------------------------------------------------------------------
+// Comparators.
+
+// key_compare key_comp() const
+
+TEST(FlatTree, KeyComp) {
+  ReversedTree cont({1, 2, 3, 4, 5});
+
+  EXPECT_TRUE(std::is_sorted(cont.begin(), cont.end(), cont.key_comp()));
+  int new_elements[] = {6, 7, 8, 9, 10};
+  std::copy(std::begin(new_elements), std::end(new_elements),
+            std::inserter(cont, cont.end()));
+  EXPECT_TRUE(std::is_sorted(cont.begin(), cont.end(), cont.key_comp()));
+}
+
+// value_compare value_comp() const
+
+TEST(FlatTree, ValueComp) {
+  ReversedTree cont({1, 2, 3, 4, 5});
+
+  EXPECT_TRUE(std::is_sorted(cont.begin(), cont.end(), cont.value_comp()));
+  int new_elements[] = {6, 7, 8, 9, 10};
+  std::copy(std::begin(new_elements), std::end(new_elements),
+            std::inserter(cont, cont.end()));
+  EXPECT_TRUE(std::is_sorted(cont.begin(), cont.end(), cont.value_comp()));
+}
+
+// ----------------------------------------------------------------------------
+// Search operations.
+
+// size_type count(const key_type& key) const
+
+TEST(FlatTree, Count) {
+  const IntTree cont({5, 6, 7, 8, 9, 10, 11, 12});
+
+  EXPECT_EQ(1U, cont.count(5));
+  EXPECT_EQ(1U, cont.count(6));
+  EXPECT_EQ(1U, cont.count(7));
+  EXPECT_EQ(1U, cont.count(8));
+  EXPECT_EQ(1U, cont.count(9));
+  EXPECT_EQ(1U, cont.count(10));
+  EXPECT_EQ(1U, cont.count(11));
+  EXPECT_EQ(1U, cont.count(12));
+  EXPECT_EQ(0U, cont.count(4));
+}
+
+// iterator find(const key_type& key)
+// const_iterator find(const key_type& key) const
+
+TEST(FlatTree, Find) {
+  {
+    IntTree cont({5, 6, 7, 8, 9, 10, 11, 12});
+
+    EXPECT_EQ(cont.begin(), cont.find(5));
+    EXPECT_EQ(std::next(cont.begin()), cont.find(6));
+    EXPECT_EQ(std::next(cont.begin(), 2), cont.find(7));
+    EXPECT_EQ(std::next(cont.begin(), 3), cont.find(8));
+    EXPECT_EQ(std::next(cont.begin(), 4), cont.find(9));
+    EXPECT_EQ(std::next(cont.begin(), 5), cont.find(10));
+    EXPECT_EQ(std::next(cont.begin(), 6), cont.find(11));
+    EXPECT_EQ(std::next(cont.begin(), 7), cont.find(12));
+    EXPECT_EQ(std::next(cont.begin(), 8), cont.find(4));
+  }
+  {
+    const IntTree cont({5, 6, 7, 8, 9, 10, 11, 12});
+
+    EXPECT_EQ(cont.begin(), cont.find(5));
+    EXPECT_EQ(std::next(cont.begin()), cont.find(6));
+    EXPECT_EQ(std::next(cont.begin(), 2), cont.find(7));
+    EXPECT_EQ(std::next(cont.begin(), 3), cont.find(8));
+    EXPECT_EQ(std::next(cont.begin(), 4), cont.find(9));
+    EXPECT_EQ(std::next(cont.begin(), 5), cont.find(10));
+    EXPECT_EQ(std::next(cont.begin(), 6), cont.find(11));
+    EXPECT_EQ(std::next(cont.begin(), 7), cont.find(12));
+    EXPECT_EQ(std::next(cont.begin(), 8), cont.find(4));
+  }
+}
+
+// pair<iterator, iterator> equal_range(const key_type& key)
+// pair<const_iterator, const_iterator> equal_range(const key_type& key) const
+
+TEST(FlatTree, EqualRange) {
+  {
+    IntTree cont({5, 7, 9, 11, 13, 15, 17, 19});
+
+    std::pair<IntTree::iterator, IntTree::iterator> result =
+        cont.equal_range(5);
+    EXPECT_EQ(std::next(cont.begin(), 0), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 1), result.second);
+    result = cont.equal_range(7);
+    EXPECT_EQ(std::next(cont.begin(), 1), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 2), result.second);
+    result = cont.equal_range(9);
+    EXPECT_EQ(std::next(cont.begin(), 2), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 3), result.second);
+    result = cont.equal_range(11);
+    EXPECT_EQ(std::next(cont.begin(), 3), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 4), result.second);
+    result = cont.equal_range(13);
+    EXPECT_EQ(std::next(cont.begin(), 4), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 5), result.second);
+    result = cont.equal_range(15);
+    EXPECT_EQ(std::next(cont.begin(), 5), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 6), result.second);
+    result = cont.equal_range(17);
+    EXPECT_EQ(std::next(cont.begin(), 6), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 7), result.second);
+    result = cont.equal_range(19);
+    EXPECT_EQ(std::next(cont.begin(), 7), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 8), result.second);
+    result = cont.equal_range(4);
+    EXPECT_EQ(std::next(cont.begin(), 0), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 0), result.second);
+    result = cont.equal_range(6);
+    EXPECT_EQ(std::next(cont.begin(), 1), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 1), result.second);
+    result = cont.equal_range(8);
+    EXPECT_EQ(std::next(cont.begin(), 2), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 2), result.second);
+    result = cont.equal_range(10);
+    EXPECT_EQ(std::next(cont.begin(), 3), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 3), result.second);
+    result = cont.equal_range(12);
+    EXPECT_EQ(std::next(cont.begin(), 4), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 4), result.second);
+    result = cont.equal_range(14);
+    EXPECT_EQ(std::next(cont.begin(), 5), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 5), result.second);
+    result = cont.equal_range(16);
+    EXPECT_EQ(std::next(cont.begin(), 6), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 6), result.second);
+    result = cont.equal_range(18);
+    EXPECT_EQ(std::next(cont.begin(), 7), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 7), result.second);
+    result = cont.equal_range(20);
+    EXPECT_EQ(std::next(cont.begin(), 8), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 8), result.second);
+  }
+  {
+    const IntTree cont({5, 7, 9, 11, 13, 15, 17, 19});
+
+    std::pair<IntTree::const_iterator, IntTree::const_iterator> result =
+        cont.equal_range(5);
+    EXPECT_EQ(std::next(cont.begin(), 0), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 1), result.second);
+    result = cont.equal_range(7);
+    EXPECT_EQ(std::next(cont.begin(), 1), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 2), result.second);
+    result = cont.equal_range(9);
+    EXPECT_EQ(std::next(cont.begin(), 2), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 3), result.second);
+    result = cont.equal_range(11);
+    EXPECT_EQ(std::next(cont.begin(), 3), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 4), result.second);
+    result = cont.equal_range(13);
+    EXPECT_EQ(std::next(cont.begin(), 4), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 5), result.second);
+    result = cont.equal_range(15);
+    EXPECT_EQ(std::next(cont.begin(), 5), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 6), result.second);
+    result = cont.equal_range(17);
+    EXPECT_EQ(std::next(cont.begin(), 6), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 7), result.second);
+    result = cont.equal_range(19);
+    EXPECT_EQ(std::next(cont.begin(), 7), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 8), result.second);
+    result = cont.equal_range(4);
+    EXPECT_EQ(std::next(cont.begin(), 0), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 0), result.second);
+    result = cont.equal_range(6);
+    EXPECT_EQ(std::next(cont.begin(), 1), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 1), result.second);
+    result = cont.equal_range(8);
+    EXPECT_EQ(std::next(cont.begin(), 2), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 2), result.second);
+    result = cont.equal_range(10);
+    EXPECT_EQ(std::next(cont.begin(), 3), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 3), result.second);
+    result = cont.equal_range(12);
+    EXPECT_EQ(std::next(cont.begin(), 4), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 4), result.second);
+    result = cont.equal_range(14);
+    EXPECT_EQ(std::next(cont.begin(), 5), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 5), result.second);
+    result = cont.equal_range(16);
+    EXPECT_EQ(std::next(cont.begin(), 6), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 6), result.second);
+    result = cont.equal_range(18);
+    EXPECT_EQ(std::next(cont.begin(), 7), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 7), result.second);
+    result = cont.equal_range(20);
+    EXPECT_EQ(std::next(cont.begin(), 8), result.first);
+    EXPECT_EQ(std::next(cont.begin(), 8), result.second);
+  }
+}
+
+//       iterator lower_bound(const key_type& key);
+// const_iterator lower_bound(const key_type& key) const;
+
+TEST(FlatTree, LowerBound) {
+  {
+    IntTree cont({5, 7, 9, 11, 13, 15, 17, 19});
+
+    EXPECT_EQ(cont.begin(), cont.lower_bound(5));
+    EXPECT_EQ(std::next(cont.begin()), cont.lower_bound(7));
+    EXPECT_EQ(std::next(cont.begin(), 2), cont.lower_bound(9));
+    EXPECT_EQ(std::next(cont.begin(), 3), cont.lower_bound(11));
+    EXPECT_EQ(std::next(cont.begin(), 4), cont.lower_bound(13));
+    EXPECT_EQ(std::next(cont.begin(), 5), cont.lower_bound(15));
+    EXPECT_EQ(std::next(cont.begin(), 6), cont.lower_bound(17));
+    EXPECT_EQ(std::next(cont.begin(), 7), cont.lower_bound(19));
+    EXPECT_EQ(std::next(cont.begin(), 0), cont.lower_bound(4));
+    EXPECT_EQ(std::next(cont.begin(), 1), cont.lower_bound(6));
+    EXPECT_EQ(std::next(cont.begin(), 2), cont.lower_bound(8));
+    EXPECT_EQ(std::next(cont.begin(), 3), cont.lower_bound(10));
+    EXPECT_EQ(std::next(cont.begin(), 4), cont.lower_bound(12));
+    EXPECT_EQ(std::next(cont.begin(), 5), cont.lower_bound(14));
+    EXPECT_EQ(std::next(cont.begin(), 6), cont.lower_bound(16));
+    EXPECT_EQ(std::next(cont.begin(), 7), cont.lower_bound(18));
+    EXPECT_EQ(std::next(cont.begin(), 8), cont.lower_bound(20));
+  }
+  {
+    const IntTree cont({5, 7, 9, 11, 13, 15, 17, 19});
+
+    EXPECT_EQ(cont.begin(), cont.lower_bound(5));
+    EXPECT_EQ(std::next(cont.begin()), cont.lower_bound(7));
+    EXPECT_EQ(std::next(cont.begin(), 2), cont.lower_bound(9));
+    EXPECT_EQ(std::next(cont.begin(), 3), cont.lower_bound(11));
+    EXPECT_EQ(std::next(cont.begin(), 4), cont.lower_bound(13));
+    EXPECT_EQ(std::next(cont.begin(), 5), cont.lower_bound(15));
+    EXPECT_EQ(std::next(cont.begin(), 6), cont.lower_bound(17));
+    EXPECT_EQ(std::next(cont.begin(), 7), cont.lower_bound(19));
+    EXPECT_EQ(std::next(cont.begin(), 0), cont.lower_bound(4));
+    EXPECT_EQ(std::next(cont.begin(), 1), cont.lower_bound(6));
+    EXPECT_EQ(std::next(cont.begin(), 2), cont.lower_bound(8));
+    EXPECT_EQ(std::next(cont.begin(), 3), cont.lower_bound(10));
+    EXPECT_EQ(std::next(cont.begin(), 4), cont.lower_bound(12));
+    EXPECT_EQ(std::next(cont.begin(), 5), cont.lower_bound(14));
+    EXPECT_EQ(std::next(cont.begin(), 6), cont.lower_bound(16));
+    EXPECT_EQ(std::next(cont.begin(), 7), cont.lower_bound(18));
+    EXPECT_EQ(std::next(cont.begin(), 8), cont.lower_bound(20));
+  }
+}
+
+// iterator upper_bound(const key_type& key)
+// const_iterator upper_bound(const key_type& key) const
+
+TEST(FlatTree, UpperBound) {
+  {
+    IntTree cont({5, 7, 9, 11, 13, 15, 17, 19});
+
+    EXPECT_EQ(std::next(cont.begin(), 1), cont.upper_bound(5));
+    EXPECT_EQ(std::next(cont.begin(), 2), cont.upper_bound(7));
+    EXPECT_EQ(std::next(cont.begin(), 3), cont.upper_bound(9));
+    EXPECT_EQ(std::next(cont.begin(), 4), cont.upper_bound(11));
+    EXPECT_EQ(std::next(cont.begin(), 5), cont.upper_bound(13));
+    EXPECT_EQ(std::next(cont.begin(), 6), cont.upper_bound(15));
+    EXPECT_EQ(std::next(cont.begin(), 7), cont.upper_bound(17));
+    EXPECT_EQ(std::next(cont.begin(), 8), cont.upper_bound(19));
+    EXPECT_EQ(std::next(cont.begin(), 0), cont.upper_bound(4));
+    EXPECT_EQ(std::next(cont.begin(), 1), cont.upper_bound(6));
+    EXPECT_EQ(std::next(cont.begin(), 2), cont.upper_bound(8));
+    EXPECT_EQ(std::next(cont.begin(), 3), cont.upper_bound(10));
+    EXPECT_EQ(std::next(cont.begin(), 4), cont.upper_bound(12));
+    EXPECT_EQ(std::next(cont.begin(), 5), cont.upper_bound(14));
+    EXPECT_EQ(std::next(cont.begin(), 6), cont.upper_bound(16));
+    EXPECT_EQ(std::next(cont.begin(), 7), cont.upper_bound(18));
+    EXPECT_EQ(std::next(cont.begin(), 8), cont.upper_bound(20));
+  }
+  {
+    const IntTree cont({5, 7, 9, 11, 13, 15, 17, 19});
+
+    EXPECT_EQ(std::next(cont.begin(), 1), cont.upper_bound(5));
+    EXPECT_EQ(std::next(cont.begin(), 2), cont.upper_bound(7));
+    EXPECT_EQ(std::next(cont.begin(), 3), cont.upper_bound(9));
+    EXPECT_EQ(std::next(cont.begin(), 4), cont.upper_bound(11));
+    EXPECT_EQ(std::next(cont.begin(), 5), cont.upper_bound(13));
+    EXPECT_EQ(std::next(cont.begin(), 6), cont.upper_bound(15));
+    EXPECT_EQ(std::next(cont.begin(), 7), cont.upper_bound(17));
+    EXPECT_EQ(std::next(cont.begin(), 8), cont.upper_bound(19));
+    EXPECT_EQ(std::next(cont.begin(), 0), cont.upper_bound(4));
+    EXPECT_EQ(std::next(cont.begin(), 1), cont.upper_bound(6));
+    EXPECT_EQ(std::next(cont.begin(), 2), cont.upper_bound(8));
+    EXPECT_EQ(std::next(cont.begin(), 3), cont.upper_bound(10));
+    EXPECT_EQ(std::next(cont.begin(), 4), cont.upper_bound(12));
+    EXPECT_EQ(std::next(cont.begin(), 5), cont.upper_bound(14));
+    EXPECT_EQ(std::next(cont.begin(), 6), cont.upper_bound(16));
+    EXPECT_EQ(std::next(cont.begin(), 7), cont.upper_bound(18));
+    EXPECT_EQ(std::next(cont.begin(), 8), cont.upper_bound(20));
+  }
+}
+
+// ----------------------------------------------------------------------------
+// General operations.
+
+// void swap(flat_tree& other)
+// void swap(flat_tree& lhs, flat_tree& rhs)
+
+TEST(FlatTreeOurs, Swap) {
+  IntTree x({1, 2, 3});
+  IntTree y({4});
+  swap(x, y);
+  EXPECT_THAT(x, ElementsAre(4));
+  EXPECT_THAT(y, ElementsAre(1, 2, 3));
+
+  y.swap(x);
+  EXPECT_THAT(x, ElementsAre(1, 2, 3));
+  EXPECT_THAT(y, ElementsAre(4));
+}
+
+// bool operator==(const flat_tree& lhs, const flat_tree& rhs)
+// bool operator!=(const flat_tree& lhs, const flat_tree& rhs)
+// bool operator<(const flat_tree& lhs, const flat_tree& rhs)
+// bool operator>(const flat_tree& lhs, const flat_tree& rhs)
+// bool operator<=(const flat_tree& lhs, const flat_tree& rhs)
+// bool operator>=(const flat_tree& lhs, const flat_tree& rhs)
+
+TEST(FlatTree, Comparison) {
+  // Provided comparator does not participate in comparison.
+  ReversedTree biggest({3});
+  ReversedTree smallest({1});
+  ReversedTree middle({1, 2});
+
+  EXPECT_EQ(biggest, biggest);
+  EXPECT_NE(biggest, smallest);
+  EXPECT_LT(smallest, middle);
+  EXPECT_LE(smallest, middle);
+  EXPECT_LE(middle, middle);
+  EXPECT_GT(biggest, middle);
+  EXPECT_GE(biggest, middle);
+  EXPECT_GE(biggest, biggest);
+}
+
+TEST(FlatSet, EraseIf) {
+  IntTree x;
+  EraseIf(x, [](int) { return false; });
+  EXPECT_THAT(x, ElementsAre());
+
+  x = {1, 2, 3};
+  EraseIf(x, [](int elem) { return !(elem & 1); });
+  EXPECT_THAT(x, ElementsAre(1, 3));
+
+  x = {1, 2, 3, 4};
+  EraseIf(x, [](int elem) { return elem & 1; });
+  EXPECT_THAT(x, ElementsAre(2, 4));
+}
+
+}  // namespace internal
+}  // namespace base
diff --git a/base/containers/hash_tables_unittest.cc b/base/containers/hash_tables_unittest.cc
new file mode 100644
index 0000000..6072e5d
--- /dev/null
+++ b/base/containers/hash_tables_unittest.cc
@@ -0,0 +1,67 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/containers/hash_tables.h"
+
+#include <stdint.h>
+#include <string>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+class HashPairTest : public testing::Test {
+};
+
+#define INSERT_PAIR_TEST(Type, value1, value2) \
+  { \
+    Type pair(value1, value2); \
+    base::hash_map<Type, int> map; \
+    map[pair] = 1; \
+  }
+
+// Verify that a hash_map can be constructed for pairs of integers of various
+// sizes.
+TEST_F(HashPairTest, IntegerPairs) {
+  typedef std::pair<int16_t, int16_t> Int16Int16Pair;
+  typedef std::pair<int16_t, int32_t> Int16Int32Pair;
+  typedef std::pair<int16_t, int64_t> Int16Int64Pair;
+
+  INSERT_PAIR_TEST(Int16Int16Pair, 4, 6);
+  INSERT_PAIR_TEST(Int16Int32Pair, 9, (1 << 29) + 378128932);
+  INSERT_PAIR_TEST(Int16Int64Pair, 10,
+                   (INT64_C(1) << 60) + INT64_C(78931732321));
+
+  typedef std::pair<int32_t, int16_t> Int32Int16Pair;
+  typedef std::pair<int32_t, int32_t> Int32Int32Pair;
+  typedef std::pair<int32_t, int64_t> Int32Int64Pair;
+
+  INSERT_PAIR_TEST(Int32Int16Pair, 4, 6);
+  INSERT_PAIR_TEST(Int32Int32Pair, 9, (1 << 29) + 378128932);
+  INSERT_PAIR_TEST(Int32Int64Pair, 10,
+                   (INT64_C(1) << 60) + INT64_C(78931732321));
+
+  typedef std::pair<int64_t, int16_t> Int64Int16Pair;
+  typedef std::pair<int64_t, int32_t> Int64Int32Pair;
+  typedef std::pair<int64_t, int64_t> Int64Int64Pair;
+
+  INSERT_PAIR_TEST(Int64Int16Pair, 4, 6);
+  INSERT_PAIR_TEST(Int64Int32Pair, 9, (1 << 29) + 378128932);
+  INSERT_PAIR_TEST(Int64Int64Pair, 10,
+                   (INT64_C(1) << 60) + INT64_C(78931732321));
+}
+
+// Verify that base::hash_set<const char*> compares by pointer value, not as C
+// strings.
+TEST(HashTableTest, CharPointers) {
+  std::string str1("hello");
+  std::string str2("hello");
+  base::hash_set<const char*> set;
+
+  set.insert(str1.c_str());
+  EXPECT_EQ(1u, set.count(str1.c_str()));
+  EXPECT_EQ(0u, set.count(str2.c_str()));
+}
+
+}  // namespace
diff --git a/base/containers/id_map.h b/base/containers/id_map.h
new file mode 100644
index 0000000..4c816da
--- /dev/null
+++ b/base/containers/id_map.h
@@ -0,0 +1,290 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_CONTAINERS_ID_MAP_H_
+#define BASE_CONTAINERS_ID_MAP_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+#include <set>
+#include <type_traits>
+#include <unordered_map>
+#include <utility>
+
+#include "base/containers/flat_set.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/sequence_checker.h"
+
+namespace base {
+
+// 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.
+
+// The map's value type (the V param) can be any dereferenceable type, such as a
+// raw pointer or smart pointer
+template <typename V, typename K = int32_t>
+class IDMap final {
+ public:
+  using KeyType = K;
+
+ private:
+  using T = typename std::remove_reference<decltype(*V())>::type;
+
+  using HashTable = std::unordered_map<KeyType, V>;
+
+ public:
+  IDMap() : iteration_depth_(0), next_id_(1), check_on_null_data_(false) {
+    // A number of consumers of IDMap create it on one thread but always
+    // access it from a different, but consistent, thread (or sequence)
+    // post-construction. The first call to CalledOnValidSequence() will re-bind
+    // it.
+    DETACH_FROM_SEQUENCE(sequence_checker_);
+  }
+
+  ~IDMap() {
+    // Many IDMap's are static, and hence will be destroyed on the main
+    // thread. However, all the accesses may take place on another thread (or
+    // sequence), such as the IO thread. Detaching again to clean this up.
+    DETACH_FROM_SEQUENCE(sequence_checker_);
+  }
+
+  // Sets whether Add and Replace should DCHECK if passed in NULL data.
+  // Default is false.
+  void set_check_on_null_data(bool value) { check_on_null_data_ = value; }
+
+  // Adds a view with an automatically generated unique ID. See AddWithID.
+  KeyType Add(V data) { return AddInternal(std::move(data)); }
+
+  // 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(V data, KeyType id) { AddWithIDInternal(std::move(data), id); }
+
+  void Remove(KeyType id) {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    typename HashTable::iterator i = data_.find(id);
+    if (i == data_.end() || IsRemoved(id)) {
+      NOTREACHED() << "Attempting to remove an item not in the list";
+      return;
+    }
+
+    if (iteration_depth_ == 0) {
+      data_.erase(i);
+    } else {
+      removed_ids_.insert(id);
+    }
+  }
+
+  // Replaces the value for |id| with |new_data| and returns the existing value.
+  // Should only be called with an already added id.
+  V Replace(KeyType id, V new_data) {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    DCHECK(!check_on_null_data_ || new_data);
+    typename HashTable::iterator i = data_.find(id);
+    DCHECK(i != data_.end());
+    DCHECK(!IsRemoved(id));
+
+    using std::swap;
+    swap(i->second, new_data);
+    return new_data;
+  }
+
+  void Clear() {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    if (iteration_depth_ == 0) {
+      data_.clear();
+    } else {
+      removed_ids_.reserve(data_.size());
+      removed_ids_.insert(KeyIterator(data_.begin()), KeyIterator(data_.end()));
+    }
+  }
+
+  bool IsEmpty() const {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    return size() == 0u;
+  }
+
+  T* Lookup(KeyType id) const {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    typename HashTable::const_iterator i = data_.find(id);
+    if (i == data_.end() || !i->second || IsRemoved(id))
+      return nullptr;
+    return &*i->second;
+  }
+
+  size_t size() const {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    return data_.size() - removed_ids_.size();
+  }
+
+#if defined(UNIT_TEST)
+  int iteration_depth() const {
+    return iteration_depth_;
+  }
+#endif  // defined(UNIT_TEST)
+
+  // It is safe to remove elements from the map during iteration. All iterators
+  // will remain valid.
+  template<class ReturnType>
+  class Iterator {
+   public:
+    Iterator(IDMap<V, K>* map) : map_(map), iter_(map_->data_.begin()) {
+      Init();
+    }
+
+    Iterator(const Iterator& iter)
+        : map_(iter.map_),
+          iter_(iter.iter_) {
+      Init();
+    }
+
+    const Iterator& operator=(const Iterator& iter) {
+      map_ = iter.map;
+      iter_ = iter.iter;
+      Init();
+      return *this;
+    }
+
+    ~Iterator() {
+      DCHECK_CALLED_ON_VALID_SEQUENCE(map_->sequence_checker_);
+
+      // We're going to decrement iteration depth. Make sure it's greater than
+      // zero so that it doesn't become negative.
+      DCHECK_LT(0, map_->iteration_depth_);
+
+      if (--map_->iteration_depth_ == 0)
+        map_->Compact();
+    }
+
+    bool IsAtEnd() const {
+      DCHECK_CALLED_ON_VALID_SEQUENCE(map_->sequence_checker_);
+      return iter_ == map_->data_.end();
+    }
+
+    KeyType GetCurrentKey() const {
+      DCHECK_CALLED_ON_VALID_SEQUENCE(map_->sequence_checker_);
+      return iter_->first;
+    }
+
+    ReturnType* GetCurrentValue() const {
+      DCHECK_CALLED_ON_VALID_SEQUENCE(map_->sequence_checker_);
+      if (!iter_->second || map_->IsRemoved(iter_->first))
+        return nullptr;
+      return &*iter_->second;
+    }
+
+    void Advance() {
+      DCHECK_CALLED_ON_VALID_SEQUENCE(map_->sequence_checker_);
+      ++iter_;
+      SkipRemovedEntries();
+    }
+
+   private:
+    void Init() {
+      DCHECK_CALLED_ON_VALID_SEQUENCE(map_->sequence_checker_);
+      ++map_->iteration_depth_;
+      SkipRemovedEntries();
+    }
+
+    void SkipRemovedEntries() {
+      while (iter_ != map_->data_.end() && map_->IsRemoved(iter_->first))
+        ++iter_;
+    }
+
+    IDMap<V, K>* map_;
+    typename HashTable::const_iterator iter_;
+  };
+
+  typedef Iterator<T> iterator;
+  typedef Iterator<const T> const_iterator;
+
+ private:
+  // Transforms a map iterator to an iterator on the keys of the map.
+  // Used by Clear() to populate |removed_ids_| in bulk.
+  struct KeyIterator : std::iterator<std::forward_iterator_tag, KeyType> {
+    using inner_iterator = typename HashTable::iterator;
+    inner_iterator iter_;
+
+    KeyIterator(inner_iterator iter) : iter_(iter) {}
+    KeyType operator*() const { return iter_->first; }
+    KeyIterator& operator++() {
+      ++iter_;
+      return *this;
+    }
+    KeyIterator operator++(int) { return KeyIterator(iter_++); }
+    bool operator==(const KeyIterator& other) const {
+      return iter_ == other.iter_;
+    }
+    bool operator!=(const KeyIterator& other) const {
+      return iter_ != other.iter_;
+    }
+  };
+
+  KeyType AddInternal(V data) {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    DCHECK(!check_on_null_data_ || data);
+    KeyType this_id = next_id_;
+    DCHECK(data_.find(this_id) == data_.end()) << "Inserting duplicate item";
+    data_[this_id] = std::move(data);
+    next_id_++;
+    return this_id;
+  }
+
+  void AddWithIDInternal(V data, KeyType id) {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    DCHECK(!check_on_null_data_ || data);
+    if (IsRemoved(id)) {
+      removed_ids_.erase(id);
+    } else {
+      DCHECK(data_.find(id) == data_.end()) << "Inserting duplicate item";
+    }
+    data_[id] = std::move(data);
+  }
+
+  bool IsRemoved(KeyType key) const {
+    return removed_ids_.find(key) != removed_ids_.end();
+  }
+
+  void Compact() {
+    DCHECK_EQ(0, iteration_depth_);
+    for (const auto& i : removed_ids_)
+      data_.erase(i);
+    removed_ids_.clear();
+  }
+
+  // Keep track of how many iterators are currently iterating on us to safely
+  // handle removing items during iteration.
+  int iteration_depth_;
+
+  // Keep set of IDs that should be removed after the outermost iteration has
+  // finished. This way we manage to not invalidate the iterator when an element
+  // is removed.
+  base::flat_set<KeyType> removed_ids_;
+
+  // The next ID that we will return from Add()
+  KeyType next_id_;
+
+  HashTable data_;
+
+  // See description above setter.
+  bool check_on_null_data_;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+
+  DISALLOW_COPY_AND_ASSIGN(IDMap);
+};
+
+}  // namespace base
+
+#endif  // BASE_CONTAINERS_ID_MAP_H_
diff --git a/base/containers/id_map_unittest.cc b/base/containers/id_map_unittest.cc
new file mode 100644
index 0000000..346b69f
--- /dev/null
+++ b/base/containers/id_map_unittest.cc
@@ -0,0 +1,399 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/containers/id_map.h"
+
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/test/gtest_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+namespace {
+
+class TestObject {
+};
+
+class DestructorCounter {
+ public:
+  explicit DestructorCounter(int* counter) : counter_(counter) {}
+  ~DestructorCounter() { ++(*counter_); }
+
+ private:
+  int* counter_;
+};
+
+}  // namespace
+
+TEST(IDMapTest, Basic) {
+  IDMap<TestObject*> map;
+  EXPECT_TRUE(map.IsEmpty());
+  EXPECT_EQ(0U, map.size());
+
+  TestObject obj1;
+  TestObject obj2;
+
+  int32_t id1 = map.Add(&obj1);
+  EXPECT_FALSE(map.IsEmpty());
+  EXPECT_EQ(1U, map.size());
+  EXPECT_EQ(&obj1, map.Lookup(id1));
+
+  int32_t id2 = map.Add(&obj2);
+  EXPECT_FALSE(map.IsEmpty());
+  EXPECT_EQ(2U, map.size());
+
+  EXPECT_EQ(&obj1, map.Lookup(id1));
+  EXPECT_EQ(&obj2, map.Lookup(id2));
+
+  map.Remove(id1);
+  EXPECT_FALSE(map.IsEmpty());
+  EXPECT_EQ(1U, map.size());
+
+  map.Remove(id2);
+  EXPECT_TRUE(map.IsEmpty());
+  EXPECT_EQ(0U, map.size());
+
+  map.AddWithID(&obj1, 1);
+  map.AddWithID(&obj2, 2);
+  EXPECT_EQ(&obj1, map.Lookup(1));
+  EXPECT_EQ(&obj2, map.Lookup(2));
+
+  EXPECT_EQ(&obj2, map.Replace(2, &obj1));
+  EXPECT_EQ(&obj1, map.Lookup(2));
+
+  EXPECT_EQ(0, map.iteration_depth());
+}
+
+TEST(IDMapTest, IteratorRemainsValidWhenRemovingCurrentElement) {
+  IDMap<TestObject*> map;
+
+  TestObject obj1;
+  TestObject obj2;
+  TestObject obj3;
+
+  map.Add(&obj1);
+  map.Add(&obj2);
+  map.Add(&obj3);
+
+  {
+    IDMap<TestObject*>::const_iterator iter(&map);
+
+    EXPECT_EQ(1, map.iteration_depth());
+
+    while (!iter.IsAtEnd()) {
+      map.Remove(iter.GetCurrentKey());
+      iter.Advance();
+    }
+
+    // Test that while an iterator is still in scope, we get the map emptiness
+    // right (http://crbug.com/35571).
+    EXPECT_TRUE(map.IsEmpty());
+    EXPECT_EQ(0U, map.size());
+  }
+
+  EXPECT_TRUE(map.IsEmpty());
+  EXPECT_EQ(0U, map.size());
+
+  EXPECT_EQ(0, map.iteration_depth());
+}
+
+TEST(IDMapTest, IteratorRemainsValidWhenRemovingOtherElements) {
+  IDMap<TestObject*> map;
+
+  const int kCount = 5;
+  TestObject obj[kCount];
+
+  for (int i = 0; i < kCount; i++)
+    map.Add(&obj[i]);
+
+  // IDMap has no predictable iteration order.
+  int32_t ids_in_iteration_order[kCount];
+  const TestObject* objs_in_iteration_order[kCount];
+  int counter = 0;
+  for (IDMap<TestObject*>::const_iterator iter(&map); !iter.IsAtEnd();
+       iter.Advance()) {
+    ids_in_iteration_order[counter] = iter.GetCurrentKey();
+    objs_in_iteration_order[counter] = iter.GetCurrentValue();
+    counter++;
+  }
+
+  counter = 0;
+  for (IDMap<TestObject*>::const_iterator iter(&map); !iter.IsAtEnd();
+       iter.Advance()) {
+    EXPECT_EQ(1, map.iteration_depth());
+
+    switch (counter) {
+      case 0:
+        EXPECT_EQ(ids_in_iteration_order[0], iter.GetCurrentKey());
+        EXPECT_EQ(objs_in_iteration_order[0], iter.GetCurrentValue());
+        map.Remove(ids_in_iteration_order[1]);
+        break;
+      case 1:
+        EXPECT_EQ(ids_in_iteration_order[2], iter.GetCurrentKey());
+        EXPECT_EQ(objs_in_iteration_order[2], iter.GetCurrentValue());
+        map.Remove(ids_in_iteration_order[3]);
+        break;
+      case 2:
+        EXPECT_EQ(ids_in_iteration_order[4], iter.GetCurrentKey());
+        EXPECT_EQ(objs_in_iteration_order[4], iter.GetCurrentValue());
+        map.Remove(ids_in_iteration_order[0]);
+        break;
+      default:
+        FAIL() << "should not have that many elements";
+        break;
+    }
+
+    counter++;
+  }
+
+  EXPECT_EQ(0, map.iteration_depth());
+}
+
+TEST(IDMapTest, CopyIterator) {
+  IDMap<TestObject*> map;
+
+  TestObject obj1;
+  TestObject obj2;
+  TestObject obj3;
+
+  map.Add(&obj1);
+  map.Add(&obj2);
+  map.Add(&obj3);
+
+  EXPECT_EQ(0, map.iteration_depth());
+
+  {
+    IDMap<TestObject*>::const_iterator iter1(&map);
+    EXPECT_EQ(1, map.iteration_depth());
+
+    // Make sure that copying the iterator correctly increments
+    // map's iteration depth.
+    IDMap<TestObject*>::const_iterator iter2(iter1);
+    EXPECT_EQ(2, map.iteration_depth());
+  }
+
+  // Make sure after destroying all iterators the map's iteration depth
+  // returns to initial state.
+  EXPECT_EQ(0, map.iteration_depth());
+}
+
+TEST(IDMapTest, AssignIterator) {
+  IDMap<TestObject*> map;
+
+  TestObject obj1;
+  TestObject obj2;
+  TestObject obj3;
+
+  map.Add(&obj1);
+  map.Add(&obj2);
+  map.Add(&obj3);
+
+  EXPECT_EQ(0, map.iteration_depth());
+
+  {
+    IDMap<TestObject*>::const_iterator iter1(&map);
+    EXPECT_EQ(1, map.iteration_depth());
+
+    IDMap<TestObject*>::const_iterator iter2(&map);
+    EXPECT_EQ(2, map.iteration_depth());
+
+    // Make sure that assigning the iterator correctly updates
+    // map's iteration depth (-1 for destruction, +1 for assignment).
+    EXPECT_EQ(2, map.iteration_depth());
+  }
+
+  // Make sure after destroying all iterators the map's iteration depth
+  // returns to initial state.
+  EXPECT_EQ(0, map.iteration_depth());
+}
+
+TEST(IDMapTest, IteratorRemainsValidWhenClearing) {
+  IDMap<TestObject*> map;
+
+  const int kCount = 5;
+  TestObject obj[kCount];
+
+  for (int i = 0; i < kCount; i++)
+    map.Add(&obj[i]);
+
+  // IDMap has no predictable iteration order.
+  int32_t ids_in_iteration_order[kCount];
+  const TestObject* objs_in_iteration_order[kCount];
+  int counter = 0;
+  for (IDMap<TestObject*>::const_iterator iter(&map); !iter.IsAtEnd();
+       iter.Advance()) {
+    ids_in_iteration_order[counter] = iter.GetCurrentKey();
+    objs_in_iteration_order[counter] = iter.GetCurrentValue();
+    counter++;
+  }
+
+  counter = 0;
+  for (IDMap<TestObject*>::const_iterator iter(&map); !iter.IsAtEnd();
+       iter.Advance()) {
+    switch (counter) {
+      case 0:
+        EXPECT_EQ(ids_in_iteration_order[0], iter.GetCurrentKey());
+        EXPECT_EQ(objs_in_iteration_order[0], iter.GetCurrentValue());
+        break;
+      case 1:
+        EXPECT_EQ(ids_in_iteration_order[1], iter.GetCurrentKey());
+        EXPECT_EQ(objs_in_iteration_order[1], iter.GetCurrentValue());
+        map.Clear();
+        EXPECT_TRUE(map.IsEmpty());
+        EXPECT_EQ(0U, map.size());
+        break;
+      default:
+        FAIL() << "should not have that many elements";
+        break;
+    }
+    counter++;
+  }
+
+  EXPECT_TRUE(map.IsEmpty());
+  EXPECT_EQ(0U, map.size());
+}
+
+TEST(IDMapTest, OwningPointersDeletesThemOnRemove) {
+  const int kCount = 3;
+
+  int external_del_count = 0;
+  DestructorCounter* external_obj[kCount];
+  int map_external_ids[kCount];
+
+  int owned_del_count = 0;
+  int map_owned_ids[kCount];
+
+  IDMap<DestructorCounter*> map_external;
+  IDMap<std::unique_ptr<DestructorCounter>> map_owned;
+
+  for (int i = 0; i < kCount; ++i) {
+    external_obj[i] = new DestructorCounter(&external_del_count);
+    map_external_ids[i] = map_external.Add(external_obj[i]);
+
+    map_owned_ids[i] =
+        map_owned.Add(std::make_unique<DestructorCounter>(&owned_del_count));
+  }
+
+  for (int i = 0; i < kCount; ++i) {
+    EXPECT_EQ(external_del_count, 0);
+    EXPECT_EQ(owned_del_count, i);
+
+    map_external.Remove(map_external_ids[i]);
+    map_owned.Remove(map_owned_ids[i]);
+  }
+
+  for (int i = 0; i < kCount; ++i) {
+    delete external_obj[i];
+  }
+
+  EXPECT_EQ(external_del_count, kCount);
+  EXPECT_EQ(owned_del_count, kCount);
+}
+
+TEST(IDMapTest, OwningPointersDeletesThemOnClear) {
+  const int kCount = 3;
+
+  int external_del_count = 0;
+  DestructorCounter* external_obj[kCount];
+
+  int owned_del_count = 0;
+
+  IDMap<DestructorCounter*> map_external;
+  IDMap<std::unique_ptr<DestructorCounter>> map_owned;
+
+  for (int i = 0; i < kCount; ++i) {
+    external_obj[i] = new DestructorCounter(&external_del_count);
+    map_external.Add(external_obj[i]);
+
+    map_owned.Add(std::make_unique<DestructorCounter>(&owned_del_count));
+  }
+
+  EXPECT_EQ(external_del_count, 0);
+  EXPECT_EQ(owned_del_count, 0);
+
+  map_external.Clear();
+  map_owned.Clear();
+
+  EXPECT_EQ(external_del_count, 0);
+  EXPECT_EQ(owned_del_count, kCount);
+
+  for (int i = 0; i < kCount; ++i) {
+    delete external_obj[i];
+  }
+
+  EXPECT_EQ(external_del_count, kCount);
+  EXPECT_EQ(owned_del_count, kCount);
+}
+
+TEST(IDMapTest, OwningPointersDeletesThemOnDestruct) {
+  const int kCount = 3;
+
+  int external_del_count = 0;
+  DestructorCounter* external_obj[kCount];
+
+  int owned_del_count = 0;
+
+  {
+    IDMap<DestructorCounter*> map_external;
+    IDMap<std::unique_ptr<DestructorCounter>> map_owned;
+
+    for (int i = 0; i < kCount; ++i) {
+      external_obj[i] = new DestructorCounter(&external_del_count);
+      map_external.Add(external_obj[i]);
+
+      map_owned.Add(std::make_unique<DestructorCounter>(&owned_del_count));
+    }
+  }
+
+  EXPECT_EQ(external_del_count, 0);
+
+  for (int i = 0; i < kCount; ++i) {
+    delete external_obj[i];
+  }
+
+  EXPECT_EQ(external_del_count, kCount);
+  EXPECT_EQ(owned_del_count, kCount);
+}
+
+TEST(IDMapTest, Int64KeyType) {
+  IDMap<TestObject*, int64_t> map;
+  TestObject obj1;
+  const int64_t kId1 = 999999999999999999;
+
+  map.AddWithID(&obj1, kId1);
+  EXPECT_EQ(&obj1, map.Lookup(kId1));
+
+  IDMap<TestObject*, int64_t>::const_iterator iter(&map);
+  ASSERT_FALSE(iter.IsAtEnd());
+  EXPECT_EQ(kId1, iter.GetCurrentKey());
+  EXPECT_EQ(&obj1, iter.GetCurrentValue());
+  iter.Advance();
+  ASSERT_TRUE(iter.IsAtEnd());
+
+  map.Remove(kId1);
+  EXPECT_TRUE(map.IsEmpty());
+}
+
+TEST(IDMapTest, RemovedValueHandling) {
+  TestObject obj;
+  IDMap<TestObject*> map;
+  int key = map.Add(&obj);
+
+  IDMap<TestObject*>::iterator itr(&map);
+  map.Clear();
+  EXPECT_DCHECK_DEATH(map.Remove(key));
+  EXPECT_DCHECK_DEATH(map.Replace(key, &obj));
+  EXPECT_FALSE(map.Lookup(key));
+  EXPECT_FALSE(itr.IsAtEnd());
+  EXPECT_FALSE(itr.GetCurrentValue());
+
+  EXPECT_TRUE(map.IsEmpty());
+  map.AddWithID(&obj, key);
+  EXPECT_EQ(1u, map.size());
+}
+
+}  // namespace base
diff --git a/base/containers/linked_list_unittest.cc b/base/containers/linked_list_unittest.cc
new file mode 100644
index 0000000..8e547ba
--- /dev/null
+++ b/base/containers/linked_list_unittest.cc
@@ -0,0 +1,349 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/containers/linked_list.h"
+#include "base/macros.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace {
+
+class Node : public LinkNode<Node> {
+ public:
+  explicit Node(int id) : id_(id) {}
+
+  int id() const { return id_; }
+
+ private:
+  int id_;
+};
+
+class MultipleInheritanceNodeBase {
+ public:
+  MultipleInheritanceNodeBase() : field_taking_up_space_(0) {}
+  int field_taking_up_space_;
+};
+
+class MultipleInheritanceNode : public MultipleInheritanceNodeBase,
+                                public LinkNode<MultipleInheritanceNode> {
+ public:
+  MultipleInheritanceNode() = default;
+};
+
+class MovableNode : public LinkNode<MovableNode> {
+ public:
+  explicit MovableNode(int id) : id_(id) {}
+
+  MovableNode(MovableNode&&) = default;
+
+  int id() const { return id_; }
+
+ private:
+  int id_;
+};
+
+// Checks that when iterating |list| (either from head to tail, or from
+// tail to head, as determined by |forward|), we get back |node_ids|,
+// which is an array of size |num_nodes|.
+void ExpectListContentsForDirection(const LinkedList<Node>& list,
+  int num_nodes, const int* node_ids, bool forward) {
+  int i = 0;
+  for (const LinkNode<Node>* node = (forward ? list.head() : list.tail());
+       node != list.end();
+       node = (forward ? node->next() : node->previous())) {
+    ASSERT_LT(i, num_nodes);
+    int index_of_id = forward ? i : num_nodes - i - 1;
+    EXPECT_EQ(node_ids[index_of_id], node->value()->id());
+    ++i;
+  }
+  EXPECT_EQ(num_nodes, i);
+}
+
+void ExpectListContents(const LinkedList<Node>& list,
+                        int num_nodes,
+                        const int* node_ids) {
+  {
+    SCOPED_TRACE("Iterating forward (from head to tail)");
+    ExpectListContentsForDirection(list, num_nodes, node_ids, true);
+  }
+  {
+    SCOPED_TRACE("Iterating backward (from tail to head)");
+    ExpectListContentsForDirection(list, num_nodes, node_ids, false);
+  }
+}
+
+TEST(LinkedList, Empty) {
+  LinkedList<Node> list;
+  EXPECT_EQ(list.end(), list.head());
+  EXPECT_EQ(list.end(), list.tail());
+  ExpectListContents(list, 0, nullptr);
+}
+
+TEST(LinkedList, Append) {
+  LinkedList<Node> list;
+  ExpectListContents(list, 0, nullptr);
+
+  Node n1(1);
+  list.Append(&n1);
+
+  EXPECT_EQ(&n1, list.head());
+  EXPECT_EQ(&n1, list.tail());
+  {
+    const int expected[] = {1};
+    ExpectListContents(list, arraysize(expected), expected);
+  }
+
+  Node n2(2);
+  list.Append(&n2);
+
+  EXPECT_EQ(&n1, list.head());
+  EXPECT_EQ(&n2, list.tail());
+  {
+    const int expected[] = {1, 2};
+    ExpectListContents(list, arraysize(expected), expected);
+  }
+
+  Node n3(3);
+  list.Append(&n3);
+
+  EXPECT_EQ(&n1, list.head());
+  EXPECT_EQ(&n3, list.tail());
+  {
+    const int expected[] = {1, 2, 3};
+    ExpectListContents(list, arraysize(expected), expected);
+  }
+}
+
+TEST(LinkedList, RemoveFromList) {
+  LinkedList<Node> list;
+
+  Node n1(1);
+  Node n2(2);
+  Node n3(3);
+  Node n4(4);
+  Node n5(5);
+
+  list.Append(&n1);
+  list.Append(&n2);
+  list.Append(&n3);
+  list.Append(&n4);
+  list.Append(&n5);
+
+  EXPECT_EQ(&n1, list.head());
+  EXPECT_EQ(&n5, list.tail());
+  {
+    const int expected[] = {1, 2, 3, 4, 5};
+    ExpectListContents(list, arraysize(expected), expected);
+  }
+
+  // Remove from the middle.
+  n3.RemoveFromList();
+
+  EXPECT_EQ(&n1, list.head());
+  EXPECT_EQ(&n5, list.tail());
+  {
+    const int expected[] = {1, 2, 4, 5};
+    ExpectListContents(list, arraysize(expected), expected);
+  }
+
+  // Remove from the tail.
+  n5.RemoveFromList();
+
+  EXPECT_EQ(&n1, list.head());
+  EXPECT_EQ(&n4, list.tail());
+  {
+    const int expected[] = {1, 2, 4};
+    ExpectListContents(list, arraysize(expected), expected);
+  }
+
+  // Remove from the head.
+  n1.RemoveFromList();
+
+  EXPECT_EQ(&n2, list.head());
+  EXPECT_EQ(&n4, list.tail());
+  {
+    const int expected[] = {2, 4};
+    ExpectListContents(list, arraysize(expected), expected);
+  }
+
+  // Empty the list.
+  n2.RemoveFromList();
+  n4.RemoveFromList();
+
+  ExpectListContents(list, 0, nullptr);
+  EXPECT_EQ(list.end(), list.head());
+  EXPECT_EQ(list.end(), list.tail());
+
+  // Fill the list once again.
+  list.Append(&n1);
+  list.Append(&n2);
+  list.Append(&n3);
+  list.Append(&n4);
+  list.Append(&n5);
+
+  EXPECT_EQ(&n1, list.head());
+  EXPECT_EQ(&n5, list.tail());
+  {
+    const int expected[] = {1, 2, 3, 4, 5};
+    ExpectListContents(list, arraysize(expected), expected);
+  }
+}
+
+TEST(LinkedList, InsertBefore) {
+  LinkedList<Node> list;
+
+  Node n1(1);
+  Node n2(2);
+  Node n3(3);
+  Node n4(4);
+
+  list.Append(&n1);
+  list.Append(&n2);
+
+  EXPECT_EQ(&n1, list.head());
+  EXPECT_EQ(&n2, list.tail());
+  {
+    const int expected[] = {1, 2};
+    ExpectListContents(list, arraysize(expected), expected);
+  }
+
+  n3.InsertBefore(&n2);
+
+  EXPECT_EQ(&n1, list.head());
+  EXPECT_EQ(&n2, list.tail());
+  {
+    const int expected[] = {1, 3, 2};
+    ExpectListContents(list, arraysize(expected), expected);
+  }
+
+  n4.InsertBefore(&n1);
+
+  EXPECT_EQ(&n4, list.head());
+  EXPECT_EQ(&n2, list.tail());
+  {
+    const int expected[] = {4, 1, 3, 2};
+    ExpectListContents(list, arraysize(expected), expected);
+  }
+}
+
+TEST(LinkedList, InsertAfter) {
+  LinkedList<Node> list;
+
+  Node n1(1);
+  Node n2(2);
+  Node n3(3);
+  Node n4(4);
+
+  list.Append(&n1);
+  list.Append(&n2);
+
+  EXPECT_EQ(&n1, list.head());
+  EXPECT_EQ(&n2, list.tail());
+  {
+    const int expected[] = {1, 2};
+    ExpectListContents(list, arraysize(expected), expected);
+  }
+
+  n3.InsertAfter(&n2);
+
+  EXPECT_EQ(&n1, list.head());
+  EXPECT_EQ(&n3, list.tail());
+  {
+    const int expected[] = {1, 2, 3};
+    ExpectListContents(list, arraysize(expected), expected);
+  }
+
+  n4.InsertAfter(&n1);
+
+  EXPECT_EQ(&n1, list.head());
+  EXPECT_EQ(&n3, list.tail());
+  {
+    const int expected[] = {1, 4, 2, 3};
+    ExpectListContents(list, arraysize(expected), expected);
+  }
+}
+
+TEST(LinkedList, MultipleInheritanceNode) {
+  MultipleInheritanceNode node;
+  EXPECT_EQ(&node, node.value());
+}
+
+TEST(LinkedList, EmptyListIsEmpty) {
+  LinkedList<Node> list;
+  EXPECT_TRUE(list.empty());
+}
+
+TEST(LinkedList, NonEmptyListIsNotEmpty) {
+  LinkedList<Node> list;
+
+  Node n(1);
+  list.Append(&n);
+
+  EXPECT_FALSE(list.empty());
+}
+
+TEST(LinkedList, EmptiedListIsEmptyAgain) {
+  LinkedList<Node> list;
+
+  Node n(1);
+  list.Append(&n);
+  n.RemoveFromList();
+
+  EXPECT_TRUE(list.empty());
+}
+
+TEST(LinkedList, NodesCanBeReused) {
+  LinkedList<Node> list1;
+  LinkedList<Node> list2;
+
+  Node n(1);
+  list1.Append(&n);
+  n.RemoveFromList();
+  list2.Append(&n);
+
+  EXPECT_EQ(list2.head()->value(), &n);
+}
+
+TEST(LinkedList, RemovedNodeHasNullNextPrevious) {
+  LinkedList<Node> list;
+
+  Node n(1);
+  list.Append(&n);
+  n.RemoveFromList();
+
+  EXPECT_EQ(nullptr, n.next());
+  EXPECT_EQ(nullptr, n.previous());
+}
+
+TEST(LinkedList, NodeMoveConstructor) {
+  LinkedList<MovableNode> list;
+
+  MovableNode n1(1);
+  MovableNode n2(2);
+  MovableNode n3(3);
+
+  list.Append(&n1);
+  list.Append(&n2);
+  list.Append(&n3);
+
+  EXPECT_EQ(&n1, n2.previous());
+  EXPECT_EQ(&n2, n1.next());
+  EXPECT_EQ(&n3, n2.next());
+  EXPECT_EQ(&n2, n3.previous());
+  EXPECT_EQ(2, n2.id());
+
+  MovableNode n2_new(std::move(n2));
+
+  EXPECT_EQ(nullptr, n2.next());
+  EXPECT_EQ(nullptr, n2.previous());
+
+  EXPECT_EQ(&n1, n2_new.previous());
+  EXPECT_EQ(&n2_new, n1.next());
+  EXPECT_EQ(&n3, n2_new.next());
+  EXPECT_EQ(&n2_new, n3.previous());
+  EXPECT_EQ(2, n2_new.id());
+}
+
+}  // namespace
+}  // namespace base
diff --git a/base/containers/mru_cache_unittest.cc b/base/containers/mru_cache_unittest.cc
new file mode 100644
index 0000000..28e6f0d
--- /dev/null
+++ b/base/containers/mru_cache_unittest.cc
@@ -0,0 +1,394 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/containers/mru_cache.h"
+
+#include <cstddef>
+#include <memory>
+
+#include "base/memory/ptr_util.h"
+#include "base/trace_event/memory_usage_estimator.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+namespace {
+
+int cached_item_live_count = 0;
+
+struct CachedItem {
+  CachedItem() : value(0) {
+    cached_item_live_count++;
+  }
+
+  explicit CachedItem(int new_value) : value(new_value) {
+    cached_item_live_count++;
+  }
+
+  explicit CachedItem(const CachedItem& other) : value(other.value) {
+    cached_item_live_count++;
+  }
+
+  ~CachedItem() {
+    cached_item_live_count--;
+  }
+
+  int value;
+};
+
+}  // namespace
+
+TEST(MRUCacheTest, Basic) {
+  typedef base::MRUCache<int, CachedItem> Cache;
+  Cache cache(Cache::NO_AUTO_EVICT);
+
+  // Check failure conditions
+  {
+    CachedItem test_item;
+    EXPECT_TRUE(cache.Get(0) == cache.end());
+    EXPECT_TRUE(cache.Peek(0) == cache.end());
+  }
+
+  static const int kItem1Key = 5;
+  CachedItem item1(10);
+  Cache::iterator inserted_item = cache.Put(kItem1Key, item1);
+  EXPECT_EQ(1U, cache.size());
+
+  // Check that item1 was properly inserted.
+  {
+    Cache::iterator found = cache.Get(kItem1Key);
+    EXPECT_TRUE(inserted_item == cache.begin());
+    EXPECT_TRUE(found != cache.end());
+
+    found = cache.Peek(kItem1Key);
+    EXPECT_TRUE(found != cache.end());
+
+    EXPECT_EQ(kItem1Key, found->first);
+    EXPECT_EQ(item1.value, found->second.value);
+  }
+
+  static const int kItem2Key = 7;
+  CachedItem item2(12);
+  cache.Put(kItem2Key, item2);
+  EXPECT_EQ(2U, cache.size());
+
+  // Check that item1 is the oldest since item2 was added afterwards.
+  {
+    Cache::reverse_iterator oldest = cache.rbegin();
+    ASSERT_TRUE(oldest != cache.rend());
+    EXPECT_EQ(kItem1Key, oldest->first);
+    EXPECT_EQ(item1.value, oldest->second.value);
+  }
+
+  // Check that item1 is still accessible by key.
+  {
+    Cache::iterator test_item = cache.Get(kItem1Key);
+    ASSERT_TRUE(test_item != cache.end());
+    EXPECT_EQ(kItem1Key, test_item->first);
+    EXPECT_EQ(item1.value, test_item->second.value);
+  }
+
+  // Check that retrieving item1 pushed item2 to oldest.
+  {
+    Cache::reverse_iterator oldest = cache.rbegin();
+    ASSERT_TRUE(oldest != cache.rend());
+    EXPECT_EQ(kItem2Key, oldest->first);
+    EXPECT_EQ(item2.value, oldest->second.value);
+  }
+
+  // Remove the oldest item and check that item1 is now the only member.
+  {
+    Cache::reverse_iterator next = cache.Erase(cache.rbegin());
+
+    EXPECT_EQ(1U, cache.size());
+
+    EXPECT_TRUE(next == cache.rbegin());
+    EXPECT_EQ(kItem1Key, next->first);
+    EXPECT_EQ(item1.value, next->second.value);
+
+    cache.Erase(cache.begin());
+    EXPECT_EQ(0U, cache.size());
+  }
+
+  // Check that Clear() works properly.
+  cache.Put(kItem1Key, item1);
+  cache.Put(kItem2Key, item2);
+  EXPECT_EQ(2U, cache.size());
+  cache.Clear();
+  EXPECT_EQ(0U, cache.size());
+}
+
+TEST(MRUCacheTest, GetVsPeek) {
+  typedef base::MRUCache<int, CachedItem> Cache;
+  Cache cache(Cache::NO_AUTO_EVICT);
+
+  static const int kItem1Key = 1;
+  CachedItem item1(10);
+  cache.Put(kItem1Key, item1);
+
+  static const int kItem2Key = 2;
+  CachedItem item2(20);
+  cache.Put(kItem2Key, item2);
+
+  // This should do nothing since the size is bigger than the number of items.
+  cache.ShrinkToSize(100);
+
+  // Check that item1 starts out as oldest
+  {
+    Cache::reverse_iterator iter = cache.rbegin();
+    ASSERT_TRUE(iter != cache.rend());
+    EXPECT_EQ(kItem1Key, iter->first);
+    EXPECT_EQ(item1.value, iter->second.value);
+  }
+
+  // Check that Peek doesn't change ordering
+  {
+    Cache::iterator peekiter = cache.Peek(kItem1Key);
+    ASSERT_TRUE(peekiter != cache.end());
+
+    Cache::reverse_iterator iter = cache.rbegin();
+    ASSERT_TRUE(iter != cache.rend());
+    EXPECT_EQ(kItem1Key, iter->first);
+    EXPECT_EQ(item1.value, iter->second.value);
+  }
+}
+
+TEST(MRUCacheTest, KeyReplacement) {
+  typedef base::MRUCache<int, CachedItem> Cache;
+  Cache cache(Cache::NO_AUTO_EVICT);
+
+  static const int kItem1Key = 1;
+  CachedItem item1(10);
+  cache.Put(kItem1Key, item1);
+
+  static const int kItem2Key = 2;
+  CachedItem item2(20);
+  cache.Put(kItem2Key, item2);
+
+  static const int kItem3Key = 3;
+  CachedItem item3(30);
+  cache.Put(kItem3Key, item3);
+
+  static const int kItem4Key = 4;
+  CachedItem item4(40);
+  cache.Put(kItem4Key, item4);
+
+  CachedItem item5(50);
+  cache.Put(kItem3Key, item5);
+
+  EXPECT_EQ(4U, cache.size());
+  for (int i = 0; i < 3; ++i) {
+    Cache::reverse_iterator iter = cache.rbegin();
+    ASSERT_TRUE(iter != cache.rend());
+  }
+
+  // Make it so only the most important element is there.
+  cache.ShrinkToSize(1);
+
+  Cache::iterator iter = cache.begin();
+  EXPECT_EQ(kItem3Key, iter->first);
+  EXPECT_EQ(item5.value, iter->second.value);
+}
+
+// Make sure that the owning version release its pointers properly.
+TEST(MRUCacheTest, Owning) {
+  using Cache = base::MRUCache<int, std::unique_ptr<CachedItem>>;
+  Cache cache(Cache::NO_AUTO_EVICT);
+
+  int initial_count = cached_item_live_count;
+
+  // First insert and item and then overwrite it.
+  static const int kItem1Key = 1;
+  cache.Put(kItem1Key, WrapUnique(new CachedItem(20)));
+  cache.Put(kItem1Key, WrapUnique(new CachedItem(22)));
+
+  // There should still be one item, and one extra live item.
+  Cache::iterator iter = cache.Get(kItem1Key);
+  EXPECT_EQ(1U, cache.size());
+  EXPECT_TRUE(iter != cache.end());
+  EXPECT_EQ(initial_count + 1, cached_item_live_count);
+
+  // Now remove it.
+  cache.Erase(cache.begin());
+  EXPECT_EQ(initial_count, cached_item_live_count);
+
+  // Now try another cache that goes out of scope to make sure its pointers
+  // go away.
+  {
+    Cache cache2(Cache::NO_AUTO_EVICT);
+    cache2.Put(1, WrapUnique(new CachedItem(20)));
+    cache2.Put(2, WrapUnique(new CachedItem(20)));
+  }
+
+  // There should be no objects leaked.
+  EXPECT_EQ(initial_count, cached_item_live_count);
+
+  // Check that Clear() also frees things correctly.
+  {
+    Cache cache2(Cache::NO_AUTO_EVICT);
+    cache2.Put(1, WrapUnique(new CachedItem(20)));
+    cache2.Put(2, WrapUnique(new CachedItem(20)));
+    EXPECT_EQ(initial_count + 2, cached_item_live_count);
+    cache2.Clear();
+    EXPECT_EQ(initial_count, cached_item_live_count);
+  }
+}
+
+TEST(MRUCacheTest, AutoEvict) {
+  using Cache = base::MRUCache<int, std::unique_ptr<CachedItem>>;
+  static const Cache::size_type kMaxSize = 3;
+
+  int initial_count = cached_item_live_count;
+
+  {
+    Cache cache(kMaxSize);
+
+    static const int kItem1Key = 1, kItem2Key = 2, kItem3Key = 3, kItem4Key = 4;
+    cache.Put(kItem1Key, std::make_unique<CachedItem>(20));
+    cache.Put(kItem2Key, std::make_unique<CachedItem>(21));
+    cache.Put(kItem3Key, std::make_unique<CachedItem>(22));
+    cache.Put(kItem4Key, std::make_unique<CachedItem>(23));
+
+    // The cache should only have kMaxSize items in it even though we inserted
+    // more.
+    EXPECT_EQ(kMaxSize, cache.size());
+  }
+
+  // There should be no objects leaked.
+  EXPECT_EQ(initial_count, cached_item_live_count);
+}
+
+TEST(MRUCacheTest, HashingMRUCache) {
+  // Very simple test to make sure that the hashing cache works correctly.
+  typedef base::HashingMRUCache<std::string, CachedItem> Cache;
+  Cache cache(Cache::NO_AUTO_EVICT);
+
+  CachedItem one(1);
+  cache.Put("First", one);
+
+  CachedItem two(2);
+  cache.Put("Second", two);
+
+  EXPECT_EQ(one.value, cache.Get("First")->second.value);
+  EXPECT_EQ(two.value, cache.Get("Second")->second.value);
+  cache.ShrinkToSize(1);
+  EXPECT_EQ(two.value, cache.Get("Second")->second.value);
+  EXPECT_TRUE(cache.Get("First") == cache.end());
+}
+
+TEST(MRUCacheTest, Swap) {
+  typedef base::MRUCache<int, CachedItem> Cache;
+  Cache cache1(Cache::NO_AUTO_EVICT);
+
+  // Insert two items into cache1.
+  static const int kItem1Key = 1;
+  CachedItem item1(2);
+  Cache::iterator inserted_item = cache1.Put(kItem1Key, item1);
+  EXPECT_EQ(1U, cache1.size());
+
+  static const int kItem2Key = 3;
+  CachedItem item2(4);
+  cache1.Put(kItem2Key, item2);
+  EXPECT_EQ(2U, cache1.size());
+
+  // Verify cache1's elements.
+  {
+    Cache::iterator iter = cache1.begin();
+    ASSERT_TRUE(iter != cache1.end());
+    EXPECT_EQ(kItem2Key, iter->first);
+    EXPECT_EQ(item2.value, iter->second.value);
+
+    ++iter;
+    ASSERT_TRUE(iter != cache1.end());
+    EXPECT_EQ(kItem1Key, iter->first);
+    EXPECT_EQ(item1.value, iter->second.value);
+  }
+
+  // Create another cache2.
+  Cache cache2(Cache::NO_AUTO_EVICT);
+
+  // Insert three items into cache2.
+  static const int kItem3Key = 5;
+  CachedItem item3(6);
+  inserted_item = cache2.Put(kItem3Key, item3);
+  EXPECT_EQ(1U, cache2.size());
+
+  static const int kItem4Key = 7;
+  CachedItem item4(8);
+  cache2.Put(kItem4Key, item4);
+  EXPECT_EQ(2U, cache2.size());
+
+  static const int kItem5Key = 9;
+  CachedItem item5(10);
+  cache2.Put(kItem5Key, item5);
+  EXPECT_EQ(3U, cache2.size());
+
+  // Verify cache2's elements.
+  {
+    Cache::iterator iter = cache2.begin();
+    ASSERT_TRUE(iter != cache2.end());
+    EXPECT_EQ(kItem5Key, iter->first);
+    EXPECT_EQ(item5.value, iter->second.value);
+
+    ++iter;
+    ASSERT_TRUE(iter != cache2.end());
+    EXPECT_EQ(kItem4Key, iter->first);
+    EXPECT_EQ(item4.value, iter->second.value);
+
+    ++iter;
+    ASSERT_TRUE(iter != cache2.end());
+    EXPECT_EQ(kItem3Key, iter->first);
+    EXPECT_EQ(item3.value, iter->second.value);
+  }
+
+  // Swap cache1 and cache2 and verify cache2 has cache1's elements and cache1
+  // has cache2's elements.
+  cache2.Swap(cache1);
+
+  EXPECT_EQ(3U, cache1.size());
+  EXPECT_EQ(2U, cache2.size());
+
+  // Verify cache1's elements.
+  {
+    Cache::iterator iter = cache1.begin();
+    ASSERT_TRUE(iter != cache1.end());
+    EXPECT_EQ(kItem5Key, iter->first);
+    EXPECT_EQ(item5.value, iter->second.value);
+
+    ++iter;
+    ASSERT_TRUE(iter != cache1.end());
+    EXPECT_EQ(kItem4Key, iter->first);
+    EXPECT_EQ(item4.value, iter->second.value);
+
+    ++iter;
+    ASSERT_TRUE(iter != cache1.end());
+    EXPECT_EQ(kItem3Key, iter->first);
+    EXPECT_EQ(item3.value, iter->second.value);
+  }
+
+  // Verify cache2's elements.
+  {
+    Cache::iterator iter = cache2.begin();
+    ASSERT_TRUE(iter != cache2.end());
+    EXPECT_EQ(kItem2Key, iter->first);
+    EXPECT_EQ(item2.value, iter->second.value);
+
+    ++iter;
+    ASSERT_TRUE(iter != cache2.end());
+    EXPECT_EQ(kItem1Key, iter->first);
+    EXPECT_EQ(item1.value, iter->second.value);
+  }
+}
+
+TEST(MRUCacheTest, EstimateMemory) {
+  base::MRUCache<std::string, int> cache(10);
+
+  const std::string key(100u, 'a');
+  cache.Put(key, 1);
+
+  EXPECT_GT(trace_event::EstimateMemoryUsage(cache),
+            trace_event::EstimateMemoryUsage(key));
+}
+
+}  // namespace base
diff --git a/base/containers/small_map_unittest.cc b/base/containers/small_map_unittest.cc
new file mode 100644
index 0000000..6561851
--- /dev/null
+++ b/base/containers/small_map_unittest.cc
@@ -0,0 +1,603 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/containers/small_map.h"
+
+#include <stddef.h>
+
+#include <algorithm>
+#include <functional>
+#include <map>
+#include <unordered_map>
+
+#include "base/logging.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+TEST(SmallMap, General) {
+  small_map<std::unordered_map<int, int>> m;
+
+  EXPECT_TRUE(m.empty());
+
+  m[0] = 5;
+
+  EXPECT_FALSE(m.empty());
+  EXPECT_EQ(m.size(), 1u);
+
+  m[9] = 2;
+
+  EXPECT_FALSE(m.empty());
+  EXPECT_EQ(m.size(), 2u);
+
+  EXPECT_EQ(m[9], 2);
+  EXPECT_EQ(m[0], 5);
+  EXPECT_FALSE(m.UsingFullMap());
+
+  small_map<std::unordered_map<int, int>>::iterator iter(m.begin());
+  ASSERT_TRUE(iter != m.end());
+  EXPECT_EQ(iter->first, 0);
+  EXPECT_EQ(iter->second, 5);
+  ++iter;
+  ASSERT_TRUE(iter != m.end());
+  EXPECT_EQ((*iter).first, 9);
+  EXPECT_EQ((*iter).second, 2);
+  ++iter;
+  EXPECT_TRUE(iter == m.end());
+
+  m[8] = 23;
+  m[1234] = 90;
+  m[-5] = 6;
+
+  EXPECT_EQ(m[   9],  2);
+  EXPECT_EQ(m[   0],  5);
+  EXPECT_EQ(m[1234], 90);
+  EXPECT_EQ(m[   8], 23);
+  EXPECT_EQ(m[  -5],  6);
+  EXPECT_EQ(m.size(), 5u);
+  EXPECT_FALSE(m.empty());
+  EXPECT_TRUE(m.UsingFullMap());
+
+  iter = m.begin();
+  for (int i = 0; i < 5; i++) {
+    EXPECT_TRUE(iter != m.end());
+    ++iter;
+  }
+  EXPECT_TRUE(iter == m.end());
+
+  const small_map<std::unordered_map<int, int>>& ref = m;
+  EXPECT_TRUE(ref.find(1234) != m.end());
+  EXPECT_TRUE(ref.find(5678) == m.end());
+}
+
+TEST(SmallMap, PostFixIteratorIncrement) {
+  small_map<std::unordered_map<int, int>> m;
+  m[0] = 5;
+  m[2] = 3;
+
+  {
+    small_map<std::unordered_map<int, int>>::iterator iter(m.begin());
+    small_map<std::unordered_map<int, int>>::iterator last(iter++);
+    ++last;
+    EXPECT_TRUE(last == iter);
+  }
+
+  {
+    small_map<std::unordered_map<int, int>>::const_iterator iter(m.begin());
+    small_map<std::unordered_map<int, int>>::const_iterator last(iter++);
+    ++last;
+    EXPECT_TRUE(last == iter);
+  }
+}
+
+// Based on the General testcase.
+TEST(SmallMap, CopyConstructor) {
+  small_map<std::unordered_map<int, int>> src;
+
+  {
+    small_map<std::unordered_map<int, int>> m(src);
+    EXPECT_TRUE(m.empty());
+  }
+
+  src[0] = 5;
+
+  {
+    small_map<std::unordered_map<int, int>> m(src);
+    EXPECT_FALSE(m.empty());
+    EXPECT_EQ(m.size(), 1u);
+  }
+
+  src[9] = 2;
+
+  {
+    small_map<std::unordered_map<int, int>> m(src);
+    EXPECT_FALSE(m.empty());
+    EXPECT_EQ(m.size(), 2u);
+
+    EXPECT_EQ(m[9], 2);
+    EXPECT_EQ(m[0], 5);
+    EXPECT_FALSE(m.UsingFullMap());
+  }
+
+  src[8] = 23;
+  src[1234] = 90;
+  src[-5] = 6;
+
+  {
+    small_map<std::unordered_map<int, int>> m(src);
+    EXPECT_EQ(m[   9],  2);
+    EXPECT_EQ(m[   0],  5);
+    EXPECT_EQ(m[1234], 90);
+    EXPECT_EQ(m[   8], 23);
+    EXPECT_EQ(m[  -5],  6);
+    EXPECT_EQ(m.size(), 5u);
+    EXPECT_FALSE(m.empty());
+    EXPECT_TRUE(m.UsingFullMap());
+  }
+}
+
+template <class inner>
+static bool SmallMapIsSubset(small_map<inner> const& a,
+                             small_map<inner> const& b) {
+  typename small_map<inner>::const_iterator it;
+  for (it = a.begin(); it != a.end(); ++it) {
+    typename small_map<inner>::const_iterator it_in_b = b.find(it->first);
+    if (it_in_b == b.end() || it_in_b->second != it->second)
+      return false;
+  }
+  return true;
+}
+
+template <class inner>
+static bool SmallMapEqual(small_map<inner> const& a,
+                          small_map<inner> const& b) {
+  return SmallMapIsSubset(a, b) && SmallMapIsSubset(b, a);
+}
+
+TEST(SmallMap, AssignmentOperator) {
+  small_map<std::unordered_map<int, int>> src_small;
+  small_map<std::unordered_map<int, int>> src_large;
+
+  src_small[1] = 20;
+  src_small[2] = 21;
+  src_small[3] = 22;
+  EXPECT_FALSE(src_small.UsingFullMap());
+
+  src_large[1] = 20;
+  src_large[2] = 21;
+  src_large[3] = 22;
+  src_large[5] = 23;
+  src_large[6] = 24;
+  src_large[7] = 25;
+  EXPECT_TRUE(src_large.UsingFullMap());
+
+  // Assignments to empty.
+  small_map<std::unordered_map<int, int>> dest_small;
+  dest_small = src_small;
+  EXPECT_TRUE(SmallMapEqual(dest_small, src_small));
+  EXPECT_EQ(dest_small.UsingFullMap(),
+            src_small.UsingFullMap());
+
+  small_map<std::unordered_map<int, int>> dest_large;
+  dest_large = src_large;
+  EXPECT_TRUE(SmallMapEqual(dest_large, src_large));
+  EXPECT_EQ(dest_large.UsingFullMap(),
+            src_large.UsingFullMap());
+
+  // Assignments which assign from full to small, and vice versa.
+  dest_small = src_large;
+  EXPECT_TRUE(SmallMapEqual(dest_small, src_large));
+  EXPECT_EQ(dest_small.UsingFullMap(),
+            src_large.UsingFullMap());
+
+  dest_large = src_small;
+  EXPECT_TRUE(SmallMapEqual(dest_large, src_small));
+  EXPECT_EQ(dest_large.UsingFullMap(),
+            src_small.UsingFullMap());
+
+  // Double check that SmallMapEqual works:
+  dest_large[42] = 666;
+  EXPECT_FALSE(SmallMapEqual(dest_large, src_small));
+}
+
+TEST(SmallMap, Insert) {
+  small_map<std::unordered_map<int, int>> sm;
+
+  // loop through the transition from small map to map.
+  for (int i = 1; i <= 10; ++i) {
+    VLOG(1) << "Iteration " << i;
+    // insert an element
+    std::pair<small_map<std::unordered_map<int, int>>::iterator, bool> ret;
+    ret = sm.insert(std::make_pair(i, 100*i));
+    EXPECT_TRUE(ret.second);
+    EXPECT_TRUE(ret.first == sm.find(i));
+    EXPECT_EQ(ret.first->first, i);
+    EXPECT_EQ(ret.first->second, 100*i);
+
+    // try to insert it again with different value, fails, but we still get an
+    // iterator back with the original value.
+    ret = sm.insert(std::make_pair(i, -i));
+    EXPECT_FALSE(ret.second);
+    EXPECT_TRUE(ret.first == sm.find(i));
+    EXPECT_EQ(ret.first->first, i);
+    EXPECT_EQ(ret.first->second, 100*i);
+
+    // check the state of the map.
+    for (int j = 1; j <= i; ++j) {
+      small_map<std::unordered_map<int, int>>::iterator it = sm.find(j);
+      EXPECT_TRUE(it != sm.end());
+      EXPECT_EQ(it->first, j);
+      EXPECT_EQ(it->second, j * 100);
+    }
+    EXPECT_EQ(sm.size(), static_cast<size_t>(i));
+    EXPECT_FALSE(sm.empty());
+  }
+}
+
+TEST(SmallMap, InsertRange) {
+  // loop through the transition from small map to map.
+  for (int elements = 0; elements <= 10; ++elements) {
+    VLOG(1) << "Elements " << elements;
+    std::unordered_map<int, int> normal_map;
+    for (int i = 1; i <= elements; ++i) {
+      normal_map.insert(std::make_pair(i, 100*i));
+    }
+
+    small_map<std::unordered_map<int, int>> sm;
+    sm.insert(normal_map.begin(), normal_map.end());
+    EXPECT_EQ(normal_map.size(), sm.size());
+    for (int i = 1; i <= elements; ++i) {
+      VLOG(1) << "Iteration " << i;
+      EXPECT_TRUE(sm.find(i) != sm.end());
+      EXPECT_EQ(sm.find(i)->first, i);
+      EXPECT_EQ(sm.find(i)->second, 100*i);
+    }
+  }
+}
+
+TEST(SmallMap, Erase) {
+  small_map<std::unordered_map<std::string, int>> m;
+  small_map<std::unordered_map<std::string, int>>::iterator iter;
+
+  m["monday"] = 1;
+  m["tuesday"] = 2;
+  m["wednesday"] = 3;
+
+  EXPECT_EQ(m["monday"   ], 1);
+  EXPECT_EQ(m["tuesday"  ], 2);
+  EXPECT_EQ(m["wednesday"], 3);
+  EXPECT_EQ(m.count("tuesday"), 1u);
+  EXPECT_FALSE(m.UsingFullMap());
+
+  iter = m.begin();
+  ASSERT_TRUE(iter != m.end());
+  EXPECT_EQ(iter->first, "monday");
+  EXPECT_EQ(iter->second, 1);
+  ++iter;
+  ASSERT_TRUE(iter != m.end());
+  EXPECT_EQ(iter->first, "tuesday");
+  EXPECT_EQ(iter->second, 2);
+  ++iter;
+  ASSERT_TRUE(iter != m.end());
+  EXPECT_EQ(iter->first, "wednesday");
+  EXPECT_EQ(iter->second, 3);
+  ++iter;
+  EXPECT_TRUE(iter == m.end());
+
+  EXPECT_EQ(m.erase("tuesday"), 1u);
+
+  EXPECT_EQ(m["monday"   ], 1);
+  EXPECT_EQ(m["wednesday"], 3);
+  EXPECT_EQ(m.count("tuesday"), 0u);
+  EXPECT_EQ(m.erase("tuesday"), 0u);
+
+  iter = m.begin();
+  ASSERT_TRUE(iter != m.end());
+  EXPECT_EQ(iter->first, "monday");
+  EXPECT_EQ(iter->second, 1);
+  ++iter;
+  ASSERT_TRUE(iter != m.end());
+  EXPECT_EQ(iter->first, "wednesday");
+  EXPECT_EQ(iter->second, 3);
+  ++iter;
+  EXPECT_TRUE(iter == m.end());
+
+  m["thursday"] = 4;
+  m["friday"] = 5;
+  EXPECT_EQ(m.size(), 4u);
+  EXPECT_FALSE(m.empty());
+  EXPECT_FALSE(m.UsingFullMap());
+
+  m["saturday"] = 6;
+  EXPECT_TRUE(m.UsingFullMap());
+
+  EXPECT_EQ(m.count("friday"), 1u);
+  EXPECT_EQ(m.erase("friday"), 1u);
+  EXPECT_TRUE(m.UsingFullMap());
+  EXPECT_EQ(m.count("friday"), 0u);
+  EXPECT_EQ(m.erase("friday"), 0u);
+
+  EXPECT_EQ(m.size(), 4u);
+  EXPECT_FALSE(m.empty());
+  EXPECT_EQ(m.erase("monday"), 1u);
+  EXPECT_EQ(m.size(), 3u);
+  EXPECT_FALSE(m.empty());
+
+  m.clear();
+  EXPECT_FALSE(m.UsingFullMap());
+  EXPECT_EQ(m.size(), 0u);
+  EXPECT_TRUE(m.empty());
+}
+
+TEST(SmallMap, EraseReturnsIteratorFollowingRemovedElement) {
+  small_map<std::unordered_map<std::string, int>> m;
+  small_map<std::unordered_map<std::string, int>>::iterator iter;
+
+  m["a"] = 0;
+  m["b"] = 1;
+  m["c"] = 2;
+
+  // Erase first item.
+  auto following_iter = m.erase(m.begin());
+  EXPECT_EQ(m.begin(), following_iter);
+  EXPECT_EQ(2u, m.size());
+  EXPECT_EQ(m.count("a"), 0u);
+  EXPECT_EQ(m.count("b"), 1u);
+  EXPECT_EQ(m.count("c"), 1u);
+
+  // Iterate to last item and erase it.
+  ++following_iter;
+  following_iter = m.erase(following_iter);
+  ASSERT_EQ(1u, m.size());
+  EXPECT_EQ(m.end(), following_iter);
+  EXPECT_EQ(m.count("b"), 0u);
+  EXPECT_EQ(m.count("c"), 1u);
+
+  // Erase remaining item.
+  following_iter = m.erase(m.begin());
+  EXPECT_TRUE(m.empty());
+  EXPECT_EQ(m.end(), following_iter);
+}
+
+TEST(SmallMap, NonHashMap) {
+  small_map<std::map<int, int>, 4, std::equal_to<int>> m;
+  EXPECT_TRUE(m.empty());
+
+  m[9] = 2;
+  m[0] = 5;
+
+  EXPECT_EQ(m[9], 2);
+  EXPECT_EQ(m[0], 5);
+  EXPECT_EQ(m.size(), 2u);
+  EXPECT_FALSE(m.empty());
+  EXPECT_FALSE(m.UsingFullMap());
+
+  small_map<std::map<int, int>, 4, std::equal_to<int>>::iterator iter(
+      m.begin());
+  ASSERT_TRUE(iter != m.end());
+  EXPECT_EQ(iter->first, 9);
+  EXPECT_EQ(iter->second, 2);
+  ++iter;
+  ASSERT_TRUE(iter != m.end());
+  EXPECT_EQ(iter->first, 0);
+  EXPECT_EQ(iter->second, 5);
+  ++iter;
+  EXPECT_TRUE(iter == m.end());
+  --iter;
+  ASSERT_TRUE(iter != m.end());
+  EXPECT_EQ(iter->first, 0);
+  EXPECT_EQ(iter->second, 5);
+
+  m[8] = 23;
+  m[1234] = 90;
+  m[-5] = 6;
+
+  EXPECT_EQ(m[   9],  2);
+  EXPECT_EQ(m[   0],  5);
+  EXPECT_EQ(m[1234], 90);
+  EXPECT_EQ(m[   8], 23);
+  EXPECT_EQ(m[  -5],  6);
+  EXPECT_EQ(m.size(), 5u);
+  EXPECT_FALSE(m.empty());
+  EXPECT_TRUE(m.UsingFullMap());
+
+  iter = m.begin();
+  ASSERT_TRUE(iter != m.end());
+  EXPECT_EQ(iter->first, -5);
+  EXPECT_EQ(iter->second, 6);
+  ++iter;
+  ASSERT_TRUE(iter != m.end());
+  EXPECT_EQ(iter->first, 0);
+  EXPECT_EQ(iter->second, 5);
+  ++iter;
+  ASSERT_TRUE(iter != m.end());
+  EXPECT_EQ(iter->first, 8);
+  EXPECT_EQ(iter->second, 23);
+  ++iter;
+  ASSERT_TRUE(iter != m.end());
+  EXPECT_EQ(iter->first, 9);
+  EXPECT_EQ(iter->second, 2);
+  ++iter;
+  ASSERT_TRUE(iter != m.end());
+  EXPECT_EQ(iter->first, 1234);
+  EXPECT_EQ(iter->second, 90);
+  ++iter;
+  EXPECT_TRUE(iter == m.end());
+  --iter;
+  ASSERT_TRUE(iter != m.end());
+  EXPECT_EQ(iter->first, 1234);
+  EXPECT_EQ(iter->second, 90);
+}
+
+TEST(SmallMap, DefaultEqualKeyWorks) {
+  // If these tests compile, they pass. The EXPECT calls are only there to avoid
+  // unused variable warnings.
+  small_map<std::unordered_map<int, int>> hm;
+  EXPECT_EQ(0u, hm.size());
+  small_map<std::map<int, int>> m;
+  EXPECT_EQ(0u, m.size());
+}
+
+namespace {
+
+class unordered_map_add_item : public std::unordered_map<int, int> {
+ public:
+  unordered_map_add_item() = default;
+  explicit unordered_map_add_item(const std::pair<int, int>& item) {
+    insert(item);
+  }
+};
+
+void InitMap(unordered_map_add_item* map_ctor) {
+  new (map_ctor) unordered_map_add_item(std::make_pair(0, 0));
+}
+
+class unordered_map_add_item_initializer {
+ public:
+  explicit unordered_map_add_item_initializer(int item_to_add)
+      : item_(item_to_add) {}
+  unordered_map_add_item_initializer() : item_(0) {}
+  void operator()(unordered_map_add_item* map_ctor) const {
+    new (map_ctor) unordered_map_add_item(std::make_pair(item_, item_));
+  }
+
+  int item_;
+};
+
+}  // anonymous namespace
+
+TEST(SmallMap, SubclassInitializationWithFunctionPointer) {
+  small_map<unordered_map_add_item, 4, std::equal_to<int>,
+            void (&)(unordered_map_add_item*)>
+      m(InitMap);
+
+  EXPECT_TRUE(m.empty());
+
+  m[1] = 1;
+  m[2] = 2;
+  m[3] = 3;
+  m[4] = 4;
+
+  EXPECT_EQ(4u, m.size());
+  EXPECT_EQ(0u, m.count(0));
+
+  m[5] = 5;
+  EXPECT_EQ(6u, m.size());
+  // Our function adds an extra item when we convert to a map.
+  EXPECT_EQ(1u, m.count(0));
+}
+
+TEST(SmallMap, SubclassInitializationWithFunctionObject) {
+  small_map<unordered_map_add_item, 4, std::equal_to<int>,
+            unordered_map_add_item_initializer>
+      m(unordered_map_add_item_initializer(-1));
+
+  EXPECT_TRUE(m.empty());
+
+  m[1] = 1;
+  m[2] = 2;
+  m[3] = 3;
+  m[4] = 4;
+
+  EXPECT_EQ(4u, m.size());
+  EXPECT_EQ(0u, m.count(-1));
+
+  m[5] = 5;
+  EXPECT_EQ(6u, m.size());
+  // Our functor adds an extra item when we convert to a map.
+  EXPECT_EQ(1u, m.count(-1));
+}
+
+namespace {
+
+// This class acts as a basic implementation of a move-only type. The canonical
+// example of such a type is scoped_ptr/unique_ptr.
+template <typename V>
+class MoveOnlyType {
+ public:
+  MoveOnlyType() : value_(0) {}
+  explicit MoveOnlyType(V value) : value_(value) {}
+
+  MoveOnlyType(MoveOnlyType&& other) {
+    *this = std::move(other);
+  }
+
+  MoveOnlyType& operator=(MoveOnlyType&& other) {
+    value_ = other.value_;
+    other.value_ = 0;
+    return *this;
+  }
+
+  MoveOnlyType(const MoveOnlyType&) = delete;
+  MoveOnlyType& operator=(const MoveOnlyType&) = delete;
+
+  V value() const { return value_; }
+
+ private:
+  V value_;
+};
+
+}  // namespace
+
+TEST(SmallMap, MoveOnlyValueType) {
+  small_map<std::map<int, MoveOnlyType<int>>, 2> m;
+
+  m[0] = MoveOnlyType<int>(1);
+  m[1] = MoveOnlyType<int>(2);
+  m.erase(m.begin());
+
+  // small_map will move m[1] to an earlier index in the internal array.
+  EXPECT_EQ(m.size(), 1u);
+  EXPECT_EQ(m[1].value(), 2);
+
+  m[0] = MoveOnlyType<int>(1);
+  // small_map must move the values from the array into the internal std::map.
+  m[2] = MoveOnlyType<int>(3);
+
+  EXPECT_EQ(m.size(), 3u);
+  EXPECT_EQ(m[0].value(), 1);
+  EXPECT_EQ(m[1].value(), 2);
+  EXPECT_EQ(m[2].value(), 3);
+
+  m.erase(m.begin());
+
+  // small_map should also let internal std::map erase with a move-only type.
+  EXPECT_EQ(m.size(), 2u);
+  EXPECT_EQ(m[1].value(), 2);
+  EXPECT_EQ(m[2].value(), 3);
+}
+
+TEST(SmallMap, Emplace) {
+  small_map<std::map<size_t, MoveOnlyType<size_t>>> sm;
+
+  // loop through the transition from small map to map.
+  for (size_t i = 1; i <= 10; ++i) {
+    // insert an element
+    auto ret = sm.emplace(i, MoveOnlyType<size_t>(100 * i));
+    EXPECT_TRUE(ret.second);
+    EXPECT_TRUE(ret.first == sm.find(i));
+    EXPECT_EQ(ret.first->first, i);
+    EXPECT_EQ(ret.first->second.value(), 100 * i);
+
+    // try to insert it again with different value, fails, but we still get an
+    // iterator back with the original value.
+    ret = sm.emplace(i, MoveOnlyType<size_t>(i));
+    EXPECT_FALSE(ret.second);
+    EXPECT_TRUE(ret.first == sm.find(i));
+    EXPECT_EQ(ret.first->first, i);
+    EXPECT_EQ(ret.first->second.value(), 100 * i);
+
+    // check the state of the map.
+    for (size_t j = 1; j <= i; ++j) {
+      const auto it = sm.find(j);
+      EXPECT_TRUE(it != sm.end());
+      EXPECT_EQ(it->first, j);
+      EXPECT_EQ(it->second.value(), j * 100);
+    }
+    EXPECT_EQ(sm.size(), i);
+    EXPECT_FALSE(sm.empty());
+  }
+}
+
+}  // namespace base
diff --git a/base/containers/stack_container_unittest.cc b/base/containers/stack_container_unittest.cc
new file mode 100644
index 0000000..b6bb9b6
--- /dev/null
+++ b/base/containers/stack_container_unittest.cc
@@ -0,0 +1,145 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/containers/stack_container.h"
+
+#include <stddef.h>
+
+#include <algorithm>
+
+#include "base/memory/ref_counted.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+namespace {
+
+class Dummy : public base::RefCounted<Dummy> {
+ public:
+  explicit Dummy(int* alive) : alive_(alive) {
+    ++*alive_;
+  }
+
+ private:
+  friend class base::RefCounted<Dummy>;
+
+  ~Dummy() {
+    --*alive_;
+  }
+
+  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(static_cast<size_t>(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 = nullptr;
+  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.
+}
+
+namespace {
+
+template <size_t alignment>
+class AlignedData {
+ public:
+  AlignedData() { memset(data_, 0, alignment); }
+  ~AlignedData() = default;
+  alignas(alignment) char data_[alignment];
+};
+
+}  // anonymous namespace
+
+#define EXPECT_ALIGNED(ptr, align) \
+    EXPECT_EQ(0u, reinterpret_cast<uintptr_t>(ptr) & (align - 1))
+
+TEST(StackContainer, BufferAlignment) {
+  StackVector<wchar_t, 16> text;
+  text->push_back(L'A');
+  EXPECT_ALIGNED(&text[0], alignof(wchar_t));
+
+  StackVector<double, 1> doubles;
+  doubles->push_back(0.0);
+  EXPECT_ALIGNED(&doubles[0], alignof(double));
+
+  StackVector<AlignedData<16>, 1> aligned16;
+  aligned16->push_back(AlignedData<16>());
+  EXPECT_ALIGNED(&aligned16[0], 16);
+
+#if !defined(__GNUC__) || defined(ARCH_CPU_X86_FAMILY)
+  // It seems that non-X86 gcc doesn't respect greater than 16 byte alignment.
+  // See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=33721 for details.
+  // TODO(sbc):re-enable this if GCC starts respecting higher alignments.
+  StackVector<AlignedData<256>, 1> aligned256;
+  aligned256->push_back(AlignedData<256>());
+  EXPECT_ALIGNED(&aligned256[0], 256);
+#endif
+}
+
+template class StackVector<int, 2>;
+template class StackVector<scoped_refptr<Dummy>, 2>;
+
+}  // namespace base
diff --git a/base/containers/unique_ptr_adapters.h b/base/containers/unique_ptr_adapters.h
new file mode 100644
index 0000000..42fab19
--- /dev/null
+++ b/base/containers/unique_ptr_adapters.h
@@ -0,0 +1,78 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_CONTAINERS_UNIQUE_PTR_ADAPTERS_H_
+#define BASE_CONTAINERS_UNIQUE_PTR_ADAPTERS_H_
+
+#include <memory>
+
+namespace base {
+
+// This transparent comparator allows to lookup by raw pointer in
+// a container of unique pointers. This functionality is based on C++14
+// extensions to std::set/std::map interface, and can also be used
+// with base::flat_set/base::flat_map.
+//
+// Example usage:
+//   Foo* foo = ...
+//   std::set<std::unique_ptr<Foo>, base::UniquePtrComparator> set;
+//   set.insert(std::unique_ptr<Foo>(foo));
+//   ...
+//   auto it = set.find(foo);
+//   EXPECT_EQ(foo, it->get());
+//
+// You can find more information about transparent comparisons here:
+// http://en.cppreference.com/w/cpp/utility/functional/less_void
+struct UniquePtrComparator {
+  using is_transparent = int;
+
+  template <typename T>
+  bool operator()(const std::unique_ptr<T>& lhs,
+                  const std::unique_ptr<T>& rhs) const {
+    return lhs < rhs;
+  }
+
+  template <typename T>
+  bool operator()(const T* lhs, const std::unique_ptr<T>& rhs) const {
+    return lhs < rhs.get();
+  }
+
+  template <typename T>
+  bool operator()(const std::unique_ptr<T>& lhs, const T* rhs) const {
+    return lhs.get() < rhs;
+  }
+};
+
+// UniquePtrMatcher is useful for finding an element in a container of
+// unique_ptrs when you have the raw pointer.
+//
+// Example usage:
+//   std::vector<std::unique_ptr<Foo>> vector;
+//   Foo* element = ...
+//   auto iter = std::find_if(vector.begin(), vector.end(),
+//                            MatchesUniquePtr(element));
+//
+// Example of erasing from container:
+//   EraseIf(v, MatchesUniquePtr(element));
+//
+template <class T, class Deleter = std::default_delete<T>>
+struct UniquePtrMatcher {
+  explicit UniquePtrMatcher(T* t) : t_(t) {}
+
+  bool operator()(const std::unique_ptr<T, Deleter>& o) {
+    return o.get() == t_;
+  }
+
+ private:
+  T* const t_;
+};
+
+template <class T, class Deleter = std::default_delete<T>>
+UniquePtrMatcher<T, Deleter> MatchesUniquePtr(T* t) {
+  return UniquePtrMatcher<T, Deleter>(t);
+}
+
+}  // namespace base
+
+#endif  // BASE_CONTAINERS_UNIQUE_PTR_ADAPTERS_H_
diff --git a/base/containers/unique_ptr_adapters_unittest.cc b/base/containers/unique_ptr_adapters_unittest.cc
new file mode 100644
index 0000000..5b8f1fc
--- /dev/null
+++ b/base/containers/unique_ptr_adapters_unittest.cc
@@ -0,0 +1,134 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/containers/unique_ptr_adapters.h"
+
+#include <memory>
+#include <vector>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace {
+
+class Foo {
+ public:
+  Foo() { instance_count++; }
+  ~Foo() { instance_count--; }
+  static int instance_count;
+};
+
+int Foo::instance_count = 0;
+
+TEST(UniquePtrComparatorTest, Basic) {
+  std::set<std::unique_ptr<Foo>, UniquePtrComparator> set;
+  Foo* foo1 = new Foo();
+  Foo* foo2 = new Foo();
+  Foo* foo3 = new Foo();
+  EXPECT_EQ(3, Foo::instance_count);
+
+  set.emplace(foo1);
+  set.emplace(foo2);
+
+  auto it1 = set.find(foo1);
+  EXPECT_TRUE(it1 != set.end());
+  EXPECT_EQ(foo1, it1->get());
+
+  {
+    auto it2 = set.find(foo2);
+    EXPECT_TRUE(it2 != set.end());
+    EXPECT_EQ(foo2, it2->get());
+  }
+
+  EXPECT_TRUE(set.find(foo3) == set.end());
+
+  set.erase(it1);
+  EXPECT_EQ(2, Foo::instance_count);
+
+  EXPECT_TRUE(set.find(foo1) == set.end());
+
+  {
+    auto it2 = set.find(foo2);
+    EXPECT_TRUE(it2 != set.end());
+    EXPECT_EQ(foo2, it2->get());
+  }
+
+  set.clear();
+  EXPECT_EQ(1, Foo::instance_count);
+
+  EXPECT_TRUE(set.find(foo1) == set.end());
+  EXPECT_TRUE(set.find(foo2) == set.end());
+  EXPECT_TRUE(set.find(foo3) == set.end());
+
+  delete foo3;
+  EXPECT_EQ(0, Foo::instance_count);
+}
+
+TEST(UniquePtrMatcherTest, Basic) {
+  std::vector<std::unique_ptr<Foo>> v;
+  auto foo_ptr1 = std::make_unique<Foo>();
+  Foo* foo1 = foo_ptr1.get();
+  v.push_back(std::move(foo_ptr1));
+  auto foo_ptr2 = std::make_unique<Foo>();
+  Foo* foo2 = foo_ptr2.get();
+  v.push_back(std::move(foo_ptr2));
+
+  {
+    auto iter = std::find_if(v.begin(), v.end(), UniquePtrMatcher<Foo>(foo1));
+    ASSERT_TRUE(iter != v.end());
+    EXPECT_EQ(foo1, iter->get());
+  }
+
+  {
+    auto iter = std::find_if(v.begin(), v.end(), UniquePtrMatcher<Foo>(foo2));
+    ASSERT_TRUE(iter != v.end());
+    EXPECT_EQ(foo2, iter->get());
+  }
+
+  {
+    auto iter = std::find_if(v.begin(), v.end(), MatchesUniquePtr(foo2));
+    ASSERT_TRUE(iter != v.end());
+    EXPECT_EQ(foo2, iter->get());
+  }
+}
+
+class TestDeleter {
+ public:
+  void operator()(Foo* foo) { delete foo; }
+};
+
+TEST(UniquePtrMatcherTest, Deleter) {
+  using UniqueFoo = std::unique_ptr<Foo, TestDeleter>;
+  std::vector<UniqueFoo> v;
+  UniqueFoo foo_ptr1(new Foo);
+  Foo* foo1 = foo_ptr1.get();
+  v.push_back(std::move(foo_ptr1));
+  UniqueFoo foo_ptr2(new Foo);
+  Foo* foo2 = foo_ptr2.get();
+  v.push_back(std::move(foo_ptr2));
+
+  {
+    auto iter = std::find_if(v.begin(), v.end(),
+                             UniquePtrMatcher<Foo, TestDeleter>(foo1));
+    ASSERT_TRUE(iter != v.end());
+    EXPECT_EQ(foo1, iter->get());
+  }
+
+  {
+    auto iter = std::find_if(v.begin(), v.end(),
+                             UniquePtrMatcher<Foo, TestDeleter>(foo2));
+    ASSERT_TRUE(iter != v.end());
+    EXPECT_EQ(foo2, iter->get());
+  }
+
+  {
+    auto iter = std::find_if(v.begin(), v.end(),
+                             MatchesUniquePtr<Foo, TestDeleter>(foo2));
+    ASSERT_TRUE(iter != v.end());
+    EXPECT_EQ(foo2, iter->get());
+  }
+}
+
+}  // namespace
+}  // namespace base
diff --git a/base/debug/activity_analyzer.cc b/base/debug/activity_analyzer.cc
new file mode 100644
index 0000000..d787829
--- /dev/null
+++ b/base/debug/activity_analyzer.cc
@@ -0,0 +1,412 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/debug/activity_analyzer.h"
+
+#include <algorithm>
+#include <utility>
+
+#include "base/files/file.h"
+#include "base/files/file_path.h"
+#include "base/files/memory_mapped_file.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/stl_util.h"
+#include "base/strings/string_util.h"
+
+namespace base {
+namespace debug {
+
+namespace {
+// An empty snapshot that can be returned when there otherwise is none.
+LazyInstance<ActivityUserData::Snapshot>::Leaky g_empty_user_data_snapshot;
+
+// DO NOT CHANGE VALUES. This is logged persistently in a histogram.
+enum AnalyzerCreationError {
+  kInvalidMemoryMappedFile,
+  kPmaBadFile,
+  kPmaUninitialized,
+  kPmaDeleted,
+  kPmaCorrupt,
+  kAnalyzerCreationErrorMax  // Keep this last.
+};
+
+void LogAnalyzerCreationError(AnalyzerCreationError error) {
+  UMA_HISTOGRAM_ENUMERATION("ActivityTracker.Collect.AnalyzerCreationError",
+                            error, kAnalyzerCreationErrorMax);
+}
+
+}  // namespace
+
+ThreadActivityAnalyzer::Snapshot::Snapshot() = default;
+ThreadActivityAnalyzer::Snapshot::~Snapshot() = default;
+
+ThreadActivityAnalyzer::ThreadActivityAnalyzer(
+    const ThreadActivityTracker& tracker)
+    : activity_snapshot_valid_(tracker.CreateSnapshot(&activity_snapshot_)) {}
+
+ThreadActivityAnalyzer::ThreadActivityAnalyzer(void* base, size_t size)
+    : ThreadActivityAnalyzer(ThreadActivityTracker(base, size)) {}
+
+ThreadActivityAnalyzer::ThreadActivityAnalyzer(
+    PersistentMemoryAllocator* allocator,
+    PersistentMemoryAllocator::Reference reference)
+    : ThreadActivityAnalyzer(allocator->GetAsArray<char>(
+                                 reference,
+                                 GlobalActivityTracker::kTypeIdActivityTracker,
+                                 PersistentMemoryAllocator::kSizeAny),
+                             allocator->GetAllocSize(reference)) {}
+
+ThreadActivityAnalyzer::~ThreadActivityAnalyzer() = default;
+
+void ThreadActivityAnalyzer::AddGlobalInformation(
+    GlobalActivityAnalyzer* global) {
+  if (!IsValid())
+    return;
+
+  // User-data is held at the global scope even though it's referenced at the
+  // thread scope.
+  activity_snapshot_.user_data_stack.clear();
+  for (auto& activity : activity_snapshot_.activity_stack) {
+    // The global GetUserDataSnapshot will return an empty snapshot if the ref
+    // or id is not valid.
+    activity_snapshot_.user_data_stack.push_back(global->GetUserDataSnapshot(
+        activity_snapshot_.process_id, activity.user_data_ref,
+        activity.user_data_id));
+  }
+}
+
+GlobalActivityAnalyzer::GlobalActivityAnalyzer(
+    std::unique_ptr<PersistentMemoryAllocator> allocator)
+    : allocator_(std::move(allocator)),
+      analysis_stamp_(0LL),
+      allocator_iterator_(allocator_.get()) {
+  DCHECK(allocator_);
+}
+
+GlobalActivityAnalyzer::~GlobalActivityAnalyzer() = default;
+
+// static
+std::unique_ptr<GlobalActivityAnalyzer>
+GlobalActivityAnalyzer::CreateWithAllocator(
+    std::unique_ptr<PersistentMemoryAllocator> allocator) {
+  if (allocator->GetMemoryState() ==
+      PersistentMemoryAllocator::MEMORY_UNINITIALIZED) {
+    LogAnalyzerCreationError(kPmaUninitialized);
+    return nullptr;
+  }
+  if (allocator->GetMemoryState() ==
+      PersistentMemoryAllocator::MEMORY_DELETED) {
+    LogAnalyzerCreationError(kPmaDeleted);
+    return nullptr;
+  }
+  if (allocator->IsCorrupt()) {
+    LogAnalyzerCreationError(kPmaCorrupt);
+    return nullptr;
+  }
+
+  return WrapUnique(new GlobalActivityAnalyzer(std::move(allocator)));
+}
+
+#if !defined(OS_NACL)
+// static
+std::unique_ptr<GlobalActivityAnalyzer> GlobalActivityAnalyzer::CreateWithFile(
+    const FilePath& file_path) {
+  // Map the file read-write so it can guarantee consistency between
+  // the analyzer and any trackers that my still be active.
+  std::unique_ptr<MemoryMappedFile> mmfile(new MemoryMappedFile());
+  mmfile->Initialize(file_path, MemoryMappedFile::READ_WRITE);
+  if (!mmfile->IsValid()) {
+    LogAnalyzerCreationError(kInvalidMemoryMappedFile);
+    return nullptr;
+  }
+
+  if (!FilePersistentMemoryAllocator::IsFileAcceptable(*mmfile, true)) {
+    LogAnalyzerCreationError(kPmaBadFile);
+    return nullptr;
+  }
+
+  return CreateWithAllocator(std::make_unique<FilePersistentMemoryAllocator>(
+      std::move(mmfile), 0, 0, StringPiece(), /*readonly=*/true));
+}
+#endif  // !defined(OS_NACL)
+
+// static
+std::unique_ptr<GlobalActivityAnalyzer>
+GlobalActivityAnalyzer::CreateWithSharedMemory(
+    std::unique_ptr<SharedMemory> shm) {
+  if (shm->mapped_size() == 0 ||
+      !SharedPersistentMemoryAllocator::IsSharedMemoryAcceptable(*shm)) {
+    return nullptr;
+  }
+  return CreateWithAllocator(std::make_unique<SharedPersistentMemoryAllocator>(
+      std::move(shm), 0, StringPiece(), /*readonly=*/true));
+}
+
+// static
+std::unique_ptr<GlobalActivityAnalyzer>
+GlobalActivityAnalyzer::CreateWithSharedMemoryHandle(
+    const SharedMemoryHandle& handle,
+    size_t size) {
+  std::unique_ptr<SharedMemory> shm(
+      new SharedMemory(handle, /*readonly=*/true));
+  if (!shm->Map(size))
+    return nullptr;
+  return CreateWithSharedMemory(std::move(shm));
+}
+
+int64_t GlobalActivityAnalyzer::GetFirstProcess() {
+  PrepareAllAnalyzers();
+  return GetNextProcess();
+}
+
+int64_t GlobalActivityAnalyzer::GetNextProcess() {
+  if (process_ids_.empty())
+    return 0;
+  int64_t pid = process_ids_.back();
+  process_ids_.pop_back();
+  return pid;
+}
+
+ThreadActivityAnalyzer* GlobalActivityAnalyzer::GetFirstAnalyzer(int64_t pid) {
+  analyzers_iterator_ = analyzers_.begin();
+  analyzers_iterator_pid_ = pid;
+  if (analyzers_iterator_ == analyzers_.end())
+    return nullptr;
+  int64_t create_stamp;
+  if (analyzers_iterator_->second->GetProcessId(&create_stamp) == pid &&
+      create_stamp <= analysis_stamp_) {
+    return analyzers_iterator_->second.get();
+  }
+  return GetNextAnalyzer();
+}
+
+ThreadActivityAnalyzer* GlobalActivityAnalyzer::GetNextAnalyzer() {
+  DCHECK(analyzers_iterator_ != analyzers_.end());
+  int64_t create_stamp;
+  do {
+    ++analyzers_iterator_;
+    if (analyzers_iterator_ == analyzers_.end())
+      return nullptr;
+  } while (analyzers_iterator_->second->GetProcessId(&create_stamp) !=
+               analyzers_iterator_pid_ ||
+           create_stamp > analysis_stamp_);
+  return analyzers_iterator_->second.get();
+}
+
+ThreadActivityAnalyzer* GlobalActivityAnalyzer::GetAnalyzerForThread(
+    const ThreadKey& key) {
+  auto found = analyzers_.find(key);
+  if (found == analyzers_.end())
+    return nullptr;
+  return found->second.get();
+}
+
+ActivityUserData::Snapshot GlobalActivityAnalyzer::GetUserDataSnapshot(
+    int64_t pid,
+    uint32_t ref,
+    uint32_t id) {
+  ActivityUserData::Snapshot snapshot;
+
+  void* memory = allocator_->GetAsArray<char>(
+      ref, GlobalActivityTracker::kTypeIdUserDataRecord,
+      PersistentMemoryAllocator::kSizeAny);
+  if (memory) {
+    size_t size = allocator_->GetAllocSize(ref);
+    const ActivityUserData user_data(memory, size);
+    user_data.CreateSnapshot(&snapshot);
+    int64_t process_id;
+    int64_t create_stamp;
+    if (!ActivityUserData::GetOwningProcessId(memory, &process_id,
+                                              &create_stamp) ||
+        process_id != pid || user_data.id() != id) {
+      // This allocation has been overwritten since it was created. Return an
+      // empty snapshot because whatever was captured is incorrect.
+      snapshot.clear();
+    }
+  }
+
+  return snapshot;
+}
+
+const ActivityUserData::Snapshot&
+GlobalActivityAnalyzer::GetProcessDataSnapshot(int64_t pid) {
+  auto iter = process_data_.find(pid);
+  if (iter == process_data_.end())
+    return g_empty_user_data_snapshot.Get();
+  if (iter->second.create_stamp > analysis_stamp_)
+    return g_empty_user_data_snapshot.Get();
+  DCHECK_EQ(pid, iter->second.process_id);
+  return iter->second.data;
+}
+
+std::vector<std::string> GlobalActivityAnalyzer::GetLogMessages() {
+  std::vector<std::string> messages;
+  PersistentMemoryAllocator::Reference ref;
+
+  PersistentMemoryAllocator::Iterator iter(allocator_.get());
+  while ((ref = iter.GetNextOfType(
+              GlobalActivityTracker::kTypeIdGlobalLogMessage)) != 0) {
+    const char* message = allocator_->GetAsArray<char>(
+        ref, GlobalActivityTracker::kTypeIdGlobalLogMessage,
+        PersistentMemoryAllocator::kSizeAny);
+    if (message)
+      messages.push_back(message);
+  }
+
+  return messages;
+}
+
+std::vector<GlobalActivityTracker::ModuleInfo>
+GlobalActivityAnalyzer::GetModules(int64_t pid) {
+  std::vector<GlobalActivityTracker::ModuleInfo> modules;
+
+  PersistentMemoryAllocator::Iterator iter(allocator_.get());
+  const GlobalActivityTracker::ModuleInfoRecord* record;
+  while (
+      (record =
+           iter.GetNextOfObject<GlobalActivityTracker::ModuleInfoRecord>()) !=
+      nullptr) {
+    int64_t process_id;
+    int64_t create_stamp;
+    if (!OwningProcess::GetOwningProcessId(&record->owner, &process_id,
+                                           &create_stamp) ||
+        pid != process_id || create_stamp > analysis_stamp_) {
+      continue;
+    }
+    GlobalActivityTracker::ModuleInfo info;
+    if (record->DecodeTo(&info, allocator_->GetAllocSize(
+                                    allocator_->GetAsReference(record)))) {
+      modules.push_back(std::move(info));
+    }
+  }
+
+  return modules;
+}
+
+GlobalActivityAnalyzer::ProgramLocation
+GlobalActivityAnalyzer::GetProgramLocationFromAddress(uint64_t address) {
+  // TODO(bcwhite): Implement this.
+  return { 0, 0 };
+}
+
+bool GlobalActivityAnalyzer::IsDataComplete() const {
+  DCHECK(allocator_);
+  return !allocator_->IsFull();
+}
+
+GlobalActivityAnalyzer::UserDataSnapshot::UserDataSnapshot() = default;
+GlobalActivityAnalyzer::UserDataSnapshot::UserDataSnapshot(
+    const UserDataSnapshot& rhs) = default;
+GlobalActivityAnalyzer::UserDataSnapshot::UserDataSnapshot(
+    UserDataSnapshot&& rhs) = default;
+GlobalActivityAnalyzer::UserDataSnapshot::~UserDataSnapshot() = default;
+
+void GlobalActivityAnalyzer::PrepareAllAnalyzers() {
+  // Record the time when analysis started.
+  analysis_stamp_ = base::Time::Now().ToInternalValue();
+
+  // Fetch all the records. This will retrieve only ones created since the
+  // last run since the PMA iterator will continue from where it left off.
+  uint32_t type;
+  PersistentMemoryAllocator::Reference ref;
+  while ((ref = allocator_iterator_.GetNext(&type)) != 0) {
+    switch (type) {
+      case GlobalActivityTracker::kTypeIdActivityTracker:
+      case GlobalActivityTracker::kTypeIdActivityTrackerFree:
+      case GlobalActivityTracker::kTypeIdProcessDataRecord:
+      case GlobalActivityTracker::kTypeIdProcessDataRecordFree:
+      case PersistentMemoryAllocator::kTypeIdTransitioning:
+        // Active, free, or transitioning: add it to the list of references
+        // for later analysis.
+        memory_references_.insert(ref);
+        break;
+    }
+  }
+
+  // Clear out any old information.
+  analyzers_.clear();
+  process_data_.clear();
+  process_ids_.clear();
+  std::set<int64_t> seen_pids;
+
+  // Go through all the known references and create objects for them with
+  // snapshots of the current state.
+  for (PersistentMemoryAllocator::Reference memory_ref : memory_references_) {
+    // Get the actual data segment for the tracker. Any type will do since it
+    // is checked below.
+    void* const base = allocator_->GetAsArray<char>(
+        memory_ref, PersistentMemoryAllocator::kTypeIdAny,
+        PersistentMemoryAllocator::kSizeAny);
+    const size_t size = allocator_->GetAllocSize(memory_ref);
+    if (!base)
+      continue;
+
+    switch (allocator_->GetType(memory_ref)) {
+      case GlobalActivityTracker::kTypeIdActivityTracker: {
+        // Create the analyzer on the data. This will capture a snapshot of the
+        // tracker state. This can fail if the tracker is somehow corrupted or
+        // is in the process of shutting down.
+        std::unique_ptr<ThreadActivityAnalyzer> analyzer(
+            new ThreadActivityAnalyzer(base, size));
+        if (!analyzer->IsValid())
+          continue;
+        analyzer->AddGlobalInformation(this);
+
+        // Track PIDs.
+        int64_t pid = analyzer->GetProcessId();
+        if (seen_pids.find(pid) == seen_pids.end()) {
+          process_ids_.push_back(pid);
+          seen_pids.insert(pid);
+        }
+
+        // Add this analyzer to the map of known ones, indexed by a unique
+        // thread
+        // identifier.
+        DCHECK(!base::ContainsKey(analyzers_, analyzer->GetThreadKey()));
+        analyzer->allocator_reference_ = ref;
+        analyzers_[analyzer->GetThreadKey()] = std::move(analyzer);
+      } break;
+
+      case GlobalActivityTracker::kTypeIdProcessDataRecord: {
+        // Get the PID associated with this data record.
+        int64_t process_id;
+        int64_t create_stamp;
+        ActivityUserData::GetOwningProcessId(base, &process_id, &create_stamp);
+        DCHECK(!base::ContainsKey(process_data_, process_id));
+
+        // Create a snapshot of the data. This can fail if the data is somehow
+        // corrupted or the process shutdown and the memory being released.
+        UserDataSnapshot& snapshot = process_data_[process_id];
+        snapshot.process_id = process_id;
+        snapshot.create_stamp = create_stamp;
+        const ActivityUserData process_data(base, size);
+        if (!process_data.CreateSnapshot(&snapshot.data))
+          break;
+
+        // Check that nothing changed. If it did, forget what was recorded.
+        ActivityUserData::GetOwningProcessId(base, &process_id, &create_stamp);
+        if (process_id != snapshot.process_id ||
+            create_stamp != snapshot.create_stamp) {
+          process_data_.erase(process_id);
+          break;
+        }
+
+        // Track PIDs.
+        if (seen_pids.find(process_id) == seen_pids.end()) {
+          process_ids_.push_back(process_id);
+          seen_pids.insert(process_id);
+        }
+      } break;
+    }
+  }
+
+  // Reverse the list of PIDs so that they get popped in the order found.
+  std::reverse(process_ids_.begin(), process_ids_.end());
+}
+
+}  // namespace debug
+}  // namespace base
diff --git a/base/debug/activity_analyzer.h b/base/debug/activity_analyzer.h
new file mode 100644
index 0000000..9add85a
--- /dev/null
+++ b/base/debug/activity_analyzer.h
@@ -0,0 +1,262 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_DEBUG_ACTIVITY_ANALYZER_H_
+#define BASE_DEBUG_ACTIVITY_ANALYZER_H_
+
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/base_export.h"
+#include "base/debug/activity_tracker.h"
+
+namespace base {
+namespace debug {
+
+class GlobalActivityAnalyzer;
+
+// This class provides analysis of data captured from a ThreadActivityTracker.
+// When created, it takes a snapshot of the data held by the tracker and
+// makes that information available to other code.
+class BASE_EXPORT ThreadActivityAnalyzer {
+ public:
+  struct BASE_EXPORT Snapshot : ThreadActivityTracker::Snapshot {
+    Snapshot();
+    ~Snapshot();
+
+    // The user-data snapshot for an activity, matching the |activity_stack|
+    // of ThreadActivityTracker::Snapshot, if any.
+    std::vector<ActivityUserData::Snapshot> user_data_stack;
+  };
+
+  // This class provides keys that uniquely identify a thread, even across
+  // multiple processes.
+  class ThreadKey {
+   public:
+    ThreadKey(int64_t pid, int64_t tid) : pid_(pid), tid_(tid) {}
+
+    bool operator<(const ThreadKey& rhs) const {
+      if (pid_ != rhs.pid_)
+        return pid_ < rhs.pid_;
+      return tid_ < rhs.tid_;
+    }
+
+    bool operator==(const ThreadKey& rhs) const {
+      return (pid_ == rhs.pid_ && tid_ == rhs.tid_);
+    }
+
+   private:
+    int64_t pid_;
+    int64_t tid_;
+  };
+
+  // Creates an analyzer for an existing activity |tracker|. A snapshot is taken
+  // immediately and the tracker is not referenced again.
+  explicit ThreadActivityAnalyzer(const ThreadActivityTracker& tracker);
+
+  // Creates an analyzer for a block of memory currently or previously in-use
+  // by an activity-tracker. A snapshot is taken immediately and the memory
+  // is not referenced again.
+  ThreadActivityAnalyzer(void* base, size_t size);
+
+  // Creates an analyzer for a block of memory held within a persistent-memory
+  // |allocator| at the given |reference|. A snapshot is taken immediately and
+  // the memory is not referenced again.
+  ThreadActivityAnalyzer(PersistentMemoryAllocator* allocator,
+                         PersistentMemoryAllocator::Reference reference);
+
+  ~ThreadActivityAnalyzer();
+
+  // Adds information from the global analyzer.
+  void AddGlobalInformation(GlobalActivityAnalyzer* global);
+
+  // Returns true iff the contained data is valid. Results from all other
+  // methods are undefined if this returns false.
+  bool IsValid() { return activity_snapshot_valid_; }
+
+  // Gets the process id and its creation stamp.
+  int64_t GetProcessId(int64_t* out_stamp = nullptr) {
+    if (out_stamp)
+      *out_stamp = activity_snapshot_.create_stamp;
+    return activity_snapshot_.process_id;
+  }
+
+  // Gets the name of the thread.
+  const std::string& GetThreadName() {
+    return activity_snapshot_.thread_name;
+  }
+
+  // Gets the TheadKey for this thread.
+  ThreadKey GetThreadKey() {
+    return ThreadKey(activity_snapshot_.process_id,
+                     activity_snapshot_.thread_id);
+  }
+
+  const Snapshot& activity_snapshot() { return activity_snapshot_; }
+
+ private:
+  friend class GlobalActivityAnalyzer;
+
+  // The snapshot of the activity tracker taken at the moment of construction.
+  Snapshot activity_snapshot_;
+
+  // Flag indicating if the snapshot data is valid.
+  bool activity_snapshot_valid_;
+
+  // A reference into a persistent memory allocator, used by the global
+  // analyzer to know where this tracker came from.
+  PersistentMemoryAllocator::Reference allocator_reference_ = 0;
+
+  DISALLOW_COPY_AND_ASSIGN(ThreadActivityAnalyzer);
+};
+
+
+// This class manages analyzers for all known processes and threads as stored
+// in a persistent memory allocator. It supports retrieval of them through
+// iteration and directly using a ThreadKey, which allows for cross-references
+// to be resolved.
+// Note that though atomic snapshots are used and everything has its snapshot
+// taken at the same time, the multi-snapshot itself is not atomic and thus may
+// show small inconsistencies between threads if attempted on a live system.
+class BASE_EXPORT GlobalActivityAnalyzer {
+ public:
+  struct ProgramLocation {
+    int module;
+    uintptr_t offset;
+  };
+
+  using ThreadKey = ThreadActivityAnalyzer::ThreadKey;
+
+  // Creates a global analyzer from a persistent memory allocator.
+  explicit GlobalActivityAnalyzer(
+      std::unique_ptr<PersistentMemoryAllocator> allocator);
+
+  ~GlobalActivityAnalyzer();
+
+  // Creates a global analyzer using a given persistent-memory |allocator|.
+  static std::unique_ptr<GlobalActivityAnalyzer> CreateWithAllocator(
+      std::unique_ptr<PersistentMemoryAllocator> allocator);
+
+#if !defined(OS_NACL)
+  // Creates a global analyzer using the contents of a file given in
+  // |file_path|.
+  static std::unique_ptr<GlobalActivityAnalyzer> CreateWithFile(
+      const FilePath& file_path);
+#endif  // !defined(OS_NACL)
+
+  // Like above but accesses an allocator in a mapped shared-memory segment.
+  static std::unique_ptr<GlobalActivityAnalyzer> CreateWithSharedMemory(
+      std::unique_ptr<SharedMemory> shm);
+
+  // Like above but takes a handle to an existing shared memory segment and
+  // maps it before creating the tracker.
+  static std::unique_ptr<GlobalActivityAnalyzer> CreateWithSharedMemoryHandle(
+      const SharedMemoryHandle& handle,
+      size_t size);
+
+  // Iterates over all known valid processes and returns their PIDs or zero
+  // if there are no more. Calls to GetFirstProcess() will perform a global
+  // snapshot in order to provide a relatively consistent state across the
+  // future calls to GetNextProcess() and GetFirst/NextAnalyzer(). PIDs are
+  // returned in the order they're found meaning that a first-launched
+  // controlling process will be found first. Note, however, that space
+  // freed by an exiting process may be re-used by a later process.
+  int64_t GetFirstProcess();
+  int64_t GetNextProcess();
+
+  // Iterates over all known valid analyzers for the a given process or returns
+  // null if there are no more.
+  //
+  // GetFirstProcess() must be called first in order to capture a global
+  // snapshot! Ownership stays with the global analyzer object and all existing
+  // analyzer pointers are invalidated when GetFirstProcess() is called.
+  ThreadActivityAnalyzer* GetFirstAnalyzer(int64_t pid);
+  ThreadActivityAnalyzer* GetNextAnalyzer();
+
+  // Gets the analyzer for a specific thread or null if there is none.
+  // Ownership stays with the global analyzer object.
+  ThreadActivityAnalyzer* GetAnalyzerForThread(const ThreadKey& key);
+
+  // Extract user data based on a reference and its identifier.
+  ActivityUserData::Snapshot GetUserDataSnapshot(int64_t pid,
+                                                 uint32_t ref,
+                                                 uint32_t id);
+
+  // Extract the data for a specific process. An empty snapshot will be
+  // returned if the process is not known.
+  const ActivityUserData::Snapshot& GetProcessDataSnapshot(int64_t pid);
+
+  // Gets all log messages stored within.
+  std::vector<std::string> GetLogMessages();
+
+  // Gets modules corresponding to a pid. This pid must come from a call to
+  // GetFirst/NextProcess. Only modules that were first registered prior to
+  // GetFirstProcess's snapshot are returned.
+  std::vector<GlobalActivityTracker::ModuleInfo> GetModules(int64_t pid);
+
+  // Gets the corresponding "program location" for a given "program counter".
+  // This will return {0,0} if no mapping could be found.
+  ProgramLocation GetProgramLocationFromAddress(uint64_t address);
+
+  // Returns whether the data is complete. Data can be incomplete if the
+  // recording size quota is hit.
+  bool IsDataComplete() const;
+
+ private:
+  using AnalyzerMap =
+      std::map<ThreadKey, std::unique_ptr<ThreadActivityAnalyzer>>;
+
+  struct UserDataSnapshot {
+    // Complex class needs out-of-line ctor/dtor.
+    UserDataSnapshot();
+    UserDataSnapshot(const UserDataSnapshot& rhs);
+    UserDataSnapshot(UserDataSnapshot&& rhs);
+    ~UserDataSnapshot();
+
+    int64_t process_id;
+    int64_t create_stamp;
+    ActivityUserData::Snapshot data;
+  };
+
+  // Finds, creates, and indexes analyzers for all known processes and threads.
+  void PrepareAllAnalyzers();
+
+  // The persistent memory allocator holding all tracking data.
+  std::unique_ptr<PersistentMemoryAllocator> allocator_;
+
+  // The time stamp when analysis began. This is used to prevent looking into
+  // process IDs that get reused when analyzing a live system.
+  int64_t analysis_stamp_;
+
+  // The iterator for finding tracking information in the allocator.
+  PersistentMemoryAllocator::Iterator allocator_iterator_;
+
+  // A set of all interesting memory references found within the allocator.
+  std::set<PersistentMemoryAllocator::Reference> memory_references_;
+
+  // A set of all process-data memory references found within the allocator.
+  std::map<int64_t, UserDataSnapshot> process_data_;
+
+  // A set of all process IDs collected during PrepareAllAnalyzers. These are
+  // popped and returned one-by-one with calls to GetFirst/NextProcess().
+  std::vector<int64_t> process_ids_;
+
+  // A map, keyed by ThreadKey, of all valid activity analyzers.
+  AnalyzerMap analyzers_;
+
+  // The iterator within the analyzers_ map for returning analyzers through
+  // first/next iteration.
+  AnalyzerMap::iterator analyzers_iterator_;
+  int64_t analyzers_iterator_pid_;
+
+  DISALLOW_COPY_AND_ASSIGN(GlobalActivityAnalyzer);
+};
+
+}  // namespace debug
+}  // namespace base
+
+#endif  // BASE_DEBUG_ACTIVITY_ANALYZER_H_
diff --git a/base/debug/activity_analyzer_unittest.cc b/base/debug/activity_analyzer_unittest.cc
new file mode 100644
index 0000000..e08b43a
--- /dev/null
+++ b/base/debug/activity_analyzer_unittest.cc
@@ -0,0 +1,546 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/debug/activity_analyzer.h"
+
+#include <atomic>
+#include <memory>
+
+#include "base/auto_reset.h"
+#include "base/bind.h"
+#include "base/debug/activity_tracker.h"
+#include "base/files/file.h"
+#include "base/files/file_util.h"
+#include "base/files/memory_mapped_file.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/memory/ptr_util.h"
+#include "base/pending_task.h"
+#include "base/process/process.h"
+#include "base/stl_util.h"
+#include "base/synchronization/condition_variable.h"
+#include "base/synchronization/lock.h"
+#include "base/synchronization/spin_wait.h"
+#include "base/threading/platform_thread.h"
+#include "base/threading/simple_thread.h"
+#include "base/time/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace debug {
+
+namespace {
+
+class TestActivityTracker : public ThreadActivityTracker {
+ public:
+  TestActivityTracker(std::unique_ptr<char[]> memory, size_t mem_size)
+      : ThreadActivityTracker(memset(memory.get(), 0, mem_size), mem_size),
+        mem_segment_(std::move(memory)) {}
+
+  ~TestActivityTracker() override = default;
+
+ private:
+  std::unique_ptr<char[]> mem_segment_;
+};
+
+}  // namespace
+
+
+class ActivityAnalyzerTest : public testing::Test {
+ public:
+  const int kMemorySize = 1 << 20;  // 1MiB
+  const int kStackSize  = 1 << 10;  // 1KiB
+
+  ActivityAnalyzerTest() = default;
+
+  ~ActivityAnalyzerTest() override {
+    GlobalActivityTracker* global_tracker = GlobalActivityTracker::Get();
+    if (global_tracker) {
+      global_tracker->ReleaseTrackerForCurrentThreadForTesting();
+      delete global_tracker;
+    }
+  }
+
+  std::unique_ptr<ThreadActivityTracker> CreateActivityTracker() {
+    std::unique_ptr<char[]> memory(new char[kStackSize]);
+    return std::make_unique<TestActivityTracker>(std::move(memory), kStackSize);
+  }
+
+  template <typename Function>
+  void AsOtherProcess(int64_t pid, Function function) {
+    std::unique_ptr<GlobalActivityTracker> old_global =
+        GlobalActivityTracker::ReleaseForTesting();
+    ASSERT_TRUE(old_global);
+
+    PersistentMemoryAllocator* old_allocator = old_global->allocator();
+    std::unique_ptr<PersistentMemoryAllocator> new_allocator(
+        std::make_unique<PersistentMemoryAllocator>(
+            const_cast<void*>(old_allocator->data()), old_allocator->size(), 0,
+            0, "", false));
+    GlobalActivityTracker::CreateWithAllocator(std::move(new_allocator), 3,
+                                               pid);
+
+    function();
+
+    GlobalActivityTracker::ReleaseForTesting();
+    GlobalActivityTracker::SetForTesting(std::move(old_global));
+  }
+
+  static void DoNothing() {}
+};
+
+TEST_F(ActivityAnalyzerTest, ThreadAnalyzerConstruction) {
+  std::unique_ptr<ThreadActivityTracker> tracker = CreateActivityTracker();
+  {
+    ThreadActivityAnalyzer analyzer(*tracker);
+    EXPECT_TRUE(analyzer.IsValid());
+    EXPECT_EQ(PlatformThread::GetName(), analyzer.GetThreadName());
+  }
+
+  // TODO(bcwhite): More tests once Analyzer does more.
+}
+
+
+// GlobalActivityAnalyzer tests below.
+
+namespace {
+
+class SimpleActivityThread : public SimpleThread {
+ public:
+  SimpleActivityThread(const std::string& name,
+                       const void* source,
+                       Activity::Type activity,
+                       const ActivityData& data)
+      : SimpleThread(name, Options()),
+        source_(source),
+        activity_(activity),
+        data_(data),
+        ready_(false),
+        exit_(false),
+        exit_condition_(&lock_) {}
+
+  ~SimpleActivityThread() override = default;
+
+  void Run() override {
+    ThreadActivityTracker::ActivityId id =
+        GlobalActivityTracker::Get()
+            ->GetOrCreateTrackerForCurrentThread()
+            ->PushActivity(source_, activity_, data_);
+
+    {
+      AutoLock auto_lock(lock_);
+      ready_.store(true, std::memory_order_release);
+      while (!exit_.load(std::memory_order_relaxed))
+        exit_condition_.Wait();
+    }
+
+    GlobalActivityTracker::Get()->GetTrackerForCurrentThread()->PopActivity(id);
+  }
+
+  void Exit() {
+    AutoLock auto_lock(lock_);
+    exit_.store(true, std::memory_order_relaxed);
+    exit_condition_.Signal();
+  }
+
+  void WaitReady() {
+    SPIN_FOR_1_SECOND_OR_UNTIL_TRUE(ready_.load(std::memory_order_acquire));
+  }
+
+ private:
+  const void* source_;
+  Activity::Type activity_;
+  ActivityData data_;
+
+  std::atomic<bool> ready_;
+  std::atomic<bool> exit_;
+  Lock lock_;
+  ConditionVariable exit_condition_;
+
+  DISALLOW_COPY_AND_ASSIGN(SimpleActivityThread);
+};
+
+}  // namespace
+
+TEST_F(ActivityAnalyzerTest, GlobalAnalyzerConstruction) {
+  GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3, 0);
+  GlobalActivityTracker::Get()->process_data().SetString("foo", "bar");
+
+  PersistentMemoryAllocator* allocator =
+      GlobalActivityTracker::Get()->allocator();
+  GlobalActivityAnalyzer analyzer(std::make_unique<PersistentMemoryAllocator>(
+      const_cast<void*>(allocator->data()), allocator->size(), 0, 0, "", true));
+
+  // The only thread at this point is the test thread of this process.
+  const int64_t pid = analyzer.GetFirstProcess();
+  ASSERT_NE(0, pid);
+  ThreadActivityAnalyzer* ta1 = analyzer.GetFirstAnalyzer(pid);
+  ASSERT_TRUE(ta1);
+  EXPECT_FALSE(analyzer.GetNextAnalyzer());
+  ThreadActivityAnalyzer::ThreadKey tk1 = ta1->GetThreadKey();
+  EXPECT_EQ(ta1, analyzer.GetAnalyzerForThread(tk1));
+  EXPECT_EQ(0, analyzer.GetNextProcess());
+
+  // Create a second thread that will do something.
+  SimpleActivityThread t2("t2", nullptr, Activity::ACT_TASK,
+                          ActivityData::ForTask(11));
+  t2.Start();
+  t2.WaitReady();
+
+  // Now there should be two. Calling GetFirstProcess invalidates any
+  // previously returned analyzer pointers.
+  ASSERT_EQ(pid, analyzer.GetFirstProcess());
+  EXPECT_TRUE(analyzer.GetFirstAnalyzer(pid));
+  EXPECT_TRUE(analyzer.GetNextAnalyzer());
+  EXPECT_FALSE(analyzer.GetNextAnalyzer());
+  EXPECT_EQ(0, analyzer.GetNextProcess());
+
+  // Let thread exit.
+  t2.Exit();
+  t2.Join();
+
+  // Now there should be only one again.
+  ASSERT_EQ(pid, analyzer.GetFirstProcess());
+  ThreadActivityAnalyzer* ta2 = analyzer.GetFirstAnalyzer(pid);
+  ASSERT_TRUE(ta2);
+  EXPECT_FALSE(analyzer.GetNextAnalyzer());
+  ThreadActivityAnalyzer::ThreadKey tk2 = ta2->GetThreadKey();
+  EXPECT_EQ(ta2, analyzer.GetAnalyzerForThread(tk2));
+  EXPECT_EQ(tk1, tk2);
+  EXPECT_EQ(0, analyzer.GetNextProcess());
+
+  // Verify that there is process data.
+  const ActivityUserData::Snapshot& data_snapshot =
+      analyzer.GetProcessDataSnapshot(pid);
+  ASSERT_LE(1U, data_snapshot.size());
+  EXPECT_EQ("bar", data_snapshot.at("foo").GetString());
+}
+
+TEST_F(ActivityAnalyzerTest, GlobalAnalyzerFromSharedMemory) {
+  SharedMemoryHandle handle1;
+  SharedMemoryHandle handle2;
+
+  {
+    std::unique_ptr<SharedMemory> shmem(new SharedMemory());
+    ASSERT_TRUE(shmem->CreateAndMapAnonymous(kMemorySize));
+    handle1 = shmem->handle().Duplicate();
+    ASSERT_TRUE(handle1.IsValid());
+    handle2 = shmem->handle().Duplicate();
+    ASSERT_TRUE(handle2.IsValid());
+  }
+
+  GlobalActivityTracker::CreateWithSharedMemoryHandle(handle1, kMemorySize, 0,
+                                                      "", 3);
+  GlobalActivityTracker::Get()->process_data().SetString("foo", "bar");
+
+  std::unique_ptr<GlobalActivityAnalyzer> analyzer =
+      GlobalActivityAnalyzer::CreateWithSharedMemoryHandle(handle2,
+                                                           kMemorySize);
+
+  const int64_t pid = analyzer->GetFirstProcess();
+  ASSERT_NE(0, pid);
+  const ActivityUserData::Snapshot& data_snapshot =
+      analyzer->GetProcessDataSnapshot(pid);
+  ASSERT_LE(1U, data_snapshot.size());
+  EXPECT_EQ("bar", data_snapshot.at("foo").GetString());
+}
+
+TEST_F(ActivityAnalyzerTest, UserDataSnapshotTest) {
+  GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3, 0);
+  ThreadActivityAnalyzer::Snapshot tracker_snapshot;
+
+  const char string1a[] = "string1a";
+  const char string1b[] = "string1b";
+  const char string2a[] = "string2a";
+  const char string2b[] = "string2b";
+
+  PersistentMemoryAllocator* allocator =
+      GlobalActivityTracker::Get()->allocator();
+  GlobalActivityAnalyzer global_analyzer(
+      std::make_unique<PersistentMemoryAllocator>(
+          const_cast<void*>(allocator->data()), allocator->size(), 0, 0, "",
+          true));
+
+  ThreadActivityTracker* tracker =
+      GlobalActivityTracker::Get()->GetOrCreateTrackerForCurrentThread();
+
+  {
+    ScopedActivity activity1(1, 11, 111);
+    ActivityUserData& user_data1 = activity1.user_data();
+    user_data1.Set("raw1", "foo1", 4);
+    user_data1.SetString("string1", "bar1");
+    user_data1.SetChar("char1", '1');
+    user_data1.SetInt("int1", -1111);
+    user_data1.SetUint("uint1", 1111);
+    user_data1.SetBool("bool1", true);
+    user_data1.SetReference("ref1", string1a, sizeof(string1a));
+    user_data1.SetStringReference("sref1", string1b);
+
+    {
+      ScopedActivity activity2(2, 22, 222);
+      ActivityUserData& user_data2 = activity2.user_data();
+      user_data2.Set("raw2", "foo2", 4);
+      user_data2.SetString("string2", "bar2");
+      user_data2.SetChar("char2", '2');
+      user_data2.SetInt("int2", -2222);
+      user_data2.SetUint("uint2", 2222);
+      user_data2.SetBool("bool2", false);
+      user_data2.SetReference("ref2", string2a, sizeof(string2a));
+      user_data2.SetStringReference("sref2", string2b);
+
+      ASSERT_TRUE(tracker->CreateSnapshot(&tracker_snapshot));
+      ASSERT_EQ(2U, tracker_snapshot.activity_stack.size());
+
+      ThreadActivityAnalyzer analyzer(*tracker);
+      analyzer.AddGlobalInformation(&global_analyzer);
+      const ThreadActivityAnalyzer::Snapshot& analyzer_snapshot =
+          analyzer.activity_snapshot();
+      ASSERT_EQ(2U, analyzer_snapshot.user_data_stack.size());
+      const ActivityUserData::Snapshot& user_data =
+          analyzer_snapshot.user_data_stack.at(1);
+      EXPECT_EQ(8U, user_data.size());
+      ASSERT_TRUE(ContainsKey(user_data, "raw2"));
+      EXPECT_EQ("foo2", user_data.at("raw2").Get().as_string());
+      ASSERT_TRUE(ContainsKey(user_data, "string2"));
+      EXPECT_EQ("bar2", user_data.at("string2").GetString().as_string());
+      ASSERT_TRUE(ContainsKey(user_data, "char2"));
+      EXPECT_EQ('2', user_data.at("char2").GetChar());
+      ASSERT_TRUE(ContainsKey(user_data, "int2"));
+      EXPECT_EQ(-2222, user_data.at("int2").GetInt());
+      ASSERT_TRUE(ContainsKey(user_data, "uint2"));
+      EXPECT_EQ(2222U, user_data.at("uint2").GetUint());
+      ASSERT_TRUE(ContainsKey(user_data, "bool2"));
+      EXPECT_FALSE(user_data.at("bool2").GetBool());
+      ASSERT_TRUE(ContainsKey(user_data, "ref2"));
+      EXPECT_EQ(string2a, user_data.at("ref2").GetReference().data());
+      EXPECT_EQ(sizeof(string2a), user_data.at("ref2").GetReference().size());
+      ASSERT_TRUE(ContainsKey(user_data, "sref2"));
+      EXPECT_EQ(string2b, user_data.at("sref2").GetStringReference().data());
+      EXPECT_EQ(strlen(string2b),
+                user_data.at("sref2").GetStringReference().size());
+    }
+
+    ASSERT_TRUE(tracker->CreateSnapshot(&tracker_snapshot));
+    ASSERT_EQ(1U, tracker_snapshot.activity_stack.size());
+
+    ThreadActivityAnalyzer analyzer(*tracker);
+    analyzer.AddGlobalInformation(&global_analyzer);
+    const ThreadActivityAnalyzer::Snapshot& analyzer_snapshot =
+        analyzer.activity_snapshot();
+    ASSERT_EQ(1U, analyzer_snapshot.user_data_stack.size());
+    const ActivityUserData::Snapshot& user_data =
+        analyzer_snapshot.user_data_stack.at(0);
+    EXPECT_EQ(8U, user_data.size());
+    EXPECT_EQ("foo1", user_data.at("raw1").Get().as_string());
+    EXPECT_EQ("bar1", user_data.at("string1").GetString().as_string());
+    EXPECT_EQ('1', user_data.at("char1").GetChar());
+    EXPECT_EQ(-1111, user_data.at("int1").GetInt());
+    EXPECT_EQ(1111U, user_data.at("uint1").GetUint());
+    EXPECT_TRUE(user_data.at("bool1").GetBool());
+    EXPECT_EQ(string1a, user_data.at("ref1").GetReference().data());
+    EXPECT_EQ(sizeof(string1a), user_data.at("ref1").GetReference().size());
+    EXPECT_EQ(string1b, user_data.at("sref1").GetStringReference().data());
+    EXPECT_EQ(strlen(string1b),
+              user_data.at("sref1").GetStringReference().size());
+  }
+
+  ASSERT_TRUE(tracker->CreateSnapshot(&tracker_snapshot));
+  ASSERT_EQ(0U, tracker_snapshot.activity_stack.size());
+}
+
+TEST_F(ActivityAnalyzerTest, GlobalUserDataTest) {
+  const int64_t pid = GetCurrentProcId();
+  GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3, 0);
+
+  const char string1[] = "foo";
+  const char string2[] = "bar";
+
+  PersistentMemoryAllocator* allocator =
+      GlobalActivityTracker::Get()->allocator();
+  GlobalActivityAnalyzer global_analyzer(
+      std::make_unique<PersistentMemoryAllocator>(
+          const_cast<void*>(allocator->data()), allocator->size(), 0, 0, "",
+          true));
+
+  ActivityUserData& process_data = GlobalActivityTracker::Get()->process_data();
+  ASSERT_NE(0U, process_data.id());
+  process_data.Set("raw", "foo", 3);
+  process_data.SetString("string", "bar");
+  process_data.SetChar("char", '9');
+  process_data.SetInt("int", -9999);
+  process_data.SetUint("uint", 9999);
+  process_data.SetBool("bool", true);
+  process_data.SetReference("ref", string1, sizeof(string1));
+  process_data.SetStringReference("sref", string2);
+
+  int64_t first_pid = global_analyzer.GetFirstProcess();
+  DCHECK_EQ(pid, first_pid);
+  const ActivityUserData::Snapshot& snapshot =
+      global_analyzer.GetProcessDataSnapshot(pid);
+  ASSERT_TRUE(ContainsKey(snapshot, "raw"));
+  EXPECT_EQ("foo", snapshot.at("raw").Get().as_string());
+  ASSERT_TRUE(ContainsKey(snapshot, "string"));
+  EXPECT_EQ("bar", snapshot.at("string").GetString().as_string());
+  ASSERT_TRUE(ContainsKey(snapshot, "char"));
+  EXPECT_EQ('9', snapshot.at("char").GetChar());
+  ASSERT_TRUE(ContainsKey(snapshot, "int"));
+  EXPECT_EQ(-9999, snapshot.at("int").GetInt());
+  ASSERT_TRUE(ContainsKey(snapshot, "uint"));
+  EXPECT_EQ(9999U, snapshot.at("uint").GetUint());
+  ASSERT_TRUE(ContainsKey(snapshot, "bool"));
+  EXPECT_TRUE(snapshot.at("bool").GetBool());
+  ASSERT_TRUE(ContainsKey(snapshot, "ref"));
+  EXPECT_EQ(string1, snapshot.at("ref").GetReference().data());
+  EXPECT_EQ(sizeof(string1), snapshot.at("ref").GetReference().size());
+  ASSERT_TRUE(ContainsKey(snapshot, "sref"));
+  EXPECT_EQ(string2, snapshot.at("sref").GetStringReference().data());
+  EXPECT_EQ(strlen(string2), snapshot.at("sref").GetStringReference().size());
+}
+
+TEST_F(ActivityAnalyzerTest, GlobalModulesTest) {
+  GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3, 0);
+  GlobalActivityTracker* global = GlobalActivityTracker::Get();
+
+  PersistentMemoryAllocator* allocator = global->allocator();
+  GlobalActivityAnalyzer global_analyzer(
+      std::make_unique<PersistentMemoryAllocator>(
+          const_cast<void*>(allocator->data()), allocator->size(), 0, 0, "",
+          true));
+
+  GlobalActivityTracker::ModuleInfo info1;
+  info1.is_loaded = true;
+  info1.address = 0x12345678;
+  info1.load_time = 1111;
+  info1.size = 0xABCDEF;
+  info1.timestamp = 111;
+  info1.age = 11;
+  info1.identifier[0] = 1;
+  info1.file = "anything";
+  info1.debug_file = "elsewhere";
+
+  global->RecordModuleInfo(info1);
+  std::vector<GlobalActivityTracker::ModuleInfo> modules1;
+  modules1 = global_analyzer.GetModules(global_analyzer.GetFirstProcess());
+  ASSERT_EQ(1U, modules1.size());
+  GlobalActivityTracker::ModuleInfo& stored1a = modules1[0];
+  EXPECT_EQ(info1.is_loaded, stored1a.is_loaded);
+  EXPECT_EQ(info1.address, stored1a.address);
+  EXPECT_NE(info1.load_time, stored1a.load_time);
+  EXPECT_EQ(info1.size, stored1a.size);
+  EXPECT_EQ(info1.timestamp, stored1a.timestamp);
+  EXPECT_EQ(info1.age, stored1a.age);
+  EXPECT_EQ(info1.identifier[0], stored1a.identifier[0]);
+  EXPECT_EQ(info1.file, stored1a.file);
+  EXPECT_EQ(info1.debug_file, stored1a.debug_file);
+
+  info1.is_loaded = false;
+  global->RecordModuleInfo(info1);
+  modules1 = global_analyzer.GetModules(global_analyzer.GetFirstProcess());
+  ASSERT_EQ(1U, modules1.size());
+  GlobalActivityTracker::ModuleInfo& stored1b = modules1[0];
+  EXPECT_EQ(info1.is_loaded, stored1b.is_loaded);
+  EXPECT_EQ(info1.address, stored1b.address);
+  EXPECT_NE(info1.load_time, stored1b.load_time);
+  EXPECT_EQ(info1.size, stored1b.size);
+  EXPECT_EQ(info1.timestamp, stored1b.timestamp);
+  EXPECT_EQ(info1.age, stored1b.age);
+  EXPECT_EQ(info1.identifier[0], stored1b.identifier[0]);
+  EXPECT_EQ(info1.file, stored1b.file);
+  EXPECT_EQ(info1.debug_file, stored1b.debug_file);
+
+  GlobalActivityTracker::ModuleInfo info2;
+  info2.is_loaded = true;
+  info2.address = 0x87654321;
+  info2.load_time = 2222;
+  info2.size = 0xFEDCBA;
+  info2.timestamp = 222;
+  info2.age = 22;
+  info2.identifier[0] = 2;
+  info2.file = "nothing";
+  info2.debug_file = "farewell";
+
+  global->RecordModuleInfo(info2);
+  std::vector<GlobalActivityTracker::ModuleInfo> modules2;
+  modules2 = global_analyzer.GetModules(global_analyzer.GetFirstProcess());
+  ASSERT_EQ(2U, modules2.size());
+  GlobalActivityTracker::ModuleInfo& stored2 = modules2[1];
+  EXPECT_EQ(info2.is_loaded, stored2.is_loaded);
+  EXPECT_EQ(info2.address, stored2.address);
+  EXPECT_NE(info2.load_time, stored2.load_time);
+  EXPECT_EQ(info2.size, stored2.size);
+  EXPECT_EQ(info2.timestamp, stored2.timestamp);
+  EXPECT_EQ(info2.age, stored2.age);
+  EXPECT_EQ(info2.identifier[0], stored2.identifier[0]);
+  EXPECT_EQ(info2.file, stored2.file);
+  EXPECT_EQ(info2.debug_file, stored2.debug_file);
+}
+
+TEST_F(ActivityAnalyzerTest, GlobalLogMessages) {
+  GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3, 0);
+
+  PersistentMemoryAllocator* allocator =
+      GlobalActivityTracker::Get()->allocator();
+  GlobalActivityAnalyzer analyzer(std::make_unique<PersistentMemoryAllocator>(
+      const_cast<void*>(allocator->data()), allocator->size(), 0, 0, "", true));
+
+  GlobalActivityTracker::Get()->RecordLogMessage("hello world");
+  GlobalActivityTracker::Get()->RecordLogMessage("foo bar");
+
+  std::vector<std::string> messages = analyzer.GetLogMessages();
+  ASSERT_EQ(2U, messages.size());
+  EXPECT_EQ("hello world", messages[0]);
+  EXPECT_EQ("foo bar", messages[1]);
+}
+
+TEST_F(ActivityAnalyzerTest, GlobalMultiProcess) {
+  GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3, 1001);
+  GlobalActivityTracker* global = GlobalActivityTracker::Get();
+  PersistentMemoryAllocator* allocator = global->allocator();
+  EXPECT_EQ(1001, global->process_id());
+
+  int64_t process_id;
+  int64_t create_stamp;
+  ActivityUserData::GetOwningProcessId(
+      GlobalActivityTracker::Get()->process_data().GetBaseAddress(),
+      &process_id, &create_stamp);
+  ASSERT_EQ(1001, process_id);
+
+  GlobalActivityTracker::Get()->process_data().SetInt("pid",
+                                                      global->process_id());
+
+  GlobalActivityAnalyzer analyzer(std::make_unique<PersistentMemoryAllocator>(
+      const_cast<void*>(allocator->data()), allocator->size(), 0, 0, "", true));
+
+  AsOtherProcess(2002, [&global]() {
+    ASSERT_NE(global, GlobalActivityTracker::Get());
+    EXPECT_EQ(2002, GlobalActivityTracker::Get()->process_id());
+
+    int64_t process_id;
+    int64_t create_stamp;
+    ActivityUserData::GetOwningProcessId(
+        GlobalActivityTracker::Get()->process_data().GetBaseAddress(),
+        &process_id, &create_stamp);
+    ASSERT_EQ(2002, process_id);
+
+    GlobalActivityTracker::Get()->process_data().SetInt(
+        "pid", GlobalActivityTracker::Get()->process_id());
+  });
+  ASSERT_EQ(global, GlobalActivityTracker::Get());
+  EXPECT_EQ(1001, GlobalActivityTracker::Get()->process_id());
+
+  const int64_t pid1 = analyzer.GetFirstProcess();
+  ASSERT_EQ(1001, pid1);
+  const int64_t pid2 = analyzer.GetNextProcess();
+  ASSERT_EQ(2002, pid2);
+  EXPECT_EQ(0, analyzer.GetNextProcess());
+
+  const ActivityUserData::Snapshot& pdata1 =
+      analyzer.GetProcessDataSnapshot(pid1);
+  const ActivityUserData::Snapshot& pdata2 =
+      analyzer.GetProcessDataSnapshot(pid2);
+  EXPECT_EQ(1001, pdata1.at("pid").GetInt());
+  EXPECT_EQ(2002, pdata2.at("pid").GetInt());
+}
+
+}  // namespace debug
+}  // namespace base
diff --git a/base/debug/asan_invalid_access.cc b/base/debug/asan_invalid_access.cc
new file mode 100644
index 0000000..07c19db
--- /dev/null
+++ b/base/debug/asan_invalid_access.cc
@@ -0,0 +1,101 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/debug/asan_invalid_access.h"
+
+#include <stddef.h>
+
+#include <memory>
+
+#include "base/debug/alias.h"
+#include "base/logging.h"
+#include "build/build_config.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+
+namespace base {
+namespace debug {
+
+namespace {
+
+#if defined(OS_WIN) && defined(ADDRESS_SANITIZER)
+// Corrupt a memory block and make sure that the corruption gets detected either
+// when we free it or when another crash happens (if |induce_crash| is set to
+// true).
+NOINLINE void CorruptMemoryBlock(bool induce_crash) {
+  // NOTE(sebmarchand): We intentionally corrupt a memory block here in order to
+  //     trigger an Address Sanitizer (ASAN) error report.
+  static const int kArraySize = 5;
+  LONG* array = new LONG[kArraySize];
+
+  // Explicitly call out to a kernel32 function to perform the memory access.
+  // This way the underflow won't be detected but the corruption will (as the
+  // allocator will still be hooked).
+  auto InterlockedIncrementFn =
+      reinterpret_cast<LONG (*)(LONG volatile * addend)>(
+          GetProcAddress(GetModuleHandle(L"kernel32"), "InterlockedIncrement"));
+  CHECK(InterlockedIncrementFn);
+
+  LONG volatile dummy = InterlockedIncrementFn(array - 1);
+  base::debug::Alias(const_cast<LONG*>(&dummy));
+
+  if (induce_crash)
+    CHECK(false);
+  delete[] array;
+}
+#endif  // OS_WIN && ADDRESS_SANITIZER
+
+}  // namespace
+
+#if defined(ADDRESS_SANITIZER)
+// NOTE(sebmarchand): We intentionally perform some invalid heap access here in
+//     order to trigger an AddressSanitizer (ASan) error report.
+
+static const size_t kArraySize = 5;
+
+void AsanHeapOverflow() {
+  // Declares the array as volatile to make sure it doesn't get optimized away.
+  std::unique_ptr<volatile int[]> array(
+      const_cast<volatile int*>(new int[kArraySize]));
+  int dummy = array[kArraySize];
+  base::debug::Alias(&dummy);
+}
+
+void AsanHeapUnderflow() {
+  // Declares the array as volatile to make sure it doesn't get optimized away.
+  std::unique_ptr<volatile int[]> array(
+      const_cast<volatile int*>(new int[kArraySize]));
+  // We need to store the underflow address in a temporary variable as trying to
+  // access array[-1] will trigger a warning C4245: "conversion from 'int' to
+  // 'size_t', signed/unsigned mismatch".
+  volatile int* underflow_address = &array[0] - 1;
+  int dummy = *underflow_address;
+  base::debug::Alias(&dummy);
+}
+
+void AsanHeapUseAfterFree() {
+  // Declares the array as volatile to make sure it doesn't get optimized away.
+  std::unique_ptr<volatile int[]> array(
+      const_cast<volatile int*>(new int[kArraySize]));
+  volatile int* dangling = array.get();
+  array.reset();
+  int dummy = dangling[kArraySize / 2];
+  base::debug::Alias(&dummy);
+}
+
+#if defined(OS_WIN)
+void AsanCorruptHeapBlock() {
+  CorruptMemoryBlock(false);
+}
+
+void AsanCorruptHeap() {
+  CorruptMemoryBlock(true);
+}
+#endif  // OS_WIN
+#endif  // ADDRESS_SANITIZER
+
+}  // namespace debug
+}  // namespace base
diff --git a/base/debug/asan_invalid_access.h b/base/debug/asan_invalid_access.h
new file mode 100644
index 0000000..dc9a7ee
--- /dev/null
+++ b/base/debug/asan_invalid_access.h
@@ -0,0 +1,46 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Defines some functions that intentionally do an invalid memory access in
+// order to trigger an AddressSanitizer (ASan) error report.
+
+#ifndef BASE_DEBUG_ASAN_INVALID_ACCESS_H_
+#define BASE_DEBUG_ASAN_INVALID_ACCESS_H_
+
+#include "base/base_export.h"
+#include "base/compiler_specific.h"
+#include "build/build_config.h"
+
+namespace base {
+namespace debug {
+
+#if defined(ADDRESS_SANITIZER)
+
+// Generates an heap buffer overflow.
+BASE_EXPORT NOINLINE void AsanHeapOverflow();
+
+// Generates an heap buffer underflow.
+BASE_EXPORT NOINLINE void AsanHeapUnderflow();
+
+// Generates an use after free.
+BASE_EXPORT NOINLINE void AsanHeapUseAfterFree();
+
+// The "corrupt-block" and "corrupt-heap" classes of bugs is specific to
+// Windows.
+#if defined(OS_WIN)
+// Corrupts a memory block and makes sure that the corruption gets detected when
+// we try to free this block.
+BASE_EXPORT NOINLINE void AsanCorruptHeapBlock();
+
+// Corrupts the heap and makes sure that the corruption gets detected when a
+// crash occur.
+BASE_EXPORT NOINLINE void AsanCorruptHeap();
+
+#endif  // OS_WIN
+#endif  // ADDRESS_SANITIZER
+
+}  // namespace debug
+}  // namespace base
+
+#endif  // BASE_DEBUG_ASAN_INVALID_ACCESS_H_
diff --git a/base/debug/crash_logging_unittest.cc b/base/debug/crash_logging_unittest.cc
new file mode 100644
index 0000000..c10d36e
--- /dev/null
+++ b/base/debug/crash_logging_unittest.cc
@@ -0,0 +1,17 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/debug/crash_logging.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(CrashLoggingTest, UninitializedCrashKeyStringSupport) {
+  auto* crash_key = base::debug::AllocateCrashKeyString(
+      "test", base::debug::CrashKeySize::Size32);
+  EXPECT_FALSE(crash_key);
+
+  base::debug::SetCrashKeyString(crash_key, "value");
+
+  base::debug::ClearCrashKeyString(crash_key);
+}
diff --git a/base/debug/proc_maps_linux_unittest.cc b/base/debug/proc_maps_linux_unittest.cc
new file mode 100644
index 0000000..7abf152
--- /dev/null
+++ b/base/debug/proc_maps_linux_unittest.cc
@@ -0,0 +1,328 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "base/debug/proc_maps_linux.h"
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "base/path_service.h"
+#include "base/strings/stringprintf.h"
+#include "base/threading/platform_thread.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace debug {
+
+TEST(ProcMapsTest, Empty) {
+  std::vector<MappedMemoryRegion> regions;
+  EXPECT_TRUE(ParseProcMaps("", &regions));
+  EXPECT_EQ(0u, regions.size());
+}
+
+TEST(ProcMapsTest, NoSpaces) {
+  static const char kNoSpaces[] =
+      "00400000-0040b000 r-xp 00002200 fc:00 794418 /bin/cat\n";
+
+  std::vector<MappedMemoryRegion> regions;
+  ASSERT_TRUE(ParseProcMaps(kNoSpaces, &regions));
+  ASSERT_EQ(1u, regions.size());
+
+  EXPECT_EQ(0x00400000u, regions[0].start);
+  EXPECT_EQ(0x0040b000u, regions[0].end);
+  EXPECT_EQ(0x00002200u, regions[0].offset);
+  EXPECT_EQ("/bin/cat", regions[0].path);
+}
+
+TEST(ProcMapsTest, Spaces) {
+  static const char kSpaces[] =
+      "00400000-0040b000 r-xp 00002200 fc:00 794418 /bin/space cat\n";
+
+  std::vector<MappedMemoryRegion> regions;
+  ASSERT_TRUE(ParseProcMaps(kSpaces, &regions));
+  ASSERT_EQ(1u, regions.size());
+
+  EXPECT_EQ(0x00400000u, regions[0].start);
+  EXPECT_EQ(0x0040b000u, regions[0].end);
+  EXPECT_EQ(0x00002200u, regions[0].offset);
+  EXPECT_EQ("/bin/space cat", regions[0].path);
+}
+
+TEST(ProcMapsTest, NoNewline) {
+  static const char kNoSpaces[] =
+      "00400000-0040b000 r-xp 00002200 fc:00 794418 /bin/cat";
+
+  std::vector<MappedMemoryRegion> regions;
+  ASSERT_FALSE(ParseProcMaps(kNoSpaces, &regions));
+}
+
+TEST(ProcMapsTest, NoPath) {
+  static const char kNoPath[] =
+      "00400000-0040b000 rw-p 00000000 00:00 0 \n";
+
+  std::vector<MappedMemoryRegion> regions;
+  ASSERT_TRUE(ParseProcMaps(kNoPath, &regions));
+  ASSERT_EQ(1u, regions.size());
+
+  EXPECT_EQ(0x00400000u, regions[0].start);
+  EXPECT_EQ(0x0040b000u, regions[0].end);
+  EXPECT_EQ(0x00000000u, regions[0].offset);
+  EXPECT_EQ("", regions[0].path);
+}
+
+TEST(ProcMapsTest, Heap) {
+  static const char kHeap[] =
+      "022ac000-022cd000 rw-p 00000000 00:00 0 [heap]\n";
+
+  std::vector<MappedMemoryRegion> regions;
+  ASSERT_TRUE(ParseProcMaps(kHeap, &regions));
+  ASSERT_EQ(1u, regions.size());
+
+  EXPECT_EQ(0x022ac000u, regions[0].start);
+  EXPECT_EQ(0x022cd000u, regions[0].end);
+  EXPECT_EQ(0x00000000u, regions[0].offset);
+  EXPECT_EQ("[heap]", regions[0].path);
+}
+
+#if defined(ARCH_CPU_32_BITS)
+TEST(ProcMapsTest, Stack32) {
+  static const char kStack[] =
+      "beb04000-beb25000 rw-p 00000000 00:00 0 [stack]\n";
+
+  std::vector<MappedMemoryRegion> regions;
+  ASSERT_TRUE(ParseProcMaps(kStack, &regions));
+  ASSERT_EQ(1u, regions.size());
+
+  EXPECT_EQ(0xbeb04000u, regions[0].start);
+  EXPECT_EQ(0xbeb25000u, regions[0].end);
+  EXPECT_EQ(0x00000000u, regions[0].offset);
+  EXPECT_EQ("[stack]", regions[0].path);
+}
+#elif defined(ARCH_CPU_64_BITS)
+TEST(ProcMapsTest, Stack64) {
+  static const char kStack[] =
+      "7fff69c5b000-7fff69c7d000 rw-p 00000000 00:00 0 [stack]\n";
+
+  std::vector<MappedMemoryRegion> regions;
+  ASSERT_TRUE(ParseProcMaps(kStack, &regions));
+  ASSERT_EQ(1u, regions.size());
+
+  EXPECT_EQ(0x7fff69c5b000u, regions[0].start);
+  EXPECT_EQ(0x7fff69c7d000u, regions[0].end);
+  EXPECT_EQ(0x00000000u, regions[0].offset);
+  EXPECT_EQ("[stack]", regions[0].path);
+}
+#endif
+
+TEST(ProcMapsTest, Multiple) {
+  static const char kMultiple[] =
+      "00400000-0040b000 r-xp 00000000 fc:00 794418 /bin/cat\n"
+      "0060a000-0060b000 r--p 0000a000 fc:00 794418 /bin/cat\n"
+      "0060b000-0060c000 rw-p 0000b000 fc:00 794418 /bin/cat\n";
+
+  std::vector<MappedMemoryRegion> regions;
+  ASSERT_TRUE(ParseProcMaps(kMultiple, &regions));
+  ASSERT_EQ(3u, regions.size());
+
+  EXPECT_EQ(0x00400000u, regions[0].start);
+  EXPECT_EQ(0x0040b000u, regions[0].end);
+  EXPECT_EQ(0x00000000u, regions[0].offset);
+  EXPECT_EQ("/bin/cat", regions[0].path);
+
+  EXPECT_EQ(0x0060a000u, regions[1].start);
+  EXPECT_EQ(0x0060b000u, regions[1].end);
+  EXPECT_EQ(0x0000a000u, regions[1].offset);
+  EXPECT_EQ("/bin/cat", regions[1].path);
+
+  EXPECT_EQ(0x0060b000u, regions[2].start);
+  EXPECT_EQ(0x0060c000u, regions[2].end);
+  EXPECT_EQ(0x0000b000u, regions[2].offset);
+  EXPECT_EQ("/bin/cat", regions[2].path);
+}
+
+TEST(ProcMapsTest, Permissions) {
+  static struct {
+    const char* input;
+    uint8_t permissions;
+  } kTestCases[] = {
+    {"00400000-0040b000 ---s 00000000 fc:00 794418 /bin/cat\n", 0},
+    {"00400000-0040b000 ---S 00000000 fc:00 794418 /bin/cat\n", 0},
+    {"00400000-0040b000 r--s 00000000 fc:00 794418 /bin/cat\n",
+     MappedMemoryRegion::READ},
+    {"00400000-0040b000 -w-s 00000000 fc:00 794418 /bin/cat\n",
+     MappedMemoryRegion::WRITE},
+    {"00400000-0040b000 --xs 00000000 fc:00 794418 /bin/cat\n",
+     MappedMemoryRegion::EXECUTE},
+    {"00400000-0040b000 rwxs 00000000 fc:00 794418 /bin/cat\n",
+     MappedMemoryRegion::READ | MappedMemoryRegion::WRITE |
+         MappedMemoryRegion::EXECUTE},
+    {"00400000-0040b000 ---p 00000000 fc:00 794418 /bin/cat\n",
+     MappedMemoryRegion::PRIVATE},
+    {"00400000-0040b000 r--p 00000000 fc:00 794418 /bin/cat\n",
+     MappedMemoryRegion::READ | MappedMemoryRegion::PRIVATE},
+    {"00400000-0040b000 -w-p 00000000 fc:00 794418 /bin/cat\n",
+     MappedMemoryRegion::WRITE | MappedMemoryRegion::PRIVATE},
+    {"00400000-0040b000 --xp 00000000 fc:00 794418 /bin/cat\n",
+     MappedMemoryRegion::EXECUTE | MappedMemoryRegion::PRIVATE},
+    {"00400000-0040b000 rwxp 00000000 fc:00 794418 /bin/cat\n",
+     MappedMemoryRegion::READ | MappedMemoryRegion::WRITE |
+         MappedMemoryRegion::EXECUTE | MappedMemoryRegion::PRIVATE},
+  };
+
+  for (size_t i = 0; i < arraysize(kTestCases); ++i) {
+    SCOPED_TRACE(
+        base::StringPrintf("kTestCases[%zu] = %s", i, kTestCases[i].input));
+
+    std::vector<MappedMemoryRegion> regions;
+    EXPECT_TRUE(ParseProcMaps(kTestCases[i].input, &regions));
+    EXPECT_EQ(1u, regions.size());
+    if (regions.empty())
+      continue;
+    EXPECT_EQ(kTestCases[i].permissions, regions[0].permissions);
+  }
+}
+
+#if defined(ADDRESS_SANITIZER)
+// AddressSanitizer may move local variables to a dedicated "fake stack" which
+// is outside the stack region listed in /proc/self/maps. We disable ASan
+// instrumentation for this function to force the variable to be local.
+__attribute__((no_sanitize_address))
+#endif
+void CheckProcMapsRegions(const std::vector<MappedMemoryRegion> &regions) {
+  // We should be able to find both the current executable as well as the stack
+  // mapped into memory. Use the address of |exe_path| as a way of finding the
+  // stack.
+  FilePath exe_path;
+  EXPECT_TRUE(PathService::Get(FILE_EXE, &exe_path));
+  uintptr_t address = reinterpret_cast<uintptr_t>(&exe_path);
+  bool found_exe = false;
+  bool found_stack = false;
+  bool found_address = false;
+
+  for (size_t i = 0; i < regions.size(); ++i) {
+    if (regions[i].path == exe_path.value()) {
+      // It's OK to find the executable mapped multiple times as there'll be
+      // multiple sections (e.g., text, data).
+      found_exe = true;
+    }
+
+    if (regions[i].path == "[stack]") {
+// On Android the test is run on a background thread, since [stack] is for
+// the main thread, we cannot test this.
+#if !defined(OS_ANDROID)
+      EXPECT_GE(address, regions[i].start);
+      EXPECT_LT(address, regions[i].end);
+#endif
+      EXPECT_TRUE(regions[i].permissions & MappedMemoryRegion::READ);
+      EXPECT_TRUE(regions[i].permissions & MappedMemoryRegion::WRITE);
+      EXPECT_FALSE(regions[i].permissions & MappedMemoryRegion::EXECUTE);
+      EXPECT_TRUE(regions[i].permissions & MappedMemoryRegion::PRIVATE);
+      EXPECT_FALSE(found_stack) << "Found duplicate stacks";
+      found_stack = true;
+    }
+
+    if (address >= regions[i].start && address < regions[i].end) {
+      EXPECT_FALSE(found_address) << "Found same address in multiple regions";
+      found_address = true;
+    }
+  }
+
+  EXPECT_TRUE(found_exe);
+  EXPECT_TRUE(found_stack);
+  EXPECT_TRUE(found_address);
+}
+
+TEST(ProcMapsTest, ReadProcMaps) {
+  std::string proc_maps;
+  ASSERT_TRUE(ReadProcMaps(&proc_maps));
+
+  std::vector<MappedMemoryRegion> regions;
+  ASSERT_TRUE(ParseProcMaps(proc_maps, &regions));
+  ASSERT_FALSE(regions.empty());
+
+  CheckProcMapsRegions(regions);
+}
+
+TEST(ProcMapsTest, ReadProcMapsNonEmptyString) {
+  std::string old_string("I forgot to clear the string");
+  std::string proc_maps(old_string);
+  ASSERT_TRUE(ReadProcMaps(&proc_maps));
+  EXPECT_EQ(std::string::npos, proc_maps.find(old_string));
+}
+
+TEST(ProcMapsTest, MissingFields) {
+  static const char* const kTestCases[] = {
+    "00400000\n",                               // Missing end + beyond.
+    "00400000-0040b000\n",                      // Missing perms + beyond.
+    "00400000-0040b000 r-xp\n",                 // Missing offset + beyond.
+    "00400000-0040b000 r-xp 00000000\n",        // Missing device + beyond.
+    "00400000-0040b000 r-xp 00000000 fc:00\n",  // Missing inode + beyond.
+    "00400000-0040b000 00000000 fc:00 794418 /bin/cat\n",  // Missing perms.
+    "00400000-0040b000 r-xp fc:00 794418 /bin/cat\n",      // Missing offset.
+    "00400000-0040b000 r-xp 00000000 fc:00 /bin/cat\n",    // Missing inode.
+    "00400000 r-xp 00000000 fc:00 794418 /bin/cat\n",      // Missing end.
+    "-0040b000 r-xp 00000000 fc:00 794418 /bin/cat\n",     // Missing start.
+    "00400000-0040b000 r-xp 00000000 794418 /bin/cat\n",   // Missing device.
+  };
+
+  for (size_t i = 0; i < arraysize(kTestCases); ++i) {
+    SCOPED_TRACE(base::StringPrintf("kTestCases[%zu] = %s", i, kTestCases[i]));
+    std::vector<MappedMemoryRegion> regions;
+    EXPECT_FALSE(ParseProcMaps(kTestCases[i], &regions));
+  }
+}
+
+TEST(ProcMapsTest, InvalidInput) {
+  static const char* const kTestCases[] = {
+    "thisisal-0040b000 rwxp 00000000 fc:00 794418 /bin/cat\n",
+    "0040000d-linvalid rwxp 00000000 fc:00 794418 /bin/cat\n",
+    "00400000-0040b000 inpu 00000000 fc:00 794418 /bin/cat\n",
+    "00400000-0040b000 rwxp tforproc fc:00 794418 /bin/cat\n",
+    "00400000-0040b000 rwxp 00000000 ma:ps 794418 /bin/cat\n",
+    "00400000-0040b000 rwxp 00000000 fc:00 parse! /bin/cat\n",
+  };
+
+  for (size_t i = 0; i < arraysize(kTestCases); ++i) {
+    SCOPED_TRACE(base::StringPrintf("kTestCases[%zu] = %s", i, kTestCases[i]));
+    std::vector<MappedMemoryRegion> regions;
+    EXPECT_FALSE(ParseProcMaps(kTestCases[i], &regions));
+  }
+}
+
+TEST(ProcMapsTest, ParseProcMapsEmptyString) {
+  std::vector<MappedMemoryRegion> regions;
+  EXPECT_TRUE(ParseProcMaps("", &regions));
+  EXPECT_EQ(0ULL, regions.size());
+}
+
+// Testing a couple of remotely possible weird things in the input:
+// - Line ending with \r\n or \n\r.
+// - File name contains quotes.
+// - File name has whitespaces.
+TEST(ProcMapsTest, ParseProcMapsWeirdCorrectInput) {
+  std::vector<MappedMemoryRegion> regions;
+  const std::string kContents =
+    "00400000-0040b000 r-xp 00000000 fc:00 2106562 "
+      "               /bin/cat\r\n"
+    "7f53b7dad000-7f53b7f62000 r-xp 00000000 fc:00 263011 "
+      "       /lib/x86_64-linux-gnu/libc-2.15.so\n\r"
+    "7f53b816d000-7f53b818f000 r-xp 00000000 fc:00 264284 "
+      "        /lib/x86_64-linux-gnu/ld-2.15.so\n"
+    "7fff9c7ff000-7fff9c800000 r-xp 00000000 00:00 0 "
+      "               \"vd so\"\n"
+    "ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 "
+      "               [vsys call]\n";
+  EXPECT_TRUE(ParseProcMaps(kContents, &regions));
+  EXPECT_EQ(5ULL, regions.size());
+  EXPECT_EQ("/bin/cat", regions[0].path);
+  EXPECT_EQ("/lib/x86_64-linux-gnu/libc-2.15.so", regions[1].path);
+  EXPECT_EQ("/lib/x86_64-linux-gnu/ld-2.15.so", regions[2].path);
+  EXPECT_EQ("\"vd so\"", regions[3].path);
+  EXPECT_EQ("[vsys call]", regions[4].path);
+}
+
+}  // namespace debug
+}  // namespace base
diff --git a/base/debug/stack_trace_unittest.cc b/base/debug/stack_trace_unittest.cc
new file mode 100644
index 0000000..02f076a
--- /dev/null
+++ b/base/debug/stack_trace_unittest.cc
@@ -0,0 +1,320 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+
+#include <limits>
+#include <sstream>
+#include <string>
+
+#include "base/debug/debugging_buildflags.h"
+#include "base/debug/stack_trace.h"
+#include "base/logging.h"
+#include "base/process/kill.h"
+#include "base/process/process_handle.h"
+#include "base/test/test_timeouts.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/multiprocess_func_list.h"
+
+#if defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_IOS)
+#include "base/test/multiprocess_test.h"
+#endif
+
+namespace base {
+namespace debug {
+
+#if defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_IOS)
+typedef MultiProcessTest StackTraceTest;
+#else
+typedef testing::Test StackTraceTest;
+#endif
+
+// Note: On Linux, this test currently only fully works on Debug builds.
+// See comments in the #ifdef soup if you intend to change this.
+#if defined(OS_WIN)
+// Always fails on Windows: crbug.com/32070
+#define MAYBE_OutputToStream DISABLED_OutputToStream
+#else
+#define MAYBE_OutputToStream OutputToStream
+#endif
+#if !defined(__UCLIBC__) && !defined(_AIX)
+TEST_F(StackTraceTest, MAYBE_OutputToStream) {
+  StackTrace trace;
+
+  // Dump the trace into a string.
+  std::ostringstream os;
+  trace.OutputToStream(&os);
+  std::string backtrace_message = os.str();
+
+  // ToString() should produce the same output.
+  EXPECT_EQ(backtrace_message, trace.ToString());
+
+#if defined(OS_POSIX) && !defined(OS_MACOSX) && NDEBUG
+  // Stack traces require an extra data table that bloats our binaries,
+  // so they're turned off for release builds.  We stop the test here,
+  // at least letting us verify that the calls don't crash.
+  return;
+#endif  // defined(OS_POSIX) && !defined(OS_MACOSX) && NDEBUG
+
+  size_t frames_found = 0;
+  trace.Addresses(&frames_found);
+  ASSERT_GE(frames_found, 5u) <<
+      "No stack frames found.  Skipping rest of test.";
+
+  // Check if the output has symbol initialization warning.  If it does, fail.
+  ASSERT_EQ(backtrace_message.find("Dumping unresolved backtrace"),
+            std::string::npos) <<
+      "Unable to resolve symbols.  Skipping rest of test.";
+
+#if defined(OS_MACOSX)
+#if 0
+  // Disabled due to -fvisibility=hidden in build config.
+
+  // Symbol resolution via the backtrace_symbol function does not work well
+  // in OS X.
+  // See this thread:
+  //
+  //    http://lists.apple.com/archives/darwin-dev/2009/Mar/msg00111.html
+  //
+  // Just check instead that we find our way back to the "start" symbol
+  // which should be the first symbol in the trace.
+  //
+  // TODO(port): Find a more reliable way to resolve symbols.
+
+  // Expect to at least find main.
+  EXPECT_TRUE(backtrace_message.find("start") != std::string::npos)
+      << "Expected to find start in backtrace:\n"
+      << backtrace_message;
+
+#endif
+#elif defined(USE_SYMBOLIZE)
+  // This branch is for gcc-compiled code, but not Mac due to the
+  // above #if.
+  // Expect a demangled symbol.
+  EXPECT_TRUE(backtrace_message.find("testing::Test::Run()") !=
+              std::string::npos)
+      << "Expected a demangled symbol in backtrace:\n"
+      << backtrace_message;
+
+#elif 0
+  // This is the fall-through case; it used to cover Windows.
+  // But it's disabled because of varying buildbot configs;
+  // some lack symbols.
+
+  // Expect to at least find main.
+  EXPECT_TRUE(backtrace_message.find("main") != std::string::npos)
+      << "Expected to find main in backtrace:\n"
+      << backtrace_message;
+
+#if defined(OS_WIN)
+// MSVC doesn't allow the use of C99's __func__ within C++, so we fake it with
+// MSVC's __FUNCTION__ macro.
+#define __func__ __FUNCTION__
+#endif
+
+  // Expect to find this function as well.
+  // Note: This will fail if not linked with -rdynamic (aka -export_dynamic)
+  EXPECT_TRUE(backtrace_message.find(__func__) != std::string::npos)
+      << "Expected to find " << __func__ << " in backtrace:\n"
+      << backtrace_message;
+
+#endif  // define(OS_MACOSX)
+}
+
+#if !defined(OFFICIAL_BUILD) && !defined(NO_UNWIND_TABLES)
+// Disabled in Official builds, where Link-Time Optimization can result in two
+// or fewer stack frames being available, causing the test to fail.
+TEST_F(StackTraceTest, TruncatedTrace) {
+  StackTrace trace;
+
+  size_t count = 0;
+  trace.Addresses(&count);
+  ASSERT_LT(2u, count);
+
+  StackTrace truncated(2);
+  truncated.Addresses(&count);
+  EXPECT_EQ(2u, count);
+}
+#endif  // !defined(OFFICIAL_BUILD)
+
+// The test is used for manual testing, e.g., to see the raw output.
+TEST_F(StackTraceTest, DebugOutputToStream) {
+  StackTrace trace;
+  std::ostringstream os;
+  trace.OutputToStream(&os);
+  VLOG(1) << os.str();
+}
+
+// The test is used for manual testing, e.g., to see the raw output.
+TEST_F(StackTraceTest, DebugPrintBacktrace) {
+  StackTrace().Print();
+}
+#endif  // !defined(__UCLIBC__)
+
+#if defined(OS_POSIX) && !defined(OS_ANDROID)
+#if !defined(OS_IOS)
+static char* newArray() {
+  // Clang warns about the mismatched new[]/delete if they occur in the same
+  // function.
+  return new char[10];
+}
+
+MULTIPROCESS_TEST_MAIN(MismatchedMallocChildProcess) {
+  char* pointer = newArray();
+  delete pointer;
+  return 2;
+}
+
+// Regression test for StackDumpingSignalHandler async-signal unsafety.
+// Combined with tcmalloc's debugallocation, that signal handler
+// and e.g. mismatched new[]/delete would cause a hang because
+// of re-entering malloc.
+TEST_F(StackTraceTest, AsyncSignalUnsafeSignalHandlerHang) {
+  Process child = SpawnChild("MismatchedMallocChildProcess");
+  ASSERT_TRUE(child.IsValid());
+  int exit_code;
+  ASSERT_TRUE(
+      child.WaitForExitWithTimeout(TestTimeouts::action_timeout(), &exit_code));
+}
+#endif  // !defined(OS_IOS)
+
+namespace {
+
+std::string itoa_r_wrapper(intptr_t i, size_t sz, int base, size_t padding) {
+  char buffer[1024];
+  CHECK_LE(sz, sizeof(buffer));
+
+  char* result = internal::itoa_r(i, buffer, sz, base, padding);
+  EXPECT_TRUE(result);
+  return std::string(buffer);
+}
+
+}  // namespace
+
+TEST_F(StackTraceTest, itoa_r) {
+  EXPECT_EQ("0", itoa_r_wrapper(0, 128, 10, 0));
+  EXPECT_EQ("-1", itoa_r_wrapper(-1, 128, 10, 0));
+
+  // Test edge cases.
+  if (sizeof(intptr_t) == 4) {
+    EXPECT_EQ("ffffffff", itoa_r_wrapper(-1, 128, 16, 0));
+    EXPECT_EQ("-2147483648",
+              itoa_r_wrapper(std::numeric_limits<intptr_t>::min(), 128, 10, 0));
+    EXPECT_EQ("2147483647",
+              itoa_r_wrapper(std::numeric_limits<intptr_t>::max(), 128, 10, 0));
+
+    EXPECT_EQ("80000000",
+              itoa_r_wrapper(std::numeric_limits<intptr_t>::min(), 128, 16, 0));
+    EXPECT_EQ("7fffffff",
+              itoa_r_wrapper(std::numeric_limits<intptr_t>::max(), 128, 16, 0));
+  } else if (sizeof(intptr_t) == 8) {
+    EXPECT_EQ("ffffffffffffffff", itoa_r_wrapper(-1, 128, 16, 0));
+    EXPECT_EQ("-9223372036854775808",
+              itoa_r_wrapper(std::numeric_limits<intptr_t>::min(), 128, 10, 0));
+    EXPECT_EQ("9223372036854775807",
+              itoa_r_wrapper(std::numeric_limits<intptr_t>::max(), 128, 10, 0));
+
+    EXPECT_EQ("8000000000000000",
+              itoa_r_wrapper(std::numeric_limits<intptr_t>::min(), 128, 16, 0));
+    EXPECT_EQ("7fffffffffffffff",
+              itoa_r_wrapper(std::numeric_limits<intptr_t>::max(), 128, 16, 0));
+  } else {
+    ADD_FAILURE() << "Missing test case for your size of intptr_t ("
+                  << sizeof(intptr_t) << ")";
+  }
+
+  // Test hex output.
+  EXPECT_EQ("688", itoa_r_wrapper(0x688, 128, 16, 0));
+  EXPECT_EQ("deadbeef", itoa_r_wrapper(0xdeadbeef, 128, 16, 0));
+
+  // Check that itoa_r respects passed buffer size limit.
+  char buffer[1024];
+  EXPECT_TRUE(internal::itoa_r(0xdeadbeef, buffer, 10, 16, 0));
+  EXPECT_TRUE(internal::itoa_r(0xdeadbeef, buffer, 9, 16, 0));
+  EXPECT_FALSE(internal::itoa_r(0xdeadbeef, buffer, 8, 16, 0));
+  EXPECT_FALSE(internal::itoa_r(0xdeadbeef, buffer, 7, 16, 0));
+  EXPECT_TRUE(internal::itoa_r(0xbeef, buffer, 5, 16, 4));
+  EXPECT_FALSE(internal::itoa_r(0xbeef, buffer, 5, 16, 5));
+  EXPECT_FALSE(internal::itoa_r(0xbeef, buffer, 5, 16, 6));
+
+  // Test padding.
+  EXPECT_EQ("1", itoa_r_wrapper(1, 128, 10, 0));
+  EXPECT_EQ("1", itoa_r_wrapper(1, 128, 10, 1));
+  EXPECT_EQ("01", itoa_r_wrapper(1, 128, 10, 2));
+  EXPECT_EQ("001", itoa_r_wrapper(1, 128, 10, 3));
+  EXPECT_EQ("0001", itoa_r_wrapper(1, 128, 10, 4));
+  EXPECT_EQ("00001", itoa_r_wrapper(1, 128, 10, 5));
+  EXPECT_EQ("688", itoa_r_wrapper(0x688, 128, 16, 0));
+  EXPECT_EQ("688", itoa_r_wrapper(0x688, 128, 16, 1));
+  EXPECT_EQ("688", itoa_r_wrapper(0x688, 128, 16, 2));
+  EXPECT_EQ("688", itoa_r_wrapper(0x688, 128, 16, 3));
+  EXPECT_EQ("0688", itoa_r_wrapper(0x688, 128, 16, 4));
+  EXPECT_EQ("00688", itoa_r_wrapper(0x688, 128, 16, 5));
+}
+#endif  // defined(OS_POSIX) && !defined(OS_ANDROID)
+
+#if BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)
+
+template <size_t Depth>
+void NOINLINE ExpectStackFramePointers(const void** frames,
+                                       size_t max_depth) {
+  code_start:
+  // Calling __builtin_frame_address() forces compiler to emit
+  // frame pointers, even if they are not enabled.
+  EXPECT_NE(nullptr, __builtin_frame_address(0));
+  ExpectStackFramePointers<Depth - 1>(frames, max_depth);
+
+  constexpr size_t frame_index = Depth - 1;
+  const void* frame = frames[frame_index];
+  EXPECT_GE(frame, &&code_start) << "For frame at index " << frame_index;
+  EXPECT_LE(frame, &&code_end) << "For frame at index " << frame_index;
+  code_end: return;
+}
+
+template <>
+void NOINLINE ExpectStackFramePointers<1>(const void** frames,
+                                          size_t max_depth) {
+  code_start:
+  // Calling __builtin_frame_address() forces compiler to emit
+  // frame pointers, even if they are not enabled.
+  EXPECT_NE(nullptr, __builtin_frame_address(0));
+  size_t count = TraceStackFramePointers(frames, max_depth, 0);
+  ASSERT_EQ(max_depth, count);
+
+  const void* frame = frames[0];
+  EXPECT_GE(frame, &&code_start) << "For the top frame";
+  EXPECT_LE(frame, &&code_end) << "For the top frame";
+  code_end: return;
+}
+
+#if defined(MEMORY_SANITIZER)
+// The test triggers use-of-uninitialized-value errors on MSan bots.
+// This is expected because we're walking and reading the stack, and
+// sometimes we read fp / pc from the place that previously held
+// uninitialized value.
+#define MAYBE_TraceStackFramePointers DISABLED_TraceStackFramePointers
+#else
+#define MAYBE_TraceStackFramePointers TraceStackFramePointers
+#endif
+TEST_F(StackTraceTest, MAYBE_TraceStackFramePointers) {
+  constexpr size_t kDepth = 5;
+  const void* frames[kDepth];
+  ExpectStackFramePointers<kDepth>(frames, kDepth);
+}
+
+#if defined(OS_ANDROID) || defined(OS_MACOSX)
+#define MAYBE_StackEnd StackEnd
+#else
+#define MAYBE_StackEnd DISABLED_StackEnd
+#endif
+
+TEST_F(StackTraceTest, MAYBE_StackEnd) {
+  EXPECT_NE(0u, GetStackEnd());
+}
+
+#endif  // BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)
+
+}  // namespace debug
+}  // namespace base
diff --git a/base/debug/thread_heap_usage_tracker.cc b/base/debug/thread_heap_usage_tracker.cc
new file mode 100644
index 0000000..6d00b1c
--- /dev/null
+++ b/base/debug/thread_heap_usage_tracker.cc
@@ -0,0 +1,340 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/debug/thread_heap_usage_tracker.h"
+
+#include <stdint.h>
+#include <algorithm>
+#include <limits>
+#include <new>
+#include <type_traits>
+
+#include "base/allocator/allocator_shim.h"
+#include "base/allocator/buildflags.h"
+#include "base/logging.h"
+#include "base/no_destructor.h"
+#include "base/threading/thread_local_storage.h"
+#include "build/build_config.h"
+
+#if defined(OS_MACOSX) || defined(OS_IOS)
+#include <malloc/malloc.h>
+#else
+#include <malloc.h>
+#endif
+
+namespace base {
+namespace debug {
+
+namespace {
+
+using base::allocator::AllocatorDispatch;
+
+const uintptr_t kSentinelMask = std::numeric_limits<uintptr_t>::max() - 1;
+ThreadHeapUsage* const kInitializationSentinel =
+    reinterpret_cast<ThreadHeapUsage*>(kSentinelMask);
+ThreadHeapUsage* const kTeardownSentinel =
+    reinterpret_cast<ThreadHeapUsage*>(kSentinelMask | 1);
+
+ThreadLocalStorage::Slot& ThreadAllocationUsage() {
+  static NoDestructor<ThreadLocalStorage::Slot> thread_allocator_usage(
+      [](void* thread_heap_usage) {
+        // This destructor will be called twice. Once to destroy the actual
+        // ThreadHeapUsage instance and a second time, immediately after, for
+        // the sentinel. Re-setting the TLS slow (below) does re-initialize the
+        // TLS slot. The ThreadLocalStorage code is designed to deal with this
+        // use case and will re-call the destructor with the kTeardownSentinel
+        // as arg.
+        if (thread_heap_usage == kTeardownSentinel)
+          return;
+        DCHECK_NE(thread_heap_usage, kInitializationSentinel);
+
+        // Deleting the ThreadHeapUsage TLS object will re-enter the shim and
+        // hit RecordFree() (see below). The sentinel prevents RecordFree() from
+        // re-creating another ThreadHeapUsage object.
+        ThreadAllocationUsage().Set(kTeardownSentinel);
+        delete static_cast<ThreadHeapUsage*>(thread_heap_usage);
+      });
+  return *thread_allocator_usage;
+}
+
+bool g_heap_tracking_enabled = false;
+
+// Forward declared as it needs to delegate memory allocation to the next
+// lower shim.
+ThreadHeapUsage* GetOrCreateThreadUsage();
+
+size_t GetAllocSizeEstimate(const AllocatorDispatch* next,
+                            void* ptr,
+                            void* context) {
+  if (ptr == nullptr)
+    return 0U;
+
+  return next->get_size_estimate_function(next, ptr, context);
+}
+
+void RecordAlloc(const AllocatorDispatch* next,
+                 void* ptr,
+                 size_t size,
+                 void* context) {
+  ThreadHeapUsage* usage = GetOrCreateThreadUsage();
+  if (usage == nullptr)
+    return;
+
+  usage->alloc_ops++;
+  size_t estimate = GetAllocSizeEstimate(next, ptr, context);
+  if (size && estimate) {
+    // Only keep track of the net number of bytes allocated in the scope if the
+    // size estimate function returns sane values, e.g. non-zero.
+    usage->alloc_bytes += estimate;
+    usage->alloc_overhead_bytes += estimate - size;
+
+    // Record the max outstanding number of bytes, but only if the difference
+    // is net positive (e.g. more bytes allocated than freed in the scope).
+    if (usage->alloc_bytes > usage->free_bytes) {
+      uint64_t allocated_bytes = usage->alloc_bytes - usage->free_bytes;
+      if (allocated_bytes > usage->max_allocated_bytes)
+        usage->max_allocated_bytes = allocated_bytes;
+    }
+  } else {
+    usage->alloc_bytes += size;
+  }
+}
+
+void RecordFree(const AllocatorDispatch* next, void* ptr, void* context) {
+  ThreadHeapUsage* usage = GetOrCreateThreadUsage();
+  if (usage == nullptr)
+    return;
+
+  size_t estimate = GetAllocSizeEstimate(next, ptr, context);
+  usage->free_ops++;
+  usage->free_bytes += estimate;
+}
+
+void* AllocFn(const AllocatorDispatch* self, size_t size, void* context) {
+  void* ret = self->next->alloc_function(self->next, size, context);
+  if (ret != nullptr)
+    RecordAlloc(self->next, ret, size, context);
+
+  return ret;
+}
+
+void* AllocZeroInitializedFn(const AllocatorDispatch* self,
+                             size_t n,
+                             size_t size,
+                             void* context) {
+  void* ret =
+      self->next->alloc_zero_initialized_function(self->next, n, size, context);
+  if (ret != nullptr)
+    RecordAlloc(self->next, ret, size, context);
+
+  return ret;
+}
+
+void* AllocAlignedFn(const AllocatorDispatch* self,
+                     size_t alignment,
+                     size_t size,
+                     void* context) {
+  void* ret =
+      self->next->alloc_aligned_function(self->next, alignment, size, context);
+  if (ret != nullptr)
+    RecordAlloc(self->next, ret, size, context);
+
+  return ret;
+}
+
+void* ReallocFn(const AllocatorDispatch* self,
+                void* address,
+                size_t size,
+                void* context) {
+  if (address != nullptr)
+    RecordFree(self->next, address, context);
+
+  void* ret = self->next->realloc_function(self->next, address, size, context);
+  if (ret != nullptr && size != 0)
+    RecordAlloc(self->next, ret, size, context);
+
+  return ret;
+}
+
+void FreeFn(const AllocatorDispatch* self, void* address, void* context) {
+  if (address != nullptr)
+    RecordFree(self->next, address, context);
+  self->next->free_function(self->next, address, context);
+}
+
+size_t GetSizeEstimateFn(const AllocatorDispatch* self,
+                         void* address,
+                         void* context) {
+  return self->next->get_size_estimate_function(self->next, address, context);
+}
+
+unsigned BatchMallocFn(const AllocatorDispatch* self,
+                       size_t size,
+                       void** results,
+                       unsigned num_requested,
+                       void* context) {
+  unsigned count = self->next->batch_malloc_function(self->next, size, results,
+                                                     num_requested, context);
+  for (unsigned i = 0; i < count; ++i) {
+    RecordAlloc(self->next, results[i], size, context);
+  }
+  return count;
+}
+
+void BatchFreeFn(const AllocatorDispatch* self,
+                 void** to_be_freed,
+                 unsigned num_to_be_freed,
+                 void* context) {
+  for (unsigned i = 0; i < num_to_be_freed; ++i) {
+    if (to_be_freed[i] != nullptr) {
+      RecordFree(self->next, to_be_freed[i], context);
+    }
+  }
+  self->next->batch_free_function(self->next, to_be_freed, num_to_be_freed,
+                                  context);
+}
+
+void FreeDefiniteSizeFn(const AllocatorDispatch* self,
+                        void* ptr,
+                        size_t size,
+                        void* context) {
+  if (ptr != nullptr)
+    RecordFree(self->next, ptr, context);
+  self->next->free_definite_size_function(self->next, ptr, size, context);
+}
+
+// The allocator dispatch used to intercept heap operations.
+AllocatorDispatch allocator_dispatch = {&AllocFn,
+                                        &AllocZeroInitializedFn,
+                                        &AllocAlignedFn,
+                                        &ReallocFn,
+                                        &FreeFn,
+                                        &GetSizeEstimateFn,
+                                        &BatchMallocFn,
+                                        &BatchFreeFn,
+                                        &FreeDefiniteSizeFn,
+                                        nullptr};
+
+ThreadHeapUsage* GetOrCreateThreadUsage() {
+  auto tls_ptr = reinterpret_cast<uintptr_t>(ThreadAllocationUsage().Get());
+  if ((tls_ptr & kSentinelMask) == kSentinelMask)
+    return nullptr;  // Re-entrancy case.
+
+  auto* allocator_usage = reinterpret_cast<ThreadHeapUsage*>(tls_ptr);
+  if (allocator_usage == nullptr) {
+    // Prevent reentrancy due to the allocation below.
+    ThreadAllocationUsage().Set(kInitializationSentinel);
+
+    allocator_usage = new ThreadHeapUsage();
+    static_assert(std::is_pod<ThreadHeapUsage>::value,
+                  "AllocatorDispatch must be POD");
+    memset(allocator_usage, 0, sizeof(*allocator_usage));
+    ThreadAllocationUsage().Set(allocator_usage);
+  }
+
+  return allocator_usage;
+}
+
+}  // namespace
+
+ThreadHeapUsageTracker::ThreadHeapUsageTracker() : thread_usage_(nullptr) {
+  static_assert(std::is_pod<ThreadHeapUsage>::value, "Must be POD.");
+}
+
+ThreadHeapUsageTracker::~ThreadHeapUsageTracker() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  if (thread_usage_ != nullptr) {
+    // If this tracker wasn't stopped, make it inclusive so that the
+    // usage isn't lost.
+    Stop(false);
+  }
+}
+
+void ThreadHeapUsageTracker::Start() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  thread_usage_ = GetOrCreateThreadUsage();
+  usage_ = *thread_usage_;
+
+  // Reset the stats for our current scope.
+  // The per-thread usage instance now tracks this scope's usage, while this
+  // instance persists the outer scope's usage stats. On destruction, this
+  // instance will restore the outer scope's usage stats with this scope's
+  // usage added.
+  memset(thread_usage_, 0, sizeof(*thread_usage_));
+}
+
+void ThreadHeapUsageTracker::Stop(bool usage_is_exclusive) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_NE(nullptr, thread_usage_);
+
+  ThreadHeapUsage current = *thread_usage_;
+  if (usage_is_exclusive) {
+    // Restore the outer scope.
+    *thread_usage_ = usage_;
+  } else {
+    // Update the outer scope with the accrued inner usage.
+    if (thread_usage_->max_allocated_bytes) {
+      uint64_t outer_net_alloc_bytes = usage_.alloc_bytes - usage_.free_bytes;
+
+      thread_usage_->max_allocated_bytes =
+          std::max(usage_.max_allocated_bytes,
+                   outer_net_alloc_bytes + thread_usage_->max_allocated_bytes);
+    }
+
+    thread_usage_->alloc_ops += usage_.alloc_ops;
+    thread_usage_->alloc_bytes += usage_.alloc_bytes;
+    thread_usage_->alloc_overhead_bytes += usage_.alloc_overhead_bytes;
+    thread_usage_->free_ops += usage_.free_ops;
+    thread_usage_->free_bytes += usage_.free_bytes;
+  }
+
+  thread_usage_ = nullptr;
+  usage_ = current;
+}
+
+ThreadHeapUsage ThreadHeapUsageTracker::GetUsageSnapshot() {
+  ThreadHeapUsage* usage = GetOrCreateThreadUsage();
+  DCHECK_NE(nullptr, usage);
+  return *usage;
+}
+
+void ThreadHeapUsageTracker::EnableHeapTracking() {
+  EnsureTLSInitialized();
+
+  CHECK_EQ(false, g_heap_tracking_enabled) << "No double-enabling.";
+  g_heap_tracking_enabled = true;
+#if BUILDFLAG(USE_ALLOCATOR_SHIM)
+  base::allocator::InsertAllocatorDispatch(&allocator_dispatch);
+#else
+  CHECK(false) << "Can't enable heap tracking without the shim.";
+#endif  // BUILDFLAG(USE_ALLOCATOR_SHIM)
+}
+
+bool ThreadHeapUsageTracker::IsHeapTrackingEnabled() {
+  return g_heap_tracking_enabled;
+}
+
+void ThreadHeapUsageTracker::DisableHeapTrackingForTesting() {
+#if BUILDFLAG(USE_ALLOCATOR_SHIM)
+  base::allocator::RemoveAllocatorDispatchForTesting(&allocator_dispatch);
+#else
+  CHECK(false) << "Can't disable heap tracking without the shim.";
+#endif  // BUILDFLAG(USE_ALLOCATOR_SHIM)
+  DCHECK_EQ(true, g_heap_tracking_enabled) << "Heap tracking not enabled.";
+  g_heap_tracking_enabled = false;
+}
+
+base::allocator::AllocatorDispatch*
+ThreadHeapUsageTracker::GetDispatchForTesting() {
+  return &allocator_dispatch;
+}
+
+void ThreadHeapUsageTracker::EnsureTLSInitialized() {
+  ignore_result(ThreadAllocationUsage());
+}
+
+}  // namespace debug
+}  // namespace base
diff --git a/base/debug/thread_heap_usage_tracker_unittest.cc b/base/debug/thread_heap_usage_tracker_unittest.cc
new file mode 100644
index 0000000..b99576c
--- /dev/null
+++ b/base/debug/thread_heap_usage_tracker_unittest.cc
@@ -0,0 +1,607 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/debug/thread_heap_usage_tracker.h"
+
+#include <map>
+
+#include "base/allocator/allocator_shim.h"
+#include "base/allocator/buildflags.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if defined(OS_MACOSX)
+#include "base/allocator/allocator_interception_mac.h"
+#endif
+
+namespace base {
+namespace debug {
+
+namespace {
+
+class TestingThreadHeapUsageTracker : public ThreadHeapUsageTracker {
+ public:
+  using ThreadHeapUsageTracker::DisableHeapTrackingForTesting;
+  using ThreadHeapUsageTracker::EnsureTLSInitialized;
+  using ThreadHeapUsageTracker::GetDispatchForTesting;
+};
+
+// A fixture class that allows testing the AllocatorDispatch associated with
+// the ThreadHeapUsageTracker class in isolation against a mocked
+// underlying
+// heap implementation.
+class ThreadHeapUsageTrackerTest : public testing::Test {
+ public:
+  using AllocatorDispatch = base::allocator::AllocatorDispatch;
+
+  static const size_t kAllocationPadding;
+  enum SizeFunctionKind {
+    EXACT_SIZE_FUNCTION,
+    PADDING_SIZE_FUNCTION,
+    ZERO_SIZE_FUNCTION,
+  };
+
+  ThreadHeapUsageTrackerTest() : size_function_kind_(EXACT_SIZE_FUNCTION) {
+    EXPECT_EQ(nullptr, g_self);
+    g_self = this;
+  }
+
+  ~ThreadHeapUsageTrackerTest() override {
+    EXPECT_EQ(this, g_self);
+    g_self = nullptr;
+  }
+
+  void set_size_function_kind(SizeFunctionKind kind) {
+    size_function_kind_ = kind;
+  }
+
+  void SetUp() override {
+    TestingThreadHeapUsageTracker::EnsureTLSInitialized();
+
+    dispatch_under_test_ =
+        TestingThreadHeapUsageTracker::GetDispatchForTesting();
+    ASSERT_EQ(nullptr, dispatch_under_test_->next);
+
+    dispatch_under_test_->next = &g_mock_dispatch;
+  }
+
+  void TearDown() override {
+    ASSERT_EQ(&g_mock_dispatch, dispatch_under_test_->next);
+
+    dispatch_under_test_->next = nullptr;
+  }
+
+  void* MockMalloc(size_t size) {
+    return dispatch_under_test_->alloc_function(dispatch_under_test_, size,
+                                                nullptr);
+  }
+
+  void* MockCalloc(size_t n, size_t size) {
+    return dispatch_under_test_->alloc_zero_initialized_function(
+        dispatch_under_test_, n, size, nullptr);
+  }
+
+  void* MockAllocAligned(size_t alignment, size_t size) {
+    return dispatch_under_test_->alloc_aligned_function(
+        dispatch_under_test_, alignment, size, nullptr);
+  }
+
+  void* MockRealloc(void* address, size_t size) {
+    return dispatch_under_test_->realloc_function(dispatch_under_test_, address,
+                                                  size, nullptr);
+  }
+
+  void MockFree(void* address) {
+    dispatch_under_test_->free_function(dispatch_under_test_, address, nullptr);
+  }
+
+  size_t MockGetSizeEstimate(void* address) {
+    return dispatch_under_test_->get_size_estimate_function(
+        dispatch_under_test_, address, nullptr);
+  }
+
+ private:
+  void RecordAlloc(void* address, size_t size) {
+    if (address != nullptr)
+      allocation_size_map_[address] = size;
+  }
+
+  void DeleteAlloc(void* address) {
+    if (address != nullptr)
+      EXPECT_EQ(1U, allocation_size_map_.erase(address));
+  }
+
+  size_t GetSizeEstimate(void* address) {
+    auto it = allocation_size_map_.find(address);
+    if (it == allocation_size_map_.end())
+      return 0;
+
+    size_t ret = it->second;
+    switch (size_function_kind_) {
+      case EXACT_SIZE_FUNCTION:
+        break;
+      case PADDING_SIZE_FUNCTION:
+        ret += kAllocationPadding;
+        break;
+      case ZERO_SIZE_FUNCTION:
+        ret = 0;
+        break;
+    }
+
+    return ret;
+  }
+
+  static void* OnAllocFn(const AllocatorDispatch* self,
+                         size_t size,
+                         void* context) {
+    EXPECT_EQ(&g_mock_dispatch, self);
+
+    void* ret = malloc(size);
+    g_self->RecordAlloc(ret, size);
+    return ret;
+  }
+
+  static void* OnAllocZeroInitializedFn(const AllocatorDispatch* self,
+                                        size_t n,
+                                        size_t size,
+                                        void* context) {
+    EXPECT_EQ(&g_mock_dispatch, self);
+
+    void* ret = calloc(n, size);
+    g_self->RecordAlloc(ret, n * size);
+    return ret;
+  }
+
+  static void* OnAllocAlignedFn(const AllocatorDispatch* self,
+                                size_t alignment,
+                                size_t size,
+                                void* context) {
+    EXPECT_EQ(&g_mock_dispatch, self);
+
+    // This is a cheat as it doesn't return aligned allocations. This has the
+    // advantage of working for all platforms for this test.
+    void* ret = malloc(size);
+    g_self->RecordAlloc(ret, size);
+    return ret;
+  }
+
+  static void* OnReallocFn(const AllocatorDispatch* self,
+                           void* address,
+                           size_t size,
+                           void* context) {
+    EXPECT_EQ(&g_mock_dispatch, self);
+
+    g_self->DeleteAlloc(address);
+    void* ret = realloc(address, size);
+    g_self->RecordAlloc(ret, size);
+    return ret;
+  }
+
+  static void OnFreeFn(const AllocatorDispatch* self,
+                       void* address,
+                       void* context) {
+    EXPECT_EQ(&g_mock_dispatch, self);
+
+    g_self->DeleteAlloc(address);
+    free(address);
+  }
+
+  static size_t OnGetSizeEstimateFn(const AllocatorDispatch* self,
+                                    void* address,
+                                    void* context) {
+    EXPECT_EQ(&g_mock_dispatch, self);
+
+    return g_self->GetSizeEstimate(address);
+  }
+
+  using AllocationSizeMap = std::map<void*, size_t>;
+
+  SizeFunctionKind size_function_kind_;
+  AllocationSizeMap allocation_size_map_;
+  AllocatorDispatch* dispatch_under_test_;
+
+  static base::allocator::AllocatorDispatch g_mock_dispatch;
+  static ThreadHeapUsageTrackerTest* g_self;
+};
+
+const size_t ThreadHeapUsageTrackerTest::kAllocationPadding = 23;
+
+ThreadHeapUsageTrackerTest* ThreadHeapUsageTrackerTest::g_self = nullptr;
+
+base::allocator::AllocatorDispatch ThreadHeapUsageTrackerTest::g_mock_dispatch =
+    {
+        &ThreadHeapUsageTrackerTest::OnAllocFn,  // alloc_function
+        &ThreadHeapUsageTrackerTest::
+            OnAllocZeroInitializedFn,  // alloc_zero_initialized_function
+        &ThreadHeapUsageTrackerTest::
+            OnAllocAlignedFn,                      // alloc_aligned_function
+        &ThreadHeapUsageTrackerTest::OnReallocFn,  // realloc_function
+        &ThreadHeapUsageTrackerTest::OnFreeFn,     // free_function
+        &ThreadHeapUsageTrackerTest::
+            OnGetSizeEstimateFn,  // get_size_estimate_function
+        nullptr,                  // batch_malloc
+        nullptr,                  // batch_free
+        nullptr,                  // free_definite_size_function
+        nullptr,                  // next
+};
+
+}  // namespace
+
+TEST_F(ThreadHeapUsageTrackerTest, SimpleUsageWithExactSizeFunction) {
+  set_size_function_kind(EXACT_SIZE_FUNCTION);
+
+  ThreadHeapUsageTracker usage_tracker;
+  usage_tracker.Start();
+
+  ThreadHeapUsage u1 = ThreadHeapUsageTracker::GetUsageSnapshot();
+
+  EXPECT_EQ(0U, u1.alloc_ops);
+  EXPECT_EQ(0U, u1.alloc_bytes);
+  EXPECT_EQ(0U, u1.alloc_overhead_bytes);
+  EXPECT_EQ(0U, u1.free_ops);
+  EXPECT_EQ(0U, u1.free_bytes);
+  EXPECT_EQ(0U, u1.max_allocated_bytes);
+
+  const size_t kAllocSize = 1029U;
+  void* ptr = MockMalloc(kAllocSize);
+  MockFree(ptr);
+
+  usage_tracker.Stop(false);
+  ThreadHeapUsage u2 = usage_tracker.usage();
+
+  EXPECT_EQ(1U, u2.alloc_ops);
+  EXPECT_EQ(kAllocSize, u2.alloc_bytes);
+  EXPECT_EQ(0U, u2.alloc_overhead_bytes);
+  EXPECT_EQ(1U, u2.free_ops);
+  EXPECT_EQ(kAllocSize, u2.free_bytes);
+  EXPECT_EQ(kAllocSize, u2.max_allocated_bytes);
+}
+
+TEST_F(ThreadHeapUsageTrackerTest, SimpleUsageWithPaddingSizeFunction) {
+  set_size_function_kind(PADDING_SIZE_FUNCTION);
+
+  ThreadHeapUsageTracker usage_tracker;
+  usage_tracker.Start();
+
+  ThreadHeapUsage u1 = ThreadHeapUsageTracker::GetUsageSnapshot();
+
+  EXPECT_EQ(0U, u1.alloc_ops);
+  EXPECT_EQ(0U, u1.alloc_bytes);
+  EXPECT_EQ(0U, u1.alloc_overhead_bytes);
+  EXPECT_EQ(0U, u1.free_ops);
+  EXPECT_EQ(0U, u1.free_bytes);
+  EXPECT_EQ(0U, u1.max_allocated_bytes);
+
+  const size_t kAllocSize = 1029U;
+  void* ptr = MockMalloc(kAllocSize);
+  MockFree(ptr);
+
+  usage_tracker.Stop(false);
+  ThreadHeapUsage u2 = usage_tracker.usage();
+
+  EXPECT_EQ(1U, u2.alloc_ops);
+  EXPECT_EQ(kAllocSize + kAllocationPadding, u2.alloc_bytes);
+  EXPECT_EQ(kAllocationPadding, u2.alloc_overhead_bytes);
+  EXPECT_EQ(1U, u2.free_ops);
+  EXPECT_EQ(kAllocSize + kAllocationPadding, u2.free_bytes);
+  EXPECT_EQ(kAllocSize + kAllocationPadding, u2.max_allocated_bytes);
+}
+
+TEST_F(ThreadHeapUsageTrackerTest, SimpleUsageWithZeroSizeFunction) {
+  set_size_function_kind(ZERO_SIZE_FUNCTION);
+
+  ThreadHeapUsageTracker usage_tracker;
+  usage_tracker.Start();
+
+  ThreadHeapUsage u1 = ThreadHeapUsageTracker::GetUsageSnapshot();
+  EXPECT_EQ(0U, u1.alloc_ops);
+  EXPECT_EQ(0U, u1.alloc_bytes);
+  EXPECT_EQ(0U, u1.alloc_overhead_bytes);
+  EXPECT_EQ(0U, u1.free_ops);
+  EXPECT_EQ(0U, u1.free_bytes);
+  EXPECT_EQ(0U, u1.max_allocated_bytes);
+
+  const size_t kAllocSize = 1029U;
+  void* ptr = MockMalloc(kAllocSize);
+  MockFree(ptr);
+
+  usage_tracker.Stop(false);
+  ThreadHeapUsage u2 = usage_tracker.usage();
+
+  // With a get-size function that returns zero, there's no way to get the size
+  // of an allocation that's being freed, hence the shim can't tally freed bytes
+  // nor the high-watermark allocated bytes.
+  EXPECT_EQ(1U, u2.alloc_ops);
+  EXPECT_EQ(kAllocSize, u2.alloc_bytes);
+  EXPECT_EQ(0U, u2.alloc_overhead_bytes);
+  EXPECT_EQ(1U, u2.free_ops);
+  EXPECT_EQ(0U, u2.free_bytes);
+  EXPECT_EQ(0U, u2.max_allocated_bytes);
+}
+
+TEST_F(ThreadHeapUsageTrackerTest, ReallocCorrectlyTallied) {
+  const size_t kAllocSize = 237U;
+
+  {
+    ThreadHeapUsageTracker usage_tracker;
+    usage_tracker.Start();
+
+    // Reallocating nullptr should count as a single alloc.
+    void* ptr = MockRealloc(nullptr, kAllocSize);
+    ThreadHeapUsage usage = ThreadHeapUsageTracker::GetUsageSnapshot();
+    EXPECT_EQ(1U, usage.alloc_ops);
+    EXPECT_EQ(kAllocSize, usage.alloc_bytes);
+    EXPECT_EQ(0U, usage.alloc_overhead_bytes);
+    EXPECT_EQ(0U, usage.free_ops);
+    EXPECT_EQ(0U, usage.free_bytes);
+    EXPECT_EQ(kAllocSize, usage.max_allocated_bytes);
+
+    // Reallocating a valid pointer to a zero size should count as a single
+    // free.
+    ptr = MockRealloc(ptr, 0U);
+
+    usage_tracker.Stop(false);
+    EXPECT_EQ(1U, usage_tracker.usage().alloc_ops);
+    EXPECT_EQ(kAllocSize, usage_tracker.usage().alloc_bytes);
+    EXPECT_EQ(0U, usage_tracker.usage().alloc_overhead_bytes);
+    EXPECT_EQ(1U, usage_tracker.usage().free_ops);
+    EXPECT_EQ(kAllocSize, usage_tracker.usage().free_bytes);
+    EXPECT_EQ(kAllocSize, usage_tracker.usage().max_allocated_bytes);
+
+    // Realloc to zero size may or may not return a nullptr - make sure to
+    // free the zero-size alloc in the latter case.
+    if (ptr != nullptr)
+      MockFree(ptr);
+  }
+
+  {
+    ThreadHeapUsageTracker usage_tracker;
+    usage_tracker.Start();
+
+    void* ptr = MockMalloc(kAllocSize);
+    ThreadHeapUsage usage = ThreadHeapUsageTracker::GetUsageSnapshot();
+    EXPECT_EQ(1U, usage.alloc_ops);
+
+    // Now try reallocating a valid pointer to a larger size, this should count
+    // as one free and one alloc.
+    const size_t kLargerAllocSize = kAllocSize + 928U;
+    ptr = MockRealloc(ptr, kLargerAllocSize);
+
+    usage_tracker.Stop(false);
+    EXPECT_EQ(2U, usage_tracker.usage().alloc_ops);
+    EXPECT_EQ(kAllocSize + kLargerAllocSize, usage_tracker.usage().alloc_bytes);
+    EXPECT_EQ(0U, usage_tracker.usage().alloc_overhead_bytes);
+    EXPECT_EQ(1U, usage_tracker.usage().free_ops);
+    EXPECT_EQ(kAllocSize, usage_tracker.usage().free_bytes);
+    EXPECT_EQ(kLargerAllocSize, usage_tracker.usage().max_allocated_bytes);
+
+    MockFree(ptr);
+  }
+}
+
+TEST_F(ThreadHeapUsageTrackerTest, NestedMaxWorks) {
+  ThreadHeapUsageTracker usage_tracker;
+  usage_tracker.Start();
+
+  const size_t kOuterAllocSize = 1029U;
+  void* ptr = MockMalloc(kOuterAllocSize);
+  MockFree(ptr);
+
+  EXPECT_EQ(kOuterAllocSize,
+            ThreadHeapUsageTracker::GetUsageSnapshot().max_allocated_bytes);
+
+  {
+    ThreadHeapUsageTracker inner_usage_tracker;
+    inner_usage_tracker.Start();
+
+    const size_t kInnerAllocSize = 673U;
+    ptr = MockMalloc(kInnerAllocSize);
+    MockFree(ptr);
+
+    inner_usage_tracker.Stop(false);
+
+    EXPECT_EQ(kInnerAllocSize, inner_usage_tracker.usage().max_allocated_bytes);
+  }
+
+  // The greater, outer allocation size should have been restored.
+  EXPECT_EQ(kOuterAllocSize,
+            ThreadHeapUsageTracker::GetUsageSnapshot().max_allocated_bytes);
+
+  const size_t kLargerInnerAllocSize = kOuterAllocSize + 673U;
+  {
+    ThreadHeapUsageTracker inner_usage_tracker;
+    inner_usage_tracker.Start();
+
+    ptr = MockMalloc(kLargerInnerAllocSize);
+    MockFree(ptr);
+
+    inner_usage_tracker.Stop(false);
+    EXPECT_EQ(kLargerInnerAllocSize,
+              inner_usage_tracker.usage().max_allocated_bytes);
+  }
+
+  // The greater, inner allocation size should have been preserved.
+  EXPECT_EQ(kLargerInnerAllocSize,
+            ThreadHeapUsageTracker::GetUsageSnapshot().max_allocated_bytes);
+
+  // Now try the case with an outstanding net alloc size when entering the
+  // inner scope.
+  void* outer_ptr = MockMalloc(kOuterAllocSize);
+  EXPECT_EQ(kLargerInnerAllocSize,
+            ThreadHeapUsageTracker::GetUsageSnapshot().max_allocated_bytes);
+  {
+    ThreadHeapUsageTracker inner_usage_tracker;
+    inner_usage_tracker.Start();
+
+    ptr = MockMalloc(kLargerInnerAllocSize);
+    MockFree(ptr);
+
+    inner_usage_tracker.Stop(false);
+    EXPECT_EQ(kLargerInnerAllocSize,
+              inner_usage_tracker.usage().max_allocated_bytes);
+  }
+
+  // While the inner scope saw only the inner net outstanding allocation size,
+  // the outer scope saw both outstanding at the same time.
+  EXPECT_EQ(kOuterAllocSize + kLargerInnerAllocSize,
+            ThreadHeapUsageTracker::GetUsageSnapshot().max_allocated_bytes);
+
+  MockFree(outer_ptr);
+
+  // Test a net-negative scope.
+  ptr = MockMalloc(kLargerInnerAllocSize);
+  {
+    ThreadHeapUsageTracker inner_usage_tracker;
+    inner_usage_tracker.Start();
+
+    MockFree(ptr);
+
+    const size_t kInnerAllocSize = 1;
+    ptr = MockMalloc(kInnerAllocSize);
+
+    inner_usage_tracker.Stop(false);
+    // Since the scope is still net-negative, the max is clamped at zero.
+    EXPECT_EQ(0U, inner_usage_tracker.usage().max_allocated_bytes);
+  }
+
+  MockFree(ptr);
+}
+
+TEST_F(ThreadHeapUsageTrackerTest, NoStopImpliesInclusive) {
+  ThreadHeapUsageTracker usage_tracker;
+  usage_tracker.Start();
+
+  const size_t kOuterAllocSize = 1029U;
+  void* ptr = MockMalloc(kOuterAllocSize);
+  MockFree(ptr);
+
+  ThreadHeapUsage usage = ThreadHeapUsageTracker::GetUsageSnapshot();
+  EXPECT_EQ(kOuterAllocSize, usage.max_allocated_bytes);
+
+  const size_t kInnerLargerAllocSize = kOuterAllocSize + 673U;
+
+  {
+    ThreadHeapUsageTracker inner_usage_tracker;
+    inner_usage_tracker.Start();
+
+    // Make a larger allocation than the outer scope.
+    ptr = MockMalloc(kInnerLargerAllocSize);
+    MockFree(ptr);
+
+    // inner_usage_tracker goes out of scope without a Stop().
+  }
+
+  ThreadHeapUsage current = ThreadHeapUsageTracker::GetUsageSnapshot();
+  EXPECT_EQ(usage.alloc_ops + 1, current.alloc_ops);
+  EXPECT_EQ(usage.alloc_bytes + kInnerLargerAllocSize, current.alloc_bytes);
+  EXPECT_EQ(usage.free_ops + 1, current.free_ops);
+  EXPECT_EQ(usage.free_bytes + kInnerLargerAllocSize, current.free_bytes);
+  EXPECT_EQ(kInnerLargerAllocSize, current.max_allocated_bytes);
+}
+
+TEST_F(ThreadHeapUsageTrackerTest, ExclusiveScopesWork) {
+  ThreadHeapUsageTracker usage_tracker;
+  usage_tracker.Start();
+
+  const size_t kOuterAllocSize = 1029U;
+  void* ptr = MockMalloc(kOuterAllocSize);
+  MockFree(ptr);
+
+  ThreadHeapUsage usage = ThreadHeapUsageTracker::GetUsageSnapshot();
+  EXPECT_EQ(kOuterAllocSize, usage.max_allocated_bytes);
+
+  {
+    ThreadHeapUsageTracker inner_usage_tracker;
+    inner_usage_tracker.Start();
+
+    // Make a larger allocation than the outer scope.
+    ptr = MockMalloc(kOuterAllocSize + 673U);
+    MockFree(ptr);
+
+    // This tracker is exlusive, all activity should be private to this scope.
+    inner_usage_tracker.Stop(true);
+  }
+
+  ThreadHeapUsage current = ThreadHeapUsageTracker::GetUsageSnapshot();
+  EXPECT_EQ(usage.alloc_ops, current.alloc_ops);
+  EXPECT_EQ(usage.alloc_bytes, current.alloc_bytes);
+  EXPECT_EQ(usage.alloc_overhead_bytes, current.alloc_overhead_bytes);
+  EXPECT_EQ(usage.free_ops, current.free_ops);
+  EXPECT_EQ(usage.free_bytes, current.free_bytes);
+  EXPECT_EQ(usage.max_allocated_bytes, current.max_allocated_bytes);
+}
+
+TEST_F(ThreadHeapUsageTrackerTest, AllShimFunctionsAreProvided) {
+  const size_t kAllocSize = 100;
+  void* alloc = MockMalloc(kAllocSize);
+  size_t estimate = MockGetSizeEstimate(alloc);
+  ASSERT_TRUE(estimate == 0 || estimate >= kAllocSize);
+  MockFree(alloc);
+
+  alloc = MockCalloc(kAllocSize, 1);
+  estimate = MockGetSizeEstimate(alloc);
+  ASSERT_TRUE(estimate == 0 || estimate >= kAllocSize);
+  MockFree(alloc);
+
+  alloc = MockAllocAligned(1, kAllocSize);
+  estimate = MockGetSizeEstimate(alloc);
+  ASSERT_TRUE(estimate == 0 || estimate >= kAllocSize);
+
+  alloc = MockRealloc(alloc, kAllocSize);
+  estimate = MockGetSizeEstimate(alloc);
+  ASSERT_TRUE(estimate == 0 || estimate >= kAllocSize);
+  MockFree(alloc);
+}
+
+#if BUILDFLAG(USE_ALLOCATOR_SHIM)
+class ThreadHeapUsageShimTest : public testing::Test {
+#if defined(OS_MACOSX)
+  void SetUp() override { allocator::InitializeAllocatorShim(); }
+  void TearDown() override { allocator::UninterceptMallocZonesForTesting(); }
+#endif
+};
+
+TEST_F(ThreadHeapUsageShimTest, HooksIntoMallocWhenShimAvailable) {
+  ASSERT_FALSE(ThreadHeapUsageTracker::IsHeapTrackingEnabled());
+
+  ThreadHeapUsageTracker::EnableHeapTracking();
+
+  ASSERT_TRUE(ThreadHeapUsageTracker::IsHeapTrackingEnabled());
+
+  const size_t kAllocSize = 9993;
+  // This test verifies that the scoped heap data is affected by malloc &
+  // free only when the shim is available.
+  ThreadHeapUsageTracker usage_tracker;
+  usage_tracker.Start();
+
+  ThreadHeapUsage u1 = ThreadHeapUsageTracker::GetUsageSnapshot();
+  void* ptr = malloc(kAllocSize);
+  // Prevent the compiler from optimizing out the malloc/free pair.
+  ASSERT_NE(nullptr, ptr);
+
+  ThreadHeapUsage u2 = ThreadHeapUsageTracker::GetUsageSnapshot();
+  free(ptr);
+
+  usage_tracker.Stop(false);
+  ThreadHeapUsage u3 = usage_tracker.usage();
+
+  // Verify that at least one allocation operation was recorded, and that free
+  // operations are at least monotonically growing.
+  EXPECT_LE(0U, u1.alloc_ops);
+  EXPECT_LE(u1.alloc_ops + 1, u2.alloc_ops);
+  EXPECT_LE(u1.alloc_ops + 1, u3.alloc_ops);
+
+  // Verify that at least the bytes above were recorded.
+  EXPECT_LE(u1.alloc_bytes + kAllocSize, u2.alloc_bytes);
+
+  // Verify that at least the one free operation above was recorded.
+  EXPECT_LE(u2.free_ops + 1, u3.free_ops);
+
+  TestingThreadHeapUsageTracker::DisableHeapTrackingForTesting();
+
+  ASSERT_FALSE(ThreadHeapUsageTracker::IsHeapTrackingEnabled());
+}
+#endif  // BUILDFLAG(USE_ALLOCATOR_SHIM)
+
+}  // namespace debug
+}  // namespace base
diff --git a/base/deferred_sequenced_task_runner.cc b/base/deferred_sequenced_task_runner.cc
new file mode 100644
index 0000000..f88170c
--- /dev/null
+++ b/base/deferred_sequenced_task_runner.cc
@@ -0,0 +1,129 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/deferred_sequenced_task_runner.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/logging.h"
+
+namespace base {
+
+DeferredSequencedTaskRunner::DeferredTask::DeferredTask()
+    : is_non_nestable(false) {
+}
+
+DeferredSequencedTaskRunner::DeferredTask::DeferredTask(DeferredTask&& other) =
+    default;
+
+DeferredSequencedTaskRunner::DeferredTask::~DeferredTask() = default;
+
+DeferredSequencedTaskRunner::DeferredTask&
+DeferredSequencedTaskRunner::DeferredTask::operator=(DeferredTask&& other) =
+    default;
+
+DeferredSequencedTaskRunner::DeferredSequencedTaskRunner(
+    scoped_refptr<SequencedTaskRunner> target_task_runner)
+    : DeferredSequencedTaskRunner() {
+  DCHECK(target_task_runner);
+  target_task_runner_ = std::move(target_task_runner);
+}
+
+DeferredSequencedTaskRunner::DeferredSequencedTaskRunner()
+    : created_thread_id_(PlatformThread::CurrentId()) {}
+
+bool DeferredSequencedTaskRunner::PostDelayedTask(const Location& from_here,
+                                                  OnceClosure task,
+                                                  TimeDelta delay) {
+  AutoLock lock(lock_);
+  if (started_) {
+    DCHECK(deferred_tasks_queue_.empty());
+    return target_task_runner_->PostDelayedTask(from_here, std::move(task),
+                                                delay);
+  }
+
+  QueueDeferredTask(from_here, std::move(task), delay,
+                    false /* is_non_nestable */);
+  return true;
+}
+
+bool DeferredSequencedTaskRunner::RunsTasksInCurrentSequence() const {
+  AutoLock lock(lock_);
+  if (target_task_runner_)
+    return target_task_runner_->RunsTasksInCurrentSequence();
+
+  return created_thread_id_ == PlatformThread::CurrentId();
+}
+
+bool DeferredSequencedTaskRunner::PostNonNestableDelayedTask(
+    const Location& from_here,
+    OnceClosure task,
+    TimeDelta delay) {
+  AutoLock lock(lock_);
+  if (started_) {
+    DCHECK(deferred_tasks_queue_.empty());
+    return target_task_runner_->PostNonNestableDelayedTask(
+        from_here, std::move(task), delay);
+  }
+  QueueDeferredTask(from_here, std::move(task), delay,
+                    true /* is_non_nestable */);
+  return true;
+}
+
+void DeferredSequencedTaskRunner::Start() {
+  AutoLock lock(lock_);
+  StartImpl();
+}
+
+void DeferredSequencedTaskRunner::StartWithTaskRunner(
+    scoped_refptr<SequencedTaskRunner> target_task_runner) {
+  AutoLock lock(lock_);
+  DCHECK(!target_task_runner_);
+  DCHECK(target_task_runner);
+  target_task_runner_ = std::move(target_task_runner);
+  StartImpl();
+}
+
+DeferredSequencedTaskRunner::~DeferredSequencedTaskRunner() = default;
+
+void DeferredSequencedTaskRunner::QueueDeferredTask(const Location& from_here,
+                                                    OnceClosure task,
+                                                    TimeDelta delay,
+                                                    bool is_non_nestable) {
+  lock_.AssertAcquired();
+
+  // Use CHECK instead of DCHECK to crash earlier. See http://crbug.com/711167
+  // for details.
+  CHECK(task);
+
+  DeferredTask deferred_task;
+  deferred_task.posted_from = from_here;
+  deferred_task.task = std::move(task);
+  deferred_task.delay = delay;
+  deferred_task.is_non_nestable = is_non_nestable;
+  deferred_tasks_queue_.push_back(std::move(deferred_task));
+}
+
+void DeferredSequencedTaskRunner::StartImpl() {
+  lock_.AssertAcquired();  // Callers should have grabbed the lock.
+  DCHECK(!started_);
+  started_ = true;
+  DCHECK(target_task_runner_);
+  for (std::vector<DeferredTask>::iterator i = deferred_tasks_queue_.begin();
+      i != deferred_tasks_queue_.end();
+      ++i) {
+    DeferredTask& task = *i;
+    if (task.is_non_nestable) {
+      target_task_runner_->PostNonNestableDelayedTask(
+          task.posted_from, std::move(task.task), task.delay);
+    } else {
+      target_task_runner_->PostDelayedTask(task.posted_from,
+                                           std::move(task.task), task.delay);
+    }
+  }
+  deferred_tasks_queue_.clear();
+}
+
+}  // namespace base
diff --git a/base/deferred_sequenced_task_runner.h b/base/deferred_sequenced_task_runner.h
new file mode 100644
index 0000000..2805f47
--- /dev/null
+++ b/base/deferred_sequenced_task_runner.h
@@ -0,0 +1,97 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_DEFERRED_SEQUENCED_TASK_RUNNER_H_
+#define BASE_DEFERRED_SEQUENCED_TASK_RUNNER_H_
+
+#include <vector>
+
+#include "base/base_export.h"
+#include "base/callback.h"
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/sequenced_task_runner.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/platform_thread.h"
+#include "base/time/time.h"
+
+namespace base {
+
+// A DeferredSequencedTaskRunner is a subclass of SequencedTaskRunner that
+// queues up all requests until the first call to Start() is issued.
+// DeferredSequencedTaskRunner may be created in two ways:
+// . with an explicit SequencedTaskRunner that the events are flushed to
+// . without a SequencedTaskRunner. In this configuration the
+//   SequencedTaskRunner is supplied in StartWithTaskRunner().
+class BASE_EXPORT DeferredSequencedTaskRunner : public SequencedTaskRunner {
+ public:
+  explicit DeferredSequencedTaskRunner(
+      scoped_refptr<SequencedTaskRunner> target_runner);
+
+  // Use this constructor when you don't have the target SequencedTaskRunner.
+  // When using this call StartWithTaskRunner().
+  DeferredSequencedTaskRunner();
+
+  // TaskRunner implementation
+  bool PostDelayedTask(const Location& from_here,
+                       OnceClosure task,
+                       TimeDelta delay) override;
+  bool RunsTasksInCurrentSequence() const override;
+
+  // SequencedTaskRunner implementation
+  bool PostNonNestableDelayedTask(const Location& from_here,
+                                  OnceClosure task,
+                                  TimeDelta delay) override;
+
+  // Start the execution - posts all queued tasks to the target executor. The
+  // deferred tasks are posted with their initial delay, meaning that the task
+  // execution delay is actually measured from Start.
+  // Fails when called a second time.
+  void Start();
+
+  // Same as Start(), but must be used with the no-arg constructor.
+  void StartWithTaskRunner(
+      scoped_refptr<SequencedTaskRunner> target_task_runner);
+
+ private:
+  struct DeferredTask  {
+    DeferredTask();
+    DeferredTask(DeferredTask&& other);
+    ~DeferredTask();
+    DeferredTask& operator=(DeferredTask&& other);
+
+    Location posted_from;
+    OnceClosure task;
+    // The delay this task was initially posted with.
+    TimeDelta delay;
+    bool is_non_nestable;
+  };
+
+  ~DeferredSequencedTaskRunner() override;
+
+  // Both variants of Start() call into this.
+  void StartImpl();
+
+  // Creates a |Task| object and adds it to |deferred_tasks_queue_|.
+  void QueueDeferredTask(const Location& from_here,
+                         OnceClosure task,
+                         TimeDelta delay,
+                         bool is_non_nestable);
+
+  // // Protects |started_| and |deferred_tasks_queue_|.
+  mutable Lock lock_;
+
+  const PlatformThreadId created_thread_id_;
+
+  bool started_ = false;
+  scoped_refptr<SequencedTaskRunner> target_task_runner_;
+  std::vector<DeferredTask> deferred_tasks_queue_;
+
+  DISALLOW_COPY_AND_ASSIGN(DeferredSequencedTaskRunner);
+};
+
+}  // namespace base
+
+#endif  // BASE_DEFERRED_SEQUENCED_TASK_RUNNER_H_
diff --git a/base/deferred_sequenced_task_runner_unittest.cc b/base/deferred_sequenced_task_runner_unittest.cc
new file mode 100644
index 0000000..5cb220f
--- /dev/null
+++ b/base/deferred_sequenced_task_runner_unittest.cc
@@ -0,0 +1,214 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/deferred_sequenced_task_runner.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback_forward.h"
+#include "base/location.h"
+#include "base/memory/ref_counted.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "base/threading/thread.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace {
+
+class DeferredSequencedTaskRunnerTest : public testing::Test {
+ public:
+  class ExecuteTaskOnDestructor : public RefCounted<ExecuteTaskOnDestructor> {
+   public:
+    ExecuteTaskOnDestructor(
+        DeferredSequencedTaskRunnerTest* executor,
+        int task_id)
+        : executor_(executor),
+          task_id_(task_id) {
+    }
+  private:
+   friend class RefCounted<ExecuteTaskOnDestructor>;
+   virtual ~ExecuteTaskOnDestructor() { executor_->ExecuteTask(task_id_); }
+   DeferredSequencedTaskRunnerTest* executor_;
+   int task_id_;
+  };
+
+  void ExecuteTask(int task_id) {
+    AutoLock lock(lock_);
+    executed_task_ids_.push_back(task_id);
+  }
+
+  void PostExecuteTask(int task_id) {
+    runner_->PostTask(FROM_HERE,
+                      BindOnce(&DeferredSequencedTaskRunnerTest::ExecuteTask,
+                               Unretained(this), task_id));
+  }
+
+  void StartRunner() {
+    runner_->Start();
+  }
+
+  void DoNothing(ExecuteTaskOnDestructor* object) {
+  }
+
+ protected:
+  DeferredSequencedTaskRunnerTest()
+      : loop_(),
+        runner_(new DeferredSequencedTaskRunner(loop_.task_runner())) {}
+
+  MessageLoop loop_;
+  scoped_refptr<DeferredSequencedTaskRunner> runner_;
+  mutable Lock lock_;
+  std::vector<int> executed_task_ids_;
+};
+
+TEST_F(DeferredSequencedTaskRunnerTest, Stopped) {
+  PostExecuteTask(1);
+  RunLoop().RunUntilIdle();
+  EXPECT_THAT(executed_task_ids_, testing::ElementsAre());
+}
+
+TEST_F(DeferredSequencedTaskRunnerTest, Start) {
+  StartRunner();
+  PostExecuteTask(1);
+  RunLoop().RunUntilIdle();
+  EXPECT_THAT(executed_task_ids_, testing::ElementsAre(1));
+}
+
+TEST_F(DeferredSequencedTaskRunnerTest, StartWithMultipleElements) {
+  StartRunner();
+  for (int i = 1; i < 5; ++i)
+    PostExecuteTask(i);
+
+  RunLoop().RunUntilIdle();
+  EXPECT_THAT(executed_task_ids_, testing::ElementsAre(1, 2, 3, 4));
+}
+
+TEST_F(DeferredSequencedTaskRunnerTest, DeferredStart) {
+  PostExecuteTask(1);
+  RunLoop().RunUntilIdle();
+  EXPECT_THAT(executed_task_ids_, testing::ElementsAre());
+
+  StartRunner();
+  RunLoop().RunUntilIdle();
+  EXPECT_THAT(executed_task_ids_, testing::ElementsAre(1));
+
+  PostExecuteTask(2);
+  RunLoop().RunUntilIdle();
+  EXPECT_THAT(executed_task_ids_, testing::ElementsAre(1, 2));
+}
+
+TEST_F(DeferredSequencedTaskRunnerTest, DeferredStartWithMultipleElements) {
+  for (int i = 1; i < 5; ++i)
+    PostExecuteTask(i);
+  RunLoop().RunUntilIdle();
+  EXPECT_THAT(executed_task_ids_, testing::ElementsAre());
+
+  StartRunner();
+  for (int i = 5; i < 9; ++i)
+    PostExecuteTask(i);
+  RunLoop().RunUntilIdle();
+  EXPECT_THAT(executed_task_ids_, testing::ElementsAre(1, 2, 3, 4, 5, 6, 7, 8));
+}
+
+TEST_F(DeferredSequencedTaskRunnerTest, DeferredStartWithMultipleThreads) {
+  {
+    Thread thread1("DeferredSequencedTaskRunnerTestThread1");
+    Thread thread2("DeferredSequencedTaskRunnerTestThread2");
+    thread1.Start();
+    thread2.Start();
+    for (int i = 0; i < 5; ++i) {
+      thread1.task_runner()->PostTask(
+          FROM_HERE, BindOnce(&DeferredSequencedTaskRunnerTest::PostExecuteTask,
+                              Unretained(this), 2 * i));
+      thread2.task_runner()->PostTask(
+          FROM_HERE, BindOnce(&DeferredSequencedTaskRunnerTest::PostExecuteTask,
+                              Unretained(this), 2 * i + 1));
+      if (i == 2) {
+        thread1.task_runner()->PostTask(
+            FROM_HERE, BindOnce(&DeferredSequencedTaskRunnerTest::StartRunner,
+                                Unretained(this)));
+      }
+    }
+  }
+
+  RunLoop().RunUntilIdle();
+  EXPECT_THAT(executed_task_ids_,
+      testing::WhenSorted(testing::ElementsAre(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)));
+}
+
+TEST_F(DeferredSequencedTaskRunnerTest, ObjectDestructionOrder) {
+  {
+    Thread thread("DeferredSequencedTaskRunnerTestThread");
+    thread.Start();
+    runner_ = new DeferredSequencedTaskRunner(thread.task_runner());
+    for (int i = 0; i < 5; ++i) {
+      {
+        // Use a block to ensure that no reference to |short_lived_object|
+        // is kept on the main thread after it is posted to |runner_|.
+        scoped_refptr<ExecuteTaskOnDestructor> short_lived_object =
+            new ExecuteTaskOnDestructor(this, 2 * i);
+        runner_->PostTask(
+            FROM_HERE,
+            BindOnce(&DeferredSequencedTaskRunnerTest::DoNothing,
+                     Unretained(this), RetainedRef(short_lived_object)));
+      }
+      // |short_lived_object| with id |2 * i| should be destroyed before the
+      // task |2 * i + 1| is executed.
+      PostExecuteTask(2 * i + 1);
+    }
+    StartRunner();
+  }
+
+  // All |short_lived_object| with id |2 * i| are destroyed before the task
+  // |2 * i + 1| is executed.
+  EXPECT_THAT(executed_task_ids_,
+              testing::ElementsAre(0, 1, 2, 3, 4, 5, 6, 7, 8, 9));
+}
+
+void GetRunsTasksInCurrentSequence(bool* result,
+                                   scoped_refptr<SequencedTaskRunner> runner,
+                                   OnceClosure quit) {
+  *result = runner->RunsTasksInCurrentSequence();
+  std::move(quit).Run();
+}
+
+TEST_F(DeferredSequencedTaskRunnerTest, RunsTasksInCurrentSequence) {
+  scoped_refptr<DeferredSequencedTaskRunner> runner =
+      MakeRefCounted<DeferredSequencedTaskRunner>();
+  EXPECT_TRUE(runner->RunsTasksInCurrentSequence());
+
+  Thread thread1("DeferredSequencedTaskRunnerTestThread1");
+  thread1.Start();
+  bool runs_task_in_current_thread = true;
+  base::RunLoop run_loop;
+  thread1.task_runner()->PostTask(
+      FROM_HERE,
+      BindOnce(&GetRunsTasksInCurrentSequence, &runs_task_in_current_thread,
+               runner, run_loop.QuitClosure()));
+  run_loop.Run();
+  EXPECT_FALSE(runs_task_in_current_thread);
+}
+
+TEST_F(DeferredSequencedTaskRunnerTest, StartWithTaskRunner) {
+  scoped_refptr<DeferredSequencedTaskRunner> runner =
+      MakeRefCounted<DeferredSequencedTaskRunner>();
+  bool run_called = false;
+  base::RunLoop run_loop;
+  runner->PostTask(FROM_HERE,
+                   BindOnce(
+                       [](bool* run_called, base::Closure quit_closure) {
+                         *run_called = true;
+                         std::move(quit_closure).Run();
+                       },
+                       &run_called, run_loop.QuitClosure()));
+  runner->StartWithTaskRunner(loop_.task_runner());
+  run_loop.Run();
+  EXPECT_TRUE(run_called);
+}
+
+}  // namespace
+}  // namespace base
diff --git a/base/file_descriptor_store.cc b/base/file_descriptor_store.cc
new file mode 100644
index 0000000..71cf2b3
--- /dev/null
+++ b/base/file_descriptor_store.cc
@@ -0,0 +1,73 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/file_descriptor_store.h"
+
+#include <utility>
+
+#include "base/logging.h"
+
+namespace base {
+
+FileDescriptorStore::Descriptor::Descriptor(const std::string& key,
+                                            base::ScopedFD fd)
+    : key(key),
+      fd(std::move(fd)),
+      region(base::MemoryMappedFile::Region::kWholeFile) {}
+
+FileDescriptorStore::Descriptor::Descriptor(
+    const std::string& key,
+    base::ScopedFD fd,
+    base::MemoryMappedFile::Region region)
+    : key(key), fd(std::move(fd)), region(region) {}
+
+FileDescriptorStore::Descriptor::Descriptor(
+    FileDescriptorStore::Descriptor&& other)
+    : key(other.key), fd(std::move(other.fd)), region(other.region) {}
+
+FileDescriptorStore::Descriptor::~Descriptor() = default;
+
+// static
+FileDescriptorStore& FileDescriptorStore::GetInstance() {
+  static FileDescriptorStore* store = new FileDescriptorStore;
+  return *store;
+}
+
+base::ScopedFD FileDescriptorStore::TakeFD(
+    const std::string& key,
+    base::MemoryMappedFile::Region* region) {
+  base::ScopedFD fd = MaybeTakeFD(key, region);
+  if (!fd.is_valid())
+    DLOG(DCHECK) << "Unknown global descriptor: " << key;
+  return fd;
+}
+
+base::ScopedFD FileDescriptorStore::MaybeTakeFD(
+    const std::string& key,
+    base::MemoryMappedFile::Region* region) {
+  auto iter = descriptors_.find(key);
+  if (iter == descriptors_.end())
+    return base::ScopedFD();
+  *region = iter->second.region;
+  base::ScopedFD result = std::move(iter->second.fd);
+  descriptors_.erase(iter);
+  return result;
+}
+
+void FileDescriptorStore::Set(const std::string& key, base::ScopedFD fd) {
+  Set(key, std::move(fd), base::MemoryMappedFile::Region::kWholeFile);
+}
+
+void FileDescriptorStore::Set(const std::string& key,
+                              base::ScopedFD fd,
+                              base::MemoryMappedFile::Region region) {
+  Descriptor descriptor(key, std::move(fd), region);
+  descriptors_.insert(std::make_pair(key, std::move(descriptor)));
+}
+
+FileDescriptorStore::FileDescriptorStore() = default;
+
+FileDescriptorStore::~FileDescriptorStore() = default;
+
+}  // namespace base
diff --git a/base/file_descriptor_store.h b/base/file_descriptor_store.h
new file mode 100644
index 0000000..b6bd079
--- /dev/null
+++ b/base/file_descriptor_store.h
@@ -0,0 +1,73 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_FILE_DESCRIPTOR_STORE_H_
+#define BASE_FILE_DESCRIPTOR_STORE_H_
+
+#include <map>
+#include <string>
+
+#include "base/files/memory_mapped_file.h"
+#include "base/files/scoped_file.h"
+#include "base/macros.h"
+
+namespace base {
+
+// The file descriptor store is used to associate file descriptors with keys
+// that must be unique.
+// It is used to share file descriptors from a process to its child.
+class BASE_EXPORT FileDescriptorStore {
+ public:
+  struct Descriptor {
+    Descriptor(const std::string& key, base::ScopedFD fd);
+    Descriptor(const std::string& key,
+               base::ScopedFD fd,
+               base::MemoryMappedFile::Region region);
+    Descriptor(Descriptor&& other);
+    ~Descriptor();
+
+    Descriptor& operator=(Descriptor&& other) = default;
+
+    // Globally unique key.
+    std::string key;
+    // Actual FD.
+    base::ScopedFD fd;
+    // Optional region, defaults to kWholeFile.
+    base::MemoryMappedFile::Region region;
+  };
+  using Mapping = std::map<std::string, Descriptor>;
+
+  // Returns the singleton instance of FileDescriptorStore.
+  static FileDescriptorStore& GetInstance();
+
+  // Gets a descriptor given a key and also populates |region|.
+  // It is a fatal error if the key is not known.
+  base::ScopedFD TakeFD(const std::string& key,
+                        base::MemoryMappedFile::Region* region);
+
+  // Gets a descriptor given a key. Returns an empty ScopedFD on error.
+  base::ScopedFD MaybeTakeFD(const std::string& key,
+                             base::MemoryMappedFile::Region* region);
+
+  // Sets the descriptor for the given |key|. This sets the region associated
+  // with |key| to kWholeFile.
+  void Set(const std::string& key, base::ScopedFD fd);
+
+  // Sets the descriptor and |region| for the given |key|.
+  void Set(const std::string& key,
+           base::ScopedFD fd,
+           base::MemoryMappedFile::Region region);
+
+ private:
+  FileDescriptorStore();
+  ~FileDescriptorStore();
+
+  Mapping descriptors_;
+
+  DISALLOW_COPY_AND_ASSIGN(FileDescriptorStore);
+};
+
+}  // namespace base
+
+#endif  // BASE_FILE_DESCRIPTOR_STORE_H_
diff --git a/base/files/file_locking_unittest.cc b/base/files/file_locking_unittest.cc
new file mode 100644
index 0000000..e158b7d
--- /dev/null
+++ b/base/files/file_locking_unittest.cc
@@ -0,0 +1,232 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/command_line.h"
+#include "base/files/file.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/macros.h"
+#include "base/test/multiprocess_test.h"
+#include "base/test/test_timeouts.h"
+#include "base/threading/platform_thread.h"
+#include "base/time/time.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/multiprocess_func_list.h"
+
+using base::File;
+using base::FilePath;
+
+namespace {
+
+// Flag for the parent to share a temp dir to the child.
+const char kTempDirFlag[] = "temp-dir";
+
+// Flags to control how the subprocess unlocks the file.
+const char kFileUnlock[] = "file-unlock";
+const char kCloseUnlock[] = "close-unlock";
+const char kExitUnlock[] = "exit-unlock";
+
+// File to lock in temp dir.
+const char kLockFile[] = "lockfile";
+
+// Constants for various requests and responses, used as |signal_file| parameter
+// to signal/wait helpers.
+const char kSignalLockFileLocked[] = "locked.signal";
+const char kSignalLockFileClose[] = "close.signal";
+const char kSignalLockFileClosed[] = "closed.signal";
+const char kSignalLockFileUnlock[] = "unlock.signal";
+const char kSignalLockFileUnlocked[] = "unlocked.signal";
+const char kSignalExit[] = "exit.signal";
+
+// Signal an event by creating a file which didn't previously exist.
+bool SignalEvent(const FilePath& signal_dir, const char* signal_file) {
+  File file(signal_dir.AppendASCII(signal_file),
+            File::FLAG_CREATE | File::FLAG_WRITE);
+  return file.IsValid();
+}
+
+// Check whether an event was signaled.
+bool CheckEvent(const FilePath& signal_dir, const char* signal_file) {
+  File file(signal_dir.AppendASCII(signal_file),
+            File::FLAG_OPEN | File::FLAG_READ);
+  return file.IsValid();
+}
+
+// Busy-wait for an event to be signaled, returning false for timeout.
+bool WaitForEventWithTimeout(const FilePath& signal_dir,
+                             const char* signal_file,
+                             const base::TimeDelta& timeout) {
+  const base::Time finish_by = base::Time::Now() + timeout;
+  while (!CheckEvent(signal_dir, signal_file)) {
+    if (base::Time::Now() > finish_by)
+      return false;
+    base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(10));
+  }
+  return true;
+}
+
+// Wait forever for the event to be signaled (should never return false).
+bool WaitForEvent(const FilePath& signal_dir, const char* signal_file) {
+  return WaitForEventWithTimeout(signal_dir, signal_file,
+                                 base::TimeDelta::Max());
+}
+
+// Keep these in sync so StartChild*() can refer to correct test main.
+#define ChildMain ChildLockUnlock
+#define ChildMainString "ChildLockUnlock"
+
+// Subprocess to test getting a file lock then releasing it.  |kTempDirFlag|
+// must pass in an existing temporary directory for the lockfile and signal
+// files.  One of the following flags must be passed to determine how to unlock
+// the lock file:
+// - |kFileUnlock| calls Unlock() to unlock.
+// - |kCloseUnlock| calls Close() while the lock is held.
+// - |kExitUnlock| exits while the lock is held.
+MULTIPROCESS_TEST_MAIN(ChildMain) {
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+  const FilePath temp_path = command_line->GetSwitchValuePath(kTempDirFlag);
+  CHECK(base::DirectoryExists(temp_path));
+
+  // Immediately lock the file.
+  File file(temp_path.AppendASCII(kLockFile),
+            File::FLAG_OPEN | File::FLAG_READ | File::FLAG_WRITE);
+  CHECK(file.IsValid());
+  CHECK_EQ(File::FILE_OK, file.Lock());
+  CHECK(SignalEvent(temp_path, kSignalLockFileLocked));
+
+  if (command_line->HasSwitch(kFileUnlock)) {
+    // Wait for signal to unlock, then unlock the file.
+    CHECK(WaitForEvent(temp_path, kSignalLockFileUnlock));
+    CHECK_EQ(File::FILE_OK, file.Unlock());
+    CHECK(SignalEvent(temp_path, kSignalLockFileUnlocked));
+  } else if (command_line->HasSwitch(kCloseUnlock)) {
+    // Wait for the signal to close, then close the file.
+    CHECK(WaitForEvent(temp_path, kSignalLockFileClose));
+    file.Close();
+    CHECK(!file.IsValid());
+    CHECK(SignalEvent(temp_path, kSignalLockFileClosed));
+  } else {
+    CHECK(command_line->HasSwitch(kExitUnlock));
+  }
+
+  // Wait for signal to exit, so that unlock or close can be distinguished from
+  // exit.
+  CHECK(WaitForEvent(temp_path, kSignalExit));
+  return 0;
+}
+
+}  // namespace
+
+class FileLockingTest : public testing::Test {
+ public:
+  FileLockingTest() = default;
+
+ protected:
+  void SetUp() override {
+    testing::Test::SetUp();
+
+    // Setup the temp dir and the lock file.
+    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+    lock_file_.Initialize(
+        temp_dir_.GetPath().AppendASCII(kLockFile),
+        File::FLAG_CREATE | File::FLAG_READ | File::FLAG_WRITE);
+    ASSERT_TRUE(lock_file_.IsValid());
+  }
+
+  bool SignalEvent(const char* signal_file) {
+    return ::SignalEvent(temp_dir_.GetPath(), signal_file);
+  }
+
+  bool WaitForEventOrTimeout(const char* signal_file) {
+    return ::WaitForEventWithTimeout(temp_dir_.GetPath(), signal_file,
+                                     TestTimeouts::action_timeout());
+  }
+
+  // Start a child process set to use the specified unlock action, and wait for
+  // it to lock the file.
+  void StartChildAndSignalLock(const char* unlock_action) {
+    // Create a temporary dir and spin up a ChildLockExit subprocess against it.
+    const FilePath temp_path = temp_dir_.GetPath();
+    base::CommandLine child_command_line(
+        base::GetMultiProcessTestChildBaseCommandLine());
+    child_command_line.AppendSwitchPath(kTempDirFlag, temp_path);
+    child_command_line.AppendSwitch(unlock_action);
+    lock_child_ = base::SpawnMultiProcessTestChild(
+        ChildMainString, child_command_line, base::LaunchOptions());
+    ASSERT_TRUE(lock_child_.IsValid());
+
+    // Wait for the child to lock the file.
+    ASSERT_TRUE(WaitForEventOrTimeout(kSignalLockFileLocked));
+  }
+
+  // Signal the child to exit cleanly.
+  void ExitChildCleanly() {
+    ASSERT_TRUE(SignalEvent(kSignalExit));
+    int rv = -1;
+    ASSERT_TRUE(WaitForMultiprocessTestChildExit(
+        lock_child_, TestTimeouts::action_timeout(), &rv));
+    ASSERT_EQ(0, rv);
+  }
+
+  base::ScopedTempDir temp_dir_;
+  base::File lock_file_;
+  base::Process lock_child_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(FileLockingTest);
+};
+
+// Test that locks are released by Unlock().
+TEST_F(FileLockingTest, LockAndUnlock) {
+  StartChildAndSignalLock(kFileUnlock);
+
+  ASSERT_NE(File::FILE_OK, lock_file_.Lock());
+  ASSERT_TRUE(SignalEvent(kSignalLockFileUnlock));
+  ASSERT_TRUE(WaitForEventOrTimeout(kSignalLockFileUnlocked));
+  ASSERT_EQ(File::FILE_OK, lock_file_.Lock());
+  ASSERT_EQ(File::FILE_OK, lock_file_.Unlock());
+
+  ExitChildCleanly();
+}
+
+// Test that locks are released on Close().
+TEST_F(FileLockingTest, UnlockOnClose) {
+  StartChildAndSignalLock(kCloseUnlock);
+
+  ASSERT_NE(File::FILE_OK, lock_file_.Lock());
+  ASSERT_TRUE(SignalEvent(kSignalLockFileClose));
+  ASSERT_TRUE(WaitForEventOrTimeout(kSignalLockFileClosed));
+  ASSERT_EQ(File::FILE_OK, lock_file_.Lock());
+  ASSERT_EQ(File::FILE_OK, lock_file_.Unlock());
+
+  ExitChildCleanly();
+}
+
+// Test that locks are released on exit.
+TEST_F(FileLockingTest, UnlockOnExit) {
+  StartChildAndSignalLock(kExitUnlock);
+
+  ASSERT_NE(File::FILE_OK, lock_file_.Lock());
+  ExitChildCleanly();
+  ASSERT_EQ(File::FILE_OK, lock_file_.Lock());
+  ASSERT_EQ(File::FILE_OK, lock_file_.Unlock());
+}
+
+// Test that killing the process releases the lock.  This should cover crashing.
+// Flaky on Android (http://crbug.com/747518)
+#if defined(OS_ANDROID)
+#define MAYBE_UnlockOnTerminate DISABLED_UnlockOnTerminate
+#else
+#define MAYBE_UnlockOnTerminate UnlockOnTerminate
+#endif
+TEST_F(FileLockingTest, MAYBE_UnlockOnTerminate) {
+  // The child will wait for an exit which never arrives.
+  StartChildAndSignalLock(kExitUnlock);
+
+  ASSERT_NE(File::FILE_OK, lock_file_.Lock());
+  ASSERT_TRUE(TerminateMultiProcessTestChild(lock_child_, 0, true));
+  ASSERT_EQ(File::FILE_OK, lock_file_.Lock());
+  ASSERT_EQ(File::FILE_OK, lock_file_.Unlock());
+}
diff --git a/base/files/file_path_watcher_fsevents.cc b/base/files/file_path_watcher_fsevents.cc
new file mode 100644
index 0000000..49ed36b
--- /dev/null
+++ b/base/files/file_path_watcher_fsevents.cc
@@ -0,0 +1,282 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/files/file_path_watcher_fsevents.h"
+
+#include <dispatch/dispatch.h>
+
+#include <list>
+
+#include "base/bind.h"
+#include "base/files/file_util.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/mac/scoped_cftyperef.h"
+#include "base/strings/stringprintf.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+
+namespace base {
+
+namespace {
+
+// The latency parameter passed to FSEventsStreamCreate().
+const CFAbsoluteTime kEventLatencySeconds = 0.3;
+
+// Resolve any symlinks in the path.
+FilePath ResolvePath(const FilePath& path) {
+  const unsigned kMaxLinksToResolve = 255;
+
+  std::vector<FilePath::StringType> component_vector;
+  path.GetComponents(&component_vector);
+  std::list<FilePath::StringType>
+      components(component_vector.begin(), component_vector.end());
+
+  FilePath result;
+  unsigned resolve_count = 0;
+  while (resolve_count < kMaxLinksToResolve && !components.empty()) {
+    FilePath component(*components.begin());
+    components.pop_front();
+
+    FilePath current;
+    if (component.IsAbsolute()) {
+      current = component;
+    } else {
+      current = result.Append(component);
+    }
+
+    FilePath target;
+    if (ReadSymbolicLink(current, &target)) {
+      if (target.IsAbsolute())
+        result.clear();
+      std::vector<FilePath::StringType> target_components;
+      target.GetComponents(&target_components);
+      components.insert(components.begin(), target_components.begin(),
+                        target_components.end());
+      resolve_count++;
+    } else {
+      result = current;
+    }
+  }
+
+  if (resolve_count >= kMaxLinksToResolve)
+    result.clear();
+  return result;
+}
+
+}  // namespace
+
+FilePathWatcherFSEvents::FilePathWatcherFSEvents()
+    : queue_(dispatch_queue_create(
+          base::StringPrintf("org.chromium.base.FilePathWatcher.%p", this)
+              .c_str(),
+          DISPATCH_QUEUE_SERIAL)),
+      fsevent_stream_(nullptr),
+      weak_factory_(this) {}
+
+FilePathWatcherFSEvents::~FilePathWatcherFSEvents() {
+  DCHECK(!task_runner() || task_runner()->RunsTasksInCurrentSequence());
+  DCHECK(callback_.is_null())
+      << "Cancel() must be called before FilePathWatcher is destroyed.";
+}
+
+bool FilePathWatcherFSEvents::Watch(const FilePath& path,
+                                    bool recursive,
+                                    const FilePathWatcher::Callback& callback) {
+  DCHECK(!callback.is_null());
+  DCHECK(callback_.is_null());
+
+  // This class could support non-recursive watches, but that is currently
+  // left to FilePathWatcherKQueue.
+  if (!recursive)
+    return false;
+
+  set_task_runner(SequencedTaskRunnerHandle::Get());
+  callback_ = callback;
+
+  FSEventStreamEventId start_event = FSEventsGetCurrentEventId();
+  // The block runtime would implicitly capture the reference, not the object
+  // it's referencing. Copy the path into a local, so that the value is
+  // captured by the block's scope.
+  const FilePath path_copy(path);
+
+  dispatch_async(queue_, ^{
+      StartEventStream(start_event, path_copy);
+  });
+  return true;
+}
+
+void FilePathWatcherFSEvents::Cancel() {
+  set_cancelled();
+  callback_.Reset();
+
+  // Switch to the dispatch queue to tear down the event stream. As the queue is
+  // owned by |this|, and this method is called from the destructor, execute the
+  // block synchronously.
+  dispatch_sync(queue_, ^{
+    if (fsevent_stream_) {
+      DestroyEventStream();
+      target_.clear();
+      resolved_target_.clear();
+    }
+  });
+}
+
+// static
+void FilePathWatcherFSEvents::FSEventsCallback(
+    ConstFSEventStreamRef stream,
+    void* event_watcher,
+    size_t num_events,
+    void* event_paths,
+    const FSEventStreamEventFlags flags[],
+    const FSEventStreamEventId event_ids[]) {
+  FilePathWatcherFSEvents* watcher =
+      reinterpret_cast<FilePathWatcherFSEvents*>(event_watcher);
+  bool root_changed = watcher->ResolveTargetPath();
+  std::vector<FilePath> paths;
+  FSEventStreamEventId root_change_at = FSEventStreamGetLatestEventId(stream);
+  for (size_t i = 0; i < num_events; i++) {
+    if (flags[i] & kFSEventStreamEventFlagRootChanged)
+      root_changed = true;
+    if (event_ids[i])
+      root_change_at = std::min(root_change_at, event_ids[i]);
+    paths.push_back(FilePath(
+        reinterpret_cast<char**>(event_paths)[i]).StripTrailingSeparators());
+  }
+
+  // Reinitialize the event stream if we find changes to the root. This is
+  // necessary since FSEvents doesn't report any events for the subtree after
+  // the directory to be watched gets created.
+  if (root_changed) {
+    // Resetting the event stream from within the callback fails (FSEvents spews
+    // bad file descriptor errors), so do the reset asynchronously.
+    //
+    // We can't dispatch_async a call to UpdateEventStream() directly because
+    // there would be no guarantee that |watcher| still exists when it runs.
+    //
+    // Instead, bounce on task_runner() and use a WeakPtr to verify that
+    // |watcher| still exists. If it does, dispatch_async a call to
+    // UpdateEventStream(). Because the destructor of |watcher| runs on
+    // task_runner() and calls dispatch_sync, it is guaranteed that |watcher|
+    // still exists when UpdateEventStream() runs.
+    watcher->task_runner()->PostTask(
+        FROM_HERE, Bind(
+                       [](WeakPtr<FilePathWatcherFSEvents> weak_watcher,
+                          FSEventStreamEventId root_change_at) {
+                         if (!weak_watcher)
+                           return;
+                         FilePathWatcherFSEvents* watcher = weak_watcher.get();
+                         dispatch_async(watcher->queue_, ^{
+                           watcher->UpdateEventStream(root_change_at);
+                         });
+                       },
+                       watcher->weak_factory_.GetWeakPtr(), root_change_at));
+  }
+
+  watcher->OnFilePathsChanged(paths);
+}
+
+void FilePathWatcherFSEvents::OnFilePathsChanged(
+    const std::vector<FilePath>& paths) {
+  DCHECK(!resolved_target_.empty());
+  task_runner()->PostTask(
+      FROM_HERE,
+      Bind(&FilePathWatcherFSEvents::DispatchEvents, weak_factory_.GetWeakPtr(),
+           paths, target_, resolved_target_));
+}
+
+void FilePathWatcherFSEvents::DispatchEvents(const std::vector<FilePath>& paths,
+                                             const FilePath& target,
+                                             const FilePath& resolved_target) {
+  DCHECK(task_runner()->RunsTasksInCurrentSequence());
+
+  // Don't issue callbacks after Cancel() has been called.
+  if (is_cancelled() || callback_.is_null()) {
+    return;
+  }
+
+  for (const FilePath& path : paths) {
+    if (resolved_target.IsParent(path) || resolved_target == path) {
+      callback_.Run(target, false);
+      return;
+    }
+  }
+}
+
+void FilePathWatcherFSEvents::UpdateEventStream(
+    FSEventStreamEventId start_event) {
+  // It can happen that the watcher gets canceled while tasks that call this
+  // function are still in flight, so abort if this situation is detected.
+  if (resolved_target_.empty())
+    return;
+
+  if (fsevent_stream_)
+    DestroyEventStream();
+
+  ScopedCFTypeRef<CFStringRef> cf_path(CFStringCreateWithCString(
+      NULL, resolved_target_.value().c_str(), kCFStringEncodingMacHFS));
+  ScopedCFTypeRef<CFStringRef> cf_dir_path(CFStringCreateWithCString(
+      NULL, resolved_target_.DirName().value().c_str(),
+      kCFStringEncodingMacHFS));
+  CFStringRef paths_array[] = { cf_path.get(), cf_dir_path.get() };
+  ScopedCFTypeRef<CFArrayRef> watched_paths(CFArrayCreate(
+      NULL, reinterpret_cast<const void**>(paths_array), arraysize(paths_array),
+      &kCFTypeArrayCallBacks));
+
+  FSEventStreamContext context;
+  context.version = 0;
+  context.info = this;
+  context.retain = NULL;
+  context.release = NULL;
+  context.copyDescription = NULL;
+
+  fsevent_stream_ = FSEventStreamCreate(NULL, &FSEventsCallback, &context,
+                                        watched_paths,
+                                        start_event,
+                                        kEventLatencySeconds,
+                                        kFSEventStreamCreateFlagWatchRoot);
+  FSEventStreamSetDispatchQueue(fsevent_stream_, queue_);
+
+  if (!FSEventStreamStart(fsevent_stream_)) {
+    task_runner()->PostTask(FROM_HERE,
+                            Bind(&FilePathWatcherFSEvents::ReportError,
+                                 weak_factory_.GetWeakPtr(), target_));
+  }
+}
+
+bool FilePathWatcherFSEvents::ResolveTargetPath() {
+  FilePath resolved = ResolvePath(target_).StripTrailingSeparators();
+  bool changed = resolved != resolved_target_;
+  resolved_target_ = resolved;
+  if (resolved_target_.empty()) {
+    task_runner()->PostTask(FROM_HERE,
+                            Bind(&FilePathWatcherFSEvents::ReportError,
+                                 weak_factory_.GetWeakPtr(), target_));
+  }
+  return changed;
+}
+
+void FilePathWatcherFSEvents::ReportError(const FilePath& target) {
+  DCHECK(task_runner()->RunsTasksInCurrentSequence());
+  if (!callback_.is_null()) {
+    callback_.Run(target, true);
+  }
+}
+
+void FilePathWatcherFSEvents::DestroyEventStream() {
+  FSEventStreamStop(fsevent_stream_);
+  FSEventStreamInvalidate(fsevent_stream_);
+  FSEventStreamRelease(fsevent_stream_);
+  fsevent_stream_ = NULL;
+}
+
+void FilePathWatcherFSEvents::StartEventStream(FSEventStreamEventId start_event,
+                                               const FilePath& path) {
+  DCHECK(resolved_target_.empty());
+
+  target_ = path;
+  ResolveTargetPath();
+  UpdateEventStream(start_event);
+}
+
+}  // namespace base
diff --git a/base/files/file_path_watcher_fsevents.h b/base/files/file_path_watcher_fsevents.h
new file mode 100644
index 0000000..dcdf2fb
--- /dev/null
+++ b/base/files/file_path_watcher_fsevents.h
@@ -0,0 +1,99 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_FILES_FILE_PATH_WATCHER_FSEVENTS_H_
+#define BASE_FILES_FILE_PATH_WATCHER_FSEVENTS_H_
+
+#include <CoreServices/CoreServices.h>
+#include <stddef.h>
+
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/files/file_path_watcher.h"
+#include "base/mac/scoped_dispatch_object.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+
+namespace base {
+
+// Mac-specific file watcher implementation based on FSEvents.
+// There are trade-offs between the FSEvents implementation and a kqueue
+// implementation. The biggest issues are that FSEvents on 10.6 sometimes drops
+// events and kqueue does not trigger for modifications to a file in a watched
+// directory. See file_path_watcher_mac.cc for the code that decides when to
+// use which one.
+class FilePathWatcherFSEvents : public FilePathWatcher::PlatformDelegate {
+ public:
+  FilePathWatcherFSEvents();
+  ~FilePathWatcherFSEvents() override;
+
+  // FilePathWatcher::PlatformDelegate overrides.
+  bool Watch(const FilePath& path,
+             bool recursive,
+             const FilePathWatcher::Callback& callback) override;
+  void Cancel() override;
+
+ private:
+  static void FSEventsCallback(ConstFSEventStreamRef stream,
+                               void* event_watcher,
+                               size_t num_events,
+                               void* event_paths,
+                               const FSEventStreamEventFlags flags[],
+                               const FSEventStreamEventId event_ids[]);
+
+  // Called from FSEventsCallback whenever there is a change to the paths.
+  void OnFilePathsChanged(const std::vector<FilePath>& paths);
+
+  // Called on the message_loop() thread to dispatch path events. Can't access
+  // target_ and resolved_target_ directly as those are modified on the
+  // libdispatch thread.
+  void DispatchEvents(const std::vector<FilePath>& paths,
+                      const FilePath& target,
+                      const FilePath& resolved_target);
+
+  // (Re-)Initialize the event stream to start reporting events from
+  // |start_event|.
+  void UpdateEventStream(FSEventStreamEventId start_event);
+
+  // Returns true if resolving the target path got a different result than
+  // last time it was done.
+  bool ResolveTargetPath();
+
+  // Report an error watching the given target.
+  void ReportError(const FilePath& target);
+
+  // Destroy the event stream.
+  void DestroyEventStream();
+
+  // Start watching the FSEventStream.
+  void StartEventStream(FSEventStreamEventId start_event, const FilePath& path);
+
+  // Callback to notify upon changes.
+  // (Only accessed from the message_loop() thread.)
+  FilePathWatcher::Callback callback_;
+
+  // The dispatch queue on which the the event stream is scheduled.
+  ScopedDispatchObject<dispatch_queue_t> queue_;
+
+  // Target path to watch (passed to callback).
+  // (Only accessed from the libdispatch queue.)
+  FilePath target_;
+
+  // Target path with all symbolic links resolved.
+  // (Only accessed from the libdispatch queue.)
+  FilePath resolved_target_;
+
+  // Backend stream we receive event callbacks from (strong reference).
+  // (Only accessed from the libdispatch queue.)
+  FSEventStreamRef fsevent_stream_;
+
+  WeakPtrFactory<FilePathWatcherFSEvents> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(FilePathWatcherFSEvents);
+};
+
+}  // namespace base
+
+#endif  // BASE_FILES_FILE_PATH_WATCHER_FSEVENTS_H_
diff --git a/base/files/file_path_watcher_kqueue.cc b/base/files/file_path_watcher_kqueue.cc
new file mode 100644
index 0000000..036809d
--- /dev/null
+++ b/base/files/file_path_watcher_kqueue.cc
@@ -0,0 +1,372 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/files/file_path_watcher_kqueue.h"
+
+#include <fcntl.h>
+#include <stddef.h>
+#include <sys/param.h>
+
+#include "base/bind.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/strings/stringprintf.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+
+// On some platforms these are not defined.
+#if !defined(EV_RECEIPT)
+#define EV_RECEIPT 0
+#endif
+#if !defined(O_EVTONLY)
+#define O_EVTONLY O_RDONLY
+#endif
+
+namespace base {
+
+FilePathWatcherKQueue::FilePathWatcherKQueue() : kqueue_(-1) {}
+
+FilePathWatcherKQueue::~FilePathWatcherKQueue() {
+  DCHECK(!task_runner() || task_runner()->RunsTasksInCurrentSequence());
+}
+
+void FilePathWatcherKQueue::ReleaseEvent(struct kevent& event) {
+  CloseFileDescriptor(&event.ident);
+  EventData* entry = EventDataForKevent(event);
+  delete entry;
+  event.udata = NULL;
+}
+
+int FilePathWatcherKQueue::EventsForPath(FilePath path, EventVector* events) {
+  // Make sure that we are working with a clean slate.
+  DCHECK(events->empty());
+
+  std::vector<FilePath::StringType> components;
+  path.GetComponents(&components);
+
+  if (components.size() < 1) {
+    return -1;
+  }
+
+  int last_existing_entry = 0;
+  FilePath built_path;
+  bool path_still_exists = true;
+  for (std::vector<FilePath::StringType>::iterator i = components.begin();
+      i != components.end(); ++i) {
+    if (i == components.begin()) {
+      built_path = FilePath(*i);
+    } else {
+      built_path = built_path.Append(*i);
+    }
+    uintptr_t fd = kNoFileDescriptor;
+    if (path_still_exists) {
+      fd = FileDescriptorForPath(built_path);
+      if (fd == kNoFileDescriptor) {
+        path_still_exists = false;
+      } else {
+        ++last_existing_entry;
+      }
+    }
+    FilePath::StringType subdir = (i != (components.end() - 1)) ? *(i + 1) : "";
+    EventData* data = new EventData(built_path, subdir);
+    struct kevent event;
+    EV_SET(&event, fd, EVFILT_VNODE, (EV_ADD | EV_CLEAR | EV_RECEIPT),
+           (NOTE_DELETE | NOTE_WRITE | NOTE_ATTRIB |
+            NOTE_RENAME | NOTE_REVOKE | NOTE_EXTEND), 0, data);
+    events->push_back(event);
+  }
+  return last_existing_entry;
+}
+
+uintptr_t FilePathWatcherKQueue::FileDescriptorForPath(const FilePath& path) {
+  int fd = HANDLE_EINTR(open(path.value().c_str(), O_EVTONLY));
+  if (fd == -1)
+    return kNoFileDescriptor;
+  return fd;
+}
+
+void FilePathWatcherKQueue::CloseFileDescriptor(uintptr_t* fd) {
+  if (*fd == kNoFileDescriptor) {
+    return;
+  }
+
+  if (IGNORE_EINTR(close(*fd)) != 0) {
+    DPLOG(ERROR) << "close";
+  }
+  *fd = kNoFileDescriptor;
+}
+
+bool FilePathWatcherKQueue::AreKeventValuesValid(struct kevent* kevents,
+                                               int count) {
+  if (count < 0) {
+    DPLOG(ERROR) << "kevent";
+    return false;
+  }
+  bool valid = true;
+  for (int i = 0; i < count; ++i) {
+    if (kevents[i].flags & EV_ERROR && kevents[i].data) {
+      // Find the kevent in |events_| that matches the kevent with the error.
+      EventVector::iterator event = events_.begin();
+      for (; event != events_.end(); ++event) {
+        if (event->ident == kevents[i].ident) {
+          break;
+        }
+      }
+      std::string path_name;
+      if (event != events_.end()) {
+        EventData* event_data = EventDataForKevent(*event);
+        if (event_data != NULL) {
+          path_name = event_data->path_.value();
+        }
+      }
+      if (path_name.empty()) {
+        path_name = base::StringPrintf(
+            "fd %ld", reinterpret_cast<long>(&kevents[i].ident));
+      }
+      DLOG(ERROR) << "Error: " << kevents[i].data << " for " << path_name;
+      valid = false;
+    }
+  }
+  return valid;
+}
+
+void FilePathWatcherKQueue::HandleAttributesChange(
+    const EventVector::iterator& event,
+    bool* target_file_affected,
+    bool* update_watches) {
+  EventVector::iterator next_event = event + 1;
+  EventData* next_event_data = EventDataForKevent(*next_event);
+  // Check to see if the next item in path is still accessible.
+  uintptr_t have_access = FileDescriptorForPath(next_event_data->path_);
+  if (have_access == kNoFileDescriptor) {
+    *target_file_affected = true;
+    *update_watches = true;
+    EventVector::iterator local_event(event);
+    for (; local_event != events_.end(); ++local_event) {
+      // Close all nodes from the event down. This has the side effect of
+      // potentially rendering other events in |updates| invalid.
+      // There is no need to remove the events from |kqueue_| because this
+      // happens as a side effect of closing the file descriptor.
+      CloseFileDescriptor(&local_event->ident);
+    }
+  } else {
+    CloseFileDescriptor(&have_access);
+  }
+}
+
+void FilePathWatcherKQueue::HandleDeleteOrMoveChange(
+    const EventVector::iterator& event,
+    bool* target_file_affected,
+    bool* update_watches) {
+  *target_file_affected = true;
+  *update_watches = true;
+  EventVector::iterator local_event(event);
+  for (; local_event != events_.end(); ++local_event) {
+    // Close all nodes from the event down. This has the side effect of
+    // potentially rendering other events in |updates| invalid.
+    // There is no need to remove the events from |kqueue_| because this
+    // happens as a side effect of closing the file descriptor.
+    CloseFileDescriptor(&local_event->ident);
+  }
+}
+
+void FilePathWatcherKQueue::HandleCreateItemChange(
+    const EventVector::iterator& event,
+    bool* target_file_affected,
+    bool* update_watches) {
+  // Get the next item in the path.
+  EventVector::iterator next_event = event + 1;
+  // Check to see if it already has a valid file descriptor.
+  if (!IsKeventFileDescriptorOpen(*next_event)) {
+    EventData* next_event_data = EventDataForKevent(*next_event);
+    // If not, attempt to open a file descriptor for it.
+    next_event->ident = FileDescriptorForPath(next_event_data->path_);
+    if (IsKeventFileDescriptorOpen(*next_event)) {
+      *update_watches = true;
+      if (next_event_data->subdir_.empty()) {
+        *target_file_affected = true;
+      }
+    }
+  }
+}
+
+bool FilePathWatcherKQueue::UpdateWatches(bool* target_file_affected) {
+  // Iterate over events adding kevents for items that exist to the kqueue.
+  // Then check to see if new components in the path have been created.
+  // Repeat until no new components in the path are detected.
+  // This is to get around races in directory creation in a watched path.
+  bool update_watches = true;
+  while (update_watches) {
+    size_t valid;
+    for (valid = 0; valid < events_.size(); ++valid) {
+      if (!IsKeventFileDescriptorOpen(events_[valid])) {
+        break;
+      }
+    }
+    if (valid == 0) {
+      // The root of the file path is inaccessible?
+      return false;
+    }
+
+    EventVector updates(valid);
+    int count = HANDLE_EINTR(kevent(kqueue_, &events_[0], valid, &updates[0],
+                                    valid, NULL));
+    if (!AreKeventValuesValid(&updates[0], count)) {
+      return false;
+    }
+    update_watches = false;
+    for (; valid < events_.size(); ++valid) {
+      EventData* event_data = EventDataForKevent(events_[valid]);
+      events_[valid].ident = FileDescriptorForPath(event_data->path_);
+      if (IsKeventFileDescriptorOpen(events_[valid])) {
+        update_watches = true;
+        if (event_data->subdir_.empty()) {
+          *target_file_affected = true;
+        }
+      } else {
+        break;
+      }
+    }
+  }
+  return true;
+}
+
+bool FilePathWatcherKQueue::Watch(const FilePath& path,
+                                  bool recursive,
+                                  const FilePathWatcher::Callback& callback) {
+  DCHECK(target_.value().empty());  // Can only watch one path.
+  DCHECK(!callback.is_null());
+  DCHECK_EQ(kqueue_, -1);
+  // Recursive watch is not supported using kqueue.
+  DCHECK(!recursive);
+
+  callback_ = callback;
+  target_ = path;
+
+  set_task_runner(SequencedTaskRunnerHandle::Get());
+
+  kqueue_ = kqueue();
+  if (kqueue_ == -1) {
+    DPLOG(ERROR) << "kqueue";
+    return false;
+  }
+
+  int last_entry = EventsForPath(target_, &events_);
+  DCHECK_NE(last_entry, 0);
+
+  EventVector responses(last_entry);
+
+  int count = HANDLE_EINTR(kevent(kqueue_, &events_[0], last_entry,
+                                  &responses[0], last_entry, NULL));
+  if (!AreKeventValuesValid(&responses[0], count)) {
+    // Calling Cancel() here to close any file descriptors that were opened.
+    // This would happen in the destructor anyways, but FilePathWatchers tend to
+    // be long lived, and if an error has occurred, there is no reason to waste
+    // the file descriptors.
+    Cancel();
+    return false;
+  }
+
+  // It's safe to use Unretained() because the watch is cancelled and the
+  // callback cannot be invoked after |kqueue_watch_controller_| (which is a
+  // member of |this|) has been deleted.
+  kqueue_watch_controller_ = FileDescriptorWatcher::WatchReadable(
+      kqueue_,
+      Bind(&FilePathWatcherKQueue::OnKQueueReadable, Unretained(this)));
+
+  return true;
+}
+
+void FilePathWatcherKQueue::Cancel() {
+  if (!task_runner()) {
+    set_cancelled();
+    return;
+  }
+
+  DCHECK(task_runner()->RunsTasksInCurrentSequence());
+  if (!is_cancelled()) {
+    set_cancelled();
+    kqueue_watch_controller_.reset();
+    if (IGNORE_EINTR(close(kqueue_)) != 0) {
+      DPLOG(ERROR) << "close kqueue";
+    }
+    kqueue_ = -1;
+    std::for_each(events_.begin(), events_.end(), ReleaseEvent);
+    events_.clear();
+    callback_.Reset();
+  }
+}
+
+void FilePathWatcherKQueue::OnKQueueReadable() {
+  DCHECK(task_runner()->RunsTasksInCurrentSequence());
+  DCHECK(events_.size());
+
+  // Request the file system update notifications that have occurred and return
+  // them in |updates|. |count| will contain the number of updates that have
+  // occurred.
+  EventVector updates(events_.size());
+  struct timespec timeout = {0, 0};
+  int count = HANDLE_EINTR(kevent(kqueue_, NULL, 0, &updates[0], updates.size(),
+                                  &timeout));
+
+  // Error values are stored within updates, so check to make sure that no
+  // errors occurred.
+  if (!AreKeventValuesValid(&updates[0], count)) {
+    callback_.Run(target_, true /* error */);
+    Cancel();
+    return;
+  }
+
+  bool update_watches = false;
+  bool send_notification = false;
+
+  // Iterate through each of the updates and react to them.
+  for (int i = 0; i < count; ++i) {
+    // Find our kevent record that matches the update notification.
+    EventVector::iterator event = events_.begin();
+    for (; event != events_.end(); ++event) {
+      if (!IsKeventFileDescriptorOpen(*event) ||
+          event->ident == updates[i].ident) {
+        break;
+      }
+    }
+    if (event == events_.end() || !IsKeventFileDescriptorOpen(*event)) {
+      // The event may no longer exist in |events_| because another event
+      // modified |events_| in such a way to make it invalid. For example if
+      // the path is /foo/bar/bam and foo is deleted, NOTE_DELETE events for
+      // foo, bar and bam will be sent. If foo is processed first, then
+      // the file descriptors for bar and bam will already be closed and set
+      // to -1 before they get a chance to be processed.
+      continue;
+    }
+
+    EventData* event_data = EventDataForKevent(*event);
+
+    // If the subdir is empty, this is the last item on the path and is the
+    // target file.
+    bool target_file_affected = event_data->subdir_.empty();
+    if ((updates[i].fflags & NOTE_ATTRIB) && !target_file_affected) {
+      HandleAttributesChange(event, &target_file_affected, &update_watches);
+    }
+    if (updates[i].fflags & (NOTE_DELETE | NOTE_REVOKE | NOTE_RENAME)) {
+      HandleDeleteOrMoveChange(event, &target_file_affected, &update_watches);
+    }
+    if ((updates[i].fflags & NOTE_WRITE) && !target_file_affected) {
+      HandleCreateItemChange(event, &target_file_affected, &update_watches);
+    }
+    send_notification |= target_file_affected;
+  }
+
+  if (update_watches) {
+    if (!UpdateWatches(&send_notification)) {
+      callback_.Run(target_, true /* error */);
+      Cancel();
+    }
+  }
+
+  if (send_notification) {
+    callback_.Run(target_, false);
+  }
+}
+
+}  // namespace base
diff --git a/base/files/file_path_watcher_kqueue.h b/base/files/file_path_watcher_kqueue.h
new file mode 100644
index 0000000..ef79be5
--- /dev/null
+++ b/base/files/file_path_watcher_kqueue.h
@@ -0,0 +1,125 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_FILES_FILE_PATH_WATCHER_KQUEUE_H_
+#define BASE_FILES_FILE_PATH_WATCHER_KQUEUE_H_
+
+#include <sys/event.h>
+
+#include <memory>
+#include <vector>
+
+#include "base/files/file_descriptor_watcher_posix.h"
+#include "base/files/file_path.h"
+#include "base/files/file_path_watcher.h"
+#include "base/macros.h"
+
+namespace base {
+
+// Mac-specific file watcher implementation based on kqueue.
+// The Linux and Windows versions are able to detect:
+// - file creation/deletion/modification in a watched directory
+// - file creation/deletion/modification for a watched file
+// - modifications to the paths to a watched object that would affect the
+//   object such as renaming/attibute changes etc.
+// The kqueue implementation will handle all of the items in the list above
+// except for detecting modifications to files in a watched directory. It will
+// detect the creation and deletion of files, just not the modification of
+// files. It does however detect the attribute changes that the FSEvents impl
+// would miss.
+class FilePathWatcherKQueue : public FilePathWatcher::PlatformDelegate {
+ public:
+  FilePathWatcherKQueue();
+  ~FilePathWatcherKQueue() override;
+
+  // FilePathWatcher::PlatformDelegate overrides.
+  bool Watch(const FilePath& path,
+             bool recursive,
+             const FilePathWatcher::Callback& callback) override;
+  void Cancel() override;
+
+ private:
+  class EventData {
+   public:
+    EventData(const FilePath& path, const FilePath::StringType& subdir)
+        : path_(path), subdir_(subdir) { }
+    FilePath path_;  // Full path to this item.
+    FilePath::StringType subdir_;  // Path to any sub item.
+  };
+
+  typedef std::vector<struct kevent> EventVector;
+
+  // Called when data is available in |kqueue_|.
+  void OnKQueueReadable();
+
+  // Returns true if the kevent values are error free.
+  bool AreKeventValuesValid(struct kevent* kevents, int count);
+
+  // Respond to a change of attributes of the path component represented by
+  // |event|. Sets |target_file_affected| to true if |target_| is affected.
+  // Sets |update_watches| to true if |events_| need to be updated.
+  void HandleAttributesChange(const EventVector::iterator& event,
+                              bool* target_file_affected,
+                              bool* update_watches);
+
+  // Respond to a move or deletion of the path component represented by
+  // |event|. Sets |target_file_affected| to true if |target_| is affected.
+  // Sets |update_watches| to true if |events_| need to be updated.
+  void HandleDeleteOrMoveChange(const EventVector::iterator& event,
+                                bool* target_file_affected,
+                                bool* update_watches);
+
+  // Respond to a creation of an item in the path component represented by
+  // |event|. Sets |target_file_affected| to true if |target_| is affected.
+  // Sets |update_watches| to true if |events_| need to be updated.
+  void HandleCreateItemChange(const EventVector::iterator& event,
+                              bool* target_file_affected,
+                              bool* update_watches);
+
+  // Update |events_| with the current status of the system.
+  // Sets |target_file_affected| to true if |target_| is affected.
+  // Returns false if an error occurs.
+  bool UpdateWatches(bool* target_file_affected);
+
+  // Fills |events| with one kevent per component in |path|.
+  // Returns the number of valid events created where a valid event is
+  // defined as one that has a ident (file descriptor) field != -1.
+  static int EventsForPath(FilePath path, EventVector *events);
+
+  // Release a kevent generated by EventsForPath.
+  static void ReleaseEvent(struct kevent& event);
+
+  // Returns a file descriptor that will not block the system from deleting
+  // the file it references.
+  static uintptr_t FileDescriptorForPath(const FilePath& path);
+
+  static const uintptr_t kNoFileDescriptor = static_cast<uintptr_t>(-1);
+
+  // Closes |*fd| and sets |*fd| to -1.
+  static void CloseFileDescriptor(uintptr_t* fd);
+
+  // Returns true if kevent has open file descriptor.
+  static bool IsKeventFileDescriptorOpen(const struct kevent& event) {
+    return event.ident != kNoFileDescriptor;
+  }
+
+  static EventData* EventDataForKevent(const struct kevent& event) {
+    return reinterpret_cast<EventData*>(event.udata);
+  }
+
+  EventVector events_;
+  FilePathWatcher::Callback callback_;
+  FilePath target_;
+  int kqueue_;
+
+  // Throughout the lifetime of this, OnKQueueReadable() will be called when
+  // data is available in |kqueue_|.
+  std::unique_ptr<FileDescriptorWatcher::Controller> kqueue_watch_controller_;
+
+  DISALLOW_COPY_AND_ASSIGN(FilePathWatcherKQueue);
+};
+
+}  // namespace base
+
+#endif  // BASE_FILES_FILE_PATH_WATCHER_KQUEUE_H_
diff --git a/base/files/file_path_watcher_stub.cc b/base/files/file_path_watcher_stub.cc
new file mode 100644
index 0000000..93a5bab
--- /dev/null
+++ b/base/files/file_path_watcher_stub.cc
@@ -0,0 +1,41 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file exists for Unix systems which don't have the inotify headers, and
+// thus cannot build file_watcher_inotify.cc
+
+#include "base/files/file_path_watcher.h"
+
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+
+namespace base {
+
+namespace {
+
+class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate {
+ public:
+  FilePathWatcherImpl() = default;
+  ~FilePathWatcherImpl() override = default;
+
+  bool Watch(const FilePath& path,
+             bool recursive,
+             const FilePathWatcher::Callback& callback) override {
+    return false;
+  }
+
+  void Cancel() override {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(FilePathWatcherImpl);
+};
+
+}  // namespace
+
+FilePathWatcher::FilePathWatcher() {
+  sequence_checker_.DetachFromSequence();
+  impl_ = std::make_unique<FilePathWatcherImpl>();
+}
+
+}  // namespace base
diff --git a/base/files/file_proxy.cc b/base/files/file_proxy.cc
new file mode 100644
index 0000000..f16e594
--- /dev/null
+++ b/base/files/file_proxy.cc
@@ -0,0 +1,358 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/files/file_proxy.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/files/file.h"
+#include "base/files/file_util.h"
+#include "base/location.h"
+#include "base/macros.h"
+#include "base/task_runner.h"
+#include "base/task_runner_util.h"
+
+namespace {
+
+void FileDeleter(base::File file) {
+}
+
+}  // namespace
+
+namespace base {
+
+class FileHelper {
+ public:
+   FileHelper(FileProxy* proxy, File file)
+      : file_(std::move(file)),
+        error_(File::FILE_ERROR_FAILED),
+        task_runner_(proxy->task_runner()),
+        proxy_(AsWeakPtr(proxy)) {
+   }
+
+   void PassFile() {
+     if (proxy_)
+       proxy_->SetFile(std::move(file_));
+     else if (file_.IsValid())
+       task_runner_->PostTask(FROM_HERE,
+                              BindOnce(&FileDeleter, std::move(file_)));
+   }
+
+ protected:
+  File file_;
+  File::Error error_;
+
+ private:
+  scoped_refptr<TaskRunner> task_runner_;
+  WeakPtr<FileProxy> proxy_;
+  DISALLOW_COPY_AND_ASSIGN(FileHelper);
+};
+
+namespace {
+
+class GenericFileHelper : public FileHelper {
+ public:
+  GenericFileHelper(FileProxy* proxy, File file)
+      : FileHelper(proxy, std::move(file)) {
+  }
+
+  void Close() {
+    file_.Close();
+    error_ = File::FILE_OK;
+  }
+
+  void SetTimes(Time last_access_time, Time last_modified_time) {
+    bool rv = file_.SetTimes(last_access_time, last_modified_time);
+    error_ = rv ? File::FILE_OK : File::FILE_ERROR_FAILED;
+  }
+
+  void SetLength(int64_t length) {
+    if (file_.SetLength(length))
+      error_ = File::FILE_OK;
+  }
+
+  void Flush() {
+    if (file_.Flush())
+      error_ = File::FILE_OK;
+  }
+
+  void Reply(FileProxy::StatusCallback callback) {
+    PassFile();
+    if (!callback.is_null())
+      std::move(callback).Run(error_);
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(GenericFileHelper);
+};
+
+class CreateOrOpenHelper : public FileHelper {
+ public:
+  CreateOrOpenHelper(FileProxy* proxy, File file)
+      : FileHelper(proxy, std::move(file)) {
+  }
+
+  void RunWork(const FilePath& file_path, int file_flags) {
+    file_.Initialize(file_path, file_flags);
+    error_ = file_.IsValid() ? File::FILE_OK : file_.error_details();
+  }
+
+  void Reply(FileProxy::StatusCallback callback) {
+    DCHECK(!callback.is_null());
+    PassFile();
+    std::move(callback).Run(error_);
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CreateOrOpenHelper);
+};
+
+class CreateTemporaryHelper : public FileHelper {
+ public:
+  CreateTemporaryHelper(FileProxy* proxy, File file)
+      : FileHelper(proxy, std::move(file)) {
+  }
+
+  void RunWork(uint32_t additional_file_flags) {
+    // TODO(darin): file_util should have a variant of CreateTemporaryFile
+    // that returns a FilePath and a File.
+    if (!CreateTemporaryFile(&file_path_)) {
+      // TODO(davidben): base::CreateTemporaryFile should preserve the error
+      // code.
+      error_ = File::FILE_ERROR_FAILED;
+      return;
+    }
+
+    uint32_t file_flags = File::FLAG_WRITE | File::FLAG_TEMPORARY |
+                          File::FLAG_CREATE_ALWAYS | additional_file_flags;
+
+    file_.Initialize(file_path_, file_flags);
+    if (file_.IsValid()) {
+      error_ = File::FILE_OK;
+    } else {
+      error_ = file_.error_details();
+      DeleteFile(file_path_, false);
+      file_path_.clear();
+    }
+  }
+
+  void Reply(FileProxy::CreateTemporaryCallback callback) {
+    DCHECK(!callback.is_null());
+    PassFile();
+    std::move(callback).Run(error_, file_path_);
+  }
+
+ private:
+  FilePath file_path_;
+  DISALLOW_COPY_AND_ASSIGN(CreateTemporaryHelper);
+};
+
+class GetInfoHelper : public FileHelper {
+ public:
+  GetInfoHelper(FileProxy* proxy, File file)
+      : FileHelper(proxy, std::move(file)) {
+  }
+
+  void RunWork() {
+    if (file_.GetInfo(&file_info_))
+      error_  = File::FILE_OK;
+  }
+
+  void Reply(FileProxy::GetFileInfoCallback callback) {
+    PassFile();
+    DCHECK(!callback.is_null());
+    std::move(callback).Run(error_, file_info_);
+  }
+
+ private:
+  File::Info file_info_;
+  DISALLOW_COPY_AND_ASSIGN(GetInfoHelper);
+};
+
+class ReadHelper : public FileHelper {
+ public:
+  ReadHelper(FileProxy* proxy, File file, int bytes_to_read)
+      : FileHelper(proxy, std::move(file)),
+        buffer_(new char[bytes_to_read]),
+        bytes_to_read_(bytes_to_read),
+        bytes_read_(0) {
+  }
+
+  void RunWork(int64_t offset) {
+    bytes_read_ = file_.Read(offset, buffer_.get(), bytes_to_read_);
+    error_ = (bytes_read_ < 0) ? File::FILE_ERROR_FAILED : File::FILE_OK;
+  }
+
+  void Reply(FileProxy::ReadCallback callback) {
+    PassFile();
+    DCHECK(!callback.is_null());
+    std::move(callback).Run(error_, buffer_.get(), bytes_read_);
+  }
+
+ private:
+  std::unique_ptr<char[]> buffer_;
+  int bytes_to_read_;
+  int bytes_read_;
+  DISALLOW_COPY_AND_ASSIGN(ReadHelper);
+};
+
+class WriteHelper : public FileHelper {
+ public:
+  WriteHelper(FileProxy* proxy,
+              File file,
+              const char* buffer, int bytes_to_write)
+      : FileHelper(proxy, std::move(file)),
+        buffer_(new char[bytes_to_write]),
+        bytes_to_write_(bytes_to_write),
+        bytes_written_(0) {
+    memcpy(buffer_.get(), buffer, bytes_to_write);
+  }
+
+  void RunWork(int64_t offset) {
+    bytes_written_ = file_.Write(offset, buffer_.get(), bytes_to_write_);
+    error_ = (bytes_written_ < 0) ? File::FILE_ERROR_FAILED : File::FILE_OK;
+  }
+
+  void Reply(FileProxy::WriteCallback callback) {
+    PassFile();
+    if (!callback.is_null())
+      std::move(callback).Run(error_, bytes_written_);
+  }
+
+ private:
+  std::unique_ptr<char[]> buffer_;
+  int bytes_to_write_;
+  int bytes_written_;
+  DISALLOW_COPY_AND_ASSIGN(WriteHelper);
+};
+
+}  // namespace
+
+FileProxy::FileProxy(TaskRunner* task_runner) : task_runner_(task_runner) {
+}
+
+FileProxy::~FileProxy() {
+  if (file_.IsValid())
+    task_runner_->PostTask(FROM_HERE, BindOnce(&FileDeleter, std::move(file_)));
+}
+
+bool FileProxy::CreateOrOpen(const FilePath& file_path,
+                             uint32_t file_flags,
+                             StatusCallback callback) {
+  DCHECK(!file_.IsValid());
+  CreateOrOpenHelper* helper = new CreateOrOpenHelper(this, File());
+  return task_runner_->PostTaskAndReply(
+      FROM_HERE,
+      BindOnce(&CreateOrOpenHelper::RunWork, Unretained(helper), file_path,
+               file_flags),
+      BindOnce(&CreateOrOpenHelper::Reply, Owned(helper), std::move(callback)));
+}
+
+bool FileProxy::CreateTemporary(uint32_t additional_file_flags,
+                                CreateTemporaryCallback callback) {
+  DCHECK(!file_.IsValid());
+  CreateTemporaryHelper* helper = new CreateTemporaryHelper(this, File());
+  return task_runner_->PostTaskAndReply(
+      FROM_HERE,
+      BindOnce(&CreateTemporaryHelper::RunWork, Unretained(helper),
+               additional_file_flags),
+      BindOnce(&CreateTemporaryHelper::Reply, Owned(helper),
+               std::move(callback)));
+}
+
+bool FileProxy::IsValid() const {
+  return file_.IsValid();
+}
+
+void FileProxy::SetFile(File file) {
+  DCHECK(!file_.IsValid());
+  file_ = std::move(file);
+}
+
+File FileProxy::TakeFile() {
+  return std::move(file_);
+}
+
+File FileProxy::DuplicateFile() {
+  return file_.Duplicate();
+}
+
+PlatformFile FileProxy::GetPlatformFile() const {
+  return file_.GetPlatformFile();
+}
+
+bool FileProxy::Close(StatusCallback callback) {
+  DCHECK(file_.IsValid());
+  GenericFileHelper* helper = new GenericFileHelper(this, std::move(file_));
+  return task_runner_->PostTaskAndReply(
+      FROM_HERE, BindOnce(&GenericFileHelper::Close, Unretained(helper)),
+      BindOnce(&GenericFileHelper::Reply, Owned(helper), std::move(callback)));
+}
+
+bool FileProxy::GetInfo(GetFileInfoCallback callback) {
+  DCHECK(file_.IsValid());
+  GetInfoHelper* helper = new GetInfoHelper(this, std::move(file_));
+  return task_runner_->PostTaskAndReply(
+      FROM_HERE, BindOnce(&GetInfoHelper::RunWork, Unretained(helper)),
+      BindOnce(&GetInfoHelper::Reply, Owned(helper), std::move(callback)));
+}
+
+bool FileProxy::Read(int64_t offset, int bytes_to_read, ReadCallback callback) {
+  DCHECK(file_.IsValid());
+  if (bytes_to_read < 0)
+    return false;
+
+  ReadHelper* helper = new ReadHelper(this, std::move(file_), bytes_to_read);
+  return task_runner_->PostTaskAndReply(
+      FROM_HERE, BindOnce(&ReadHelper::RunWork, Unretained(helper), offset),
+      BindOnce(&ReadHelper::Reply, Owned(helper), std::move(callback)));
+}
+
+bool FileProxy::Write(int64_t offset,
+                      const char* buffer,
+                      int bytes_to_write,
+                      WriteCallback callback) {
+  DCHECK(file_.IsValid());
+  if (bytes_to_write <= 0 || buffer == nullptr)
+    return false;
+
+  WriteHelper* helper =
+      new WriteHelper(this, std::move(file_), buffer, bytes_to_write);
+  return task_runner_->PostTaskAndReply(
+      FROM_HERE, BindOnce(&WriteHelper::RunWork, Unretained(helper), offset),
+      BindOnce(&WriteHelper::Reply, Owned(helper), std::move(callback)));
+}
+
+bool FileProxy::SetTimes(Time last_access_time,
+                         Time last_modified_time,
+                         StatusCallback callback) {
+  DCHECK(file_.IsValid());
+  GenericFileHelper* helper = new GenericFileHelper(this, std::move(file_));
+  return task_runner_->PostTaskAndReply(
+      FROM_HERE,
+      BindOnce(&GenericFileHelper::SetTimes, Unretained(helper),
+               last_access_time, last_modified_time),
+      BindOnce(&GenericFileHelper::Reply, Owned(helper), std::move(callback)));
+}
+
+bool FileProxy::SetLength(int64_t length, StatusCallback callback) {
+  DCHECK(file_.IsValid());
+  GenericFileHelper* helper = new GenericFileHelper(this, std::move(file_));
+  return task_runner_->PostTaskAndReply(
+      FROM_HERE,
+      BindOnce(&GenericFileHelper::SetLength, Unretained(helper), length),
+      BindOnce(&GenericFileHelper::Reply, Owned(helper), std::move(callback)));
+}
+
+bool FileProxy::Flush(StatusCallback callback) {
+  DCHECK(file_.IsValid());
+  GenericFileHelper* helper = new GenericFileHelper(this, std::move(file_));
+  return task_runner_->PostTaskAndReply(
+      FROM_HERE, BindOnce(&GenericFileHelper::Flush, Unretained(helper)),
+      BindOnce(&GenericFileHelper::Reply, Owned(helper), std::move(callback)));
+}
+
+}  // namespace base
diff --git a/base/files/file_proxy.h b/base/files/file_proxy.h
new file mode 100644
index 0000000..d17e4d3
--- /dev/null
+++ b/base/files/file_proxy.h
@@ -0,0 +1,142 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_FILES_FILE_PROXY_H_
+#define BASE_FILES_FILE_PROXY_H_
+
+#include <stdint.h>
+
+#include "base/base_export.h"
+#include "base/callback_forward.h"
+#include "base/files/file.h"
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+
+namespace base {
+
+class TaskRunner;
+class Time;
+
+// This class provides asynchronous access to a File. All methods follow the
+// same rules of the equivalent File method, as they are implemented by bouncing
+// the operation to File using a TaskRunner.
+//
+// This class performs automatic proxying to close the underlying file at
+// destruction.
+//
+// The TaskRunner is in charge of any sequencing of the operations, but a single
+// operation can be proxied at a time, regardless of the use of a callback.
+// In other words, having a sequence like
+//
+//   proxy.Write(...);
+//   proxy.Write(...);
+//
+// means the second Write will always fail.
+class BASE_EXPORT FileProxy : public SupportsWeakPtr<FileProxy> {
+ public:
+  // This callback is used by methods that report only an error code. It is
+  // valid to pass a null callback to some functions that takes a
+  // StatusCallback, in which case the operation will complete silently.
+  using StatusCallback = OnceCallback<void(File::Error)>;
+  using CreateTemporaryCallback =
+      OnceCallback<void(File::Error, const FilePath&)>;
+  using GetFileInfoCallback =
+      OnceCallback<void(File::Error, const File::Info&)>;
+  using ReadCallback =
+      OnceCallback<void(File::Error, const char* data, int bytes_read)>;
+  using WriteCallback = OnceCallback<void(File::Error, int bytes_written)>;
+
+  FileProxy();
+  explicit FileProxy(TaskRunner* task_runner);
+  ~FileProxy();
+
+  // Creates or opens a file with the given flags. It is invalid to pass a null
+  // callback. If File::FLAG_CREATE is set in |file_flags| it always tries to
+  // create a new file at the given |file_path| and fails if the file already
+  // exists.
+  //
+  // This returns false if task posting to |task_runner| has failed.
+  bool CreateOrOpen(const FilePath& file_path,
+                    uint32_t file_flags,
+                    StatusCallback callback);
+
+  // Creates a temporary file for writing. The path and an open file are
+  // returned. It is invalid to pass a null callback. The additional file flags
+  // will be added on top of the default file flags which are:
+  //   File::FLAG_CREATE_ALWAYS
+  //   File::FLAG_WRITE
+  //   File::FLAG_TEMPORARY.
+  //
+  // This returns false if task posting to |task_runner| has failed.
+  bool CreateTemporary(uint32_t additional_file_flags,
+                       CreateTemporaryCallback callback);
+
+  // Returns true if the underlying |file_| is valid.
+  bool IsValid() const;
+
+  // Returns true if a new file was created (or an old one truncated to zero
+  // length to simulate a new file), and false otherwise.
+  bool created() const { return file_.created(); }
+
+  // Claims ownership of |file|. It is an error to call this method when
+  // IsValid() returns true.
+  void SetFile(File file);
+
+  File TakeFile();
+
+  // Returns a new File object that is a duplicate of the underlying |file_|.
+  // See the comment at File::Duplicate for caveats.
+  File DuplicateFile();
+
+  PlatformFile GetPlatformFile() const;
+
+  // Proxies File::Close. The callback can be null.
+  // This returns false if task posting to |task_runner| has failed.
+  bool Close(StatusCallback callback);
+
+  // Proxies File::GetInfo. The callback can't be null.
+  // This returns false if task posting to |task_runner| has failed.
+  bool GetInfo(GetFileInfoCallback callback);
+
+  // Proxies File::Read. The callback can't be null.
+  // This returns false if |bytes_to_read| is less than zero, or
+  // if task posting to |task_runner| has failed.
+  bool Read(int64_t offset, int bytes_to_read, ReadCallback callback);
+
+  // Proxies File::Write. The callback can be null.
+  // This returns false if |bytes_to_write| is less than or equal to zero,
+  // if |buffer| is NULL, or if task posting to |task_runner| has failed.
+  bool Write(int64_t offset,
+             const char* buffer,
+             int bytes_to_write,
+             WriteCallback callback);
+
+  // Proxies File::SetTimes. The callback can be null.
+  // This returns false if task posting to |task_runner| has failed.
+  bool SetTimes(Time last_access_time,
+                Time last_modified_time,
+                StatusCallback callback);
+
+  // Proxies File::SetLength. The callback can be null.
+  // This returns false if task posting to |task_runner| has failed.
+  bool SetLength(int64_t length, StatusCallback callback);
+
+  // Proxies File::Flush. The callback can be null.
+  // This returns false if task posting to |task_runner| has failed.
+  bool Flush(StatusCallback callback);
+
+ private:
+  friend class FileHelper;
+  TaskRunner* task_runner() { return task_runner_.get(); }
+
+  scoped_refptr<TaskRunner> task_runner_;
+  File file_;
+  DISALLOW_COPY_AND_ASSIGN(FileProxy);
+};
+
+}  // namespace base
+
+#endif  // BASE_FILES_FILE_PROXY_H_
diff --git a/base/files/file_proxy_unittest.cc b/base/files/file_proxy_unittest.cc
new file mode 100644
index 0000000..cb689db
--- /dev/null
+++ b/base/files/file_proxy_unittest.cc
@@ -0,0 +1,401 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/files/file_proxy.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/files/file.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/threading/thread.h"
+#include "base/threading/thread_restrictions.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+class FileProxyTest : public testing::Test {
+ public:
+  FileProxyTest()
+      : file_thread_("FileProxyTestFileThread"),
+        error_(File::FILE_OK),
+        bytes_written_(-1),
+        weak_factory_(this) {}
+
+  void SetUp() override {
+    ASSERT_TRUE(dir_.CreateUniqueTempDir());
+    ASSERT_TRUE(file_thread_.Start());
+  }
+
+  void DidFinish(File::Error error) {
+    error_ = error;
+    RunLoop::QuitCurrentWhenIdleDeprecated();
+  }
+
+  void DidCreateOrOpen(File::Error error) {
+    error_ = error;
+    RunLoop::QuitCurrentWhenIdleDeprecated();
+  }
+
+  void DidCreateTemporary(File::Error error,
+                          const FilePath& path) {
+    error_ = error;
+    path_ = path;
+    RunLoop::QuitCurrentWhenIdleDeprecated();
+  }
+
+  void DidGetFileInfo(File::Error error,
+                      const File::Info& file_info) {
+    error_ = error;
+    file_info_ = file_info;
+    RunLoop::QuitCurrentWhenIdleDeprecated();
+  }
+
+  void DidRead(File::Error error,
+               const char* data,
+               int bytes_read) {
+    error_ = error;
+    buffer_.resize(bytes_read);
+    memcpy(&buffer_[0], data, bytes_read);
+    RunLoop::QuitCurrentWhenIdleDeprecated();
+  }
+
+  void DidWrite(File::Error error,
+                int bytes_written) {
+    error_ = error;
+    bytes_written_ = bytes_written;
+    RunLoop::QuitCurrentWhenIdleDeprecated();
+  }
+
+ protected:
+  void CreateProxy(uint32_t flags, FileProxy* proxy) {
+    proxy->CreateOrOpen(
+        TestPath(), flags,
+        BindOnce(&FileProxyTest::DidCreateOrOpen, weak_factory_.GetWeakPtr()));
+    RunLoop().Run();
+    EXPECT_TRUE(proxy->IsValid());
+  }
+
+  TaskRunner* file_task_runner() const {
+    return file_thread_.task_runner().get();
+  }
+  const FilePath& TestDirPath() const { return dir_.GetPath(); }
+  const FilePath TestPath() const { return dir_.GetPath().AppendASCII("test"); }
+
+  ScopedTempDir dir_;
+  MessageLoopForIO message_loop_;
+  Thread file_thread_;
+
+  File::Error error_;
+  FilePath path_;
+  File::Info file_info_;
+  std::vector<char> buffer_;
+  int bytes_written_;
+  WeakPtrFactory<FileProxyTest> weak_factory_;
+};
+
+TEST_F(FileProxyTest, CreateOrOpen_Create) {
+  FileProxy proxy(file_task_runner());
+  proxy.CreateOrOpen(
+      TestPath(), File::FLAG_CREATE | File::FLAG_READ,
+      BindOnce(&FileProxyTest::DidCreateOrOpen, weak_factory_.GetWeakPtr()));
+  RunLoop().Run();
+
+  EXPECT_EQ(File::FILE_OK, error_);
+  EXPECT_TRUE(proxy.IsValid());
+  EXPECT_TRUE(proxy.created());
+  EXPECT_TRUE(PathExists(TestPath()));
+}
+
+TEST_F(FileProxyTest, CreateOrOpen_Open) {
+  // Creates a file.
+  base::WriteFile(TestPath(), nullptr, 0);
+  ASSERT_TRUE(PathExists(TestPath()));
+
+  // Opens the created file.
+  FileProxy proxy(file_task_runner());
+  proxy.CreateOrOpen(
+      TestPath(), File::FLAG_OPEN | File::FLAG_READ,
+      BindOnce(&FileProxyTest::DidCreateOrOpen, weak_factory_.GetWeakPtr()));
+  RunLoop().Run();
+
+  EXPECT_EQ(File::FILE_OK, error_);
+  EXPECT_TRUE(proxy.IsValid());
+  EXPECT_FALSE(proxy.created());
+}
+
+TEST_F(FileProxyTest, CreateOrOpen_OpenNonExistent) {
+  FileProxy proxy(file_task_runner());
+  proxy.CreateOrOpen(
+      TestPath(), File::FLAG_OPEN | File::FLAG_READ,
+      BindOnce(&FileProxyTest::DidCreateOrOpen, weak_factory_.GetWeakPtr()));
+  RunLoop().Run();
+  EXPECT_EQ(File::FILE_ERROR_NOT_FOUND, error_);
+  EXPECT_FALSE(proxy.IsValid());
+  EXPECT_FALSE(proxy.created());
+  EXPECT_FALSE(PathExists(TestPath()));
+}
+
+TEST_F(FileProxyTest, CreateOrOpen_AbandonedCreate) {
+  bool prev = ThreadRestrictions::SetIOAllowed(false);
+  {
+    FileProxy proxy(file_task_runner());
+    proxy.CreateOrOpen(
+        TestPath(), File::FLAG_CREATE | File::FLAG_READ,
+        BindOnce(&FileProxyTest::DidCreateOrOpen, weak_factory_.GetWeakPtr()));
+  }
+  RunLoop().Run();
+  ThreadRestrictions::SetIOAllowed(prev);
+
+  EXPECT_TRUE(PathExists(TestPath()));
+}
+
+TEST_F(FileProxyTest, Close) {
+  // Creates a file.
+  FileProxy proxy(file_task_runner());
+  CreateProxy(File::FLAG_CREATE | File::FLAG_WRITE, &proxy);
+
+#if defined(OS_WIN)
+  // This fails on Windows if the file is not closed.
+  EXPECT_FALSE(base::Move(TestPath(), TestDirPath().AppendASCII("new")));
+#endif
+
+  proxy.Close(BindOnce(&FileProxyTest::DidFinish, weak_factory_.GetWeakPtr()));
+  RunLoop().Run();
+  EXPECT_EQ(File::FILE_OK, error_);
+  EXPECT_FALSE(proxy.IsValid());
+
+  // Now it should pass on all platforms.
+  EXPECT_TRUE(base::Move(TestPath(), TestDirPath().AppendASCII("new")));
+}
+
+TEST_F(FileProxyTest, CreateTemporary) {
+  {
+    FileProxy proxy(file_task_runner());
+    proxy.CreateTemporary(0 /* additional_file_flags */,
+                          BindOnce(&FileProxyTest::DidCreateTemporary,
+                                   weak_factory_.GetWeakPtr()));
+    RunLoop().Run();
+
+    EXPECT_TRUE(proxy.IsValid());
+    EXPECT_EQ(File::FILE_OK, error_);
+    EXPECT_TRUE(PathExists(path_));
+
+    // The file should be writable.
+    proxy.Write(0, "test", 4,
+                BindOnce(&FileProxyTest::DidWrite, weak_factory_.GetWeakPtr()));
+    RunLoop().Run();
+    EXPECT_EQ(File::FILE_OK, error_);
+    EXPECT_EQ(4, bytes_written_);
+  }
+
+  // Make sure the written data can be read from the returned path.
+  std::string data;
+  EXPECT_TRUE(ReadFileToString(path_, &data));
+  EXPECT_EQ("test", data);
+
+  // Make sure we can & do delete the created file to prevent leaks on the bots.
+  EXPECT_TRUE(base::DeleteFile(path_, false));
+}
+
+TEST_F(FileProxyTest, SetAndTake) {
+  File file(TestPath(), File::FLAG_CREATE | File::FLAG_READ);
+  ASSERT_TRUE(file.IsValid());
+  FileProxy proxy(file_task_runner());
+  EXPECT_FALSE(proxy.IsValid());
+  proxy.SetFile(std::move(file));
+  EXPECT_TRUE(proxy.IsValid());
+  EXPECT_FALSE(file.IsValid());
+
+  file = proxy.TakeFile();
+  EXPECT_FALSE(proxy.IsValid());
+  EXPECT_TRUE(file.IsValid());
+}
+
+TEST_F(FileProxyTest, DuplicateFile) {
+  FileProxy proxy(file_task_runner());
+  CreateProxy(File::FLAG_CREATE | File::FLAG_WRITE, &proxy);
+  ASSERT_TRUE(proxy.IsValid());
+
+  base::File duplicate = proxy.DuplicateFile();
+  EXPECT_TRUE(proxy.IsValid());
+  EXPECT_TRUE(duplicate.IsValid());
+
+  FileProxy invalid_proxy(file_task_runner());
+  ASSERT_FALSE(invalid_proxy.IsValid());
+
+  base::File invalid_duplicate = invalid_proxy.DuplicateFile();
+  EXPECT_FALSE(invalid_proxy.IsValid());
+  EXPECT_FALSE(invalid_duplicate.IsValid());
+}
+
+TEST_F(FileProxyTest, GetInfo) {
+  // Setup.
+  ASSERT_EQ(4, base::WriteFile(TestPath(), "test", 4));
+  File::Info expected_info;
+  GetFileInfo(TestPath(), &expected_info);
+
+  // Run.
+  FileProxy proxy(file_task_runner());
+  CreateProxy(File::FLAG_OPEN | File::FLAG_READ, &proxy);
+  proxy.GetInfo(
+      BindOnce(&FileProxyTest::DidGetFileInfo, weak_factory_.GetWeakPtr()));
+  RunLoop().Run();
+
+  // Verify.
+  EXPECT_EQ(File::FILE_OK, error_);
+  EXPECT_EQ(expected_info.size, file_info_.size);
+  EXPECT_EQ(expected_info.is_directory, file_info_.is_directory);
+  EXPECT_EQ(expected_info.is_symbolic_link, file_info_.is_symbolic_link);
+  EXPECT_EQ(expected_info.last_modified, file_info_.last_modified);
+  EXPECT_EQ(expected_info.creation_time, file_info_.creation_time);
+}
+
+TEST_F(FileProxyTest, Read) {
+  // Setup.
+  const char expected_data[] = "bleh";
+  int expected_bytes = arraysize(expected_data);
+  ASSERT_EQ(expected_bytes,
+            base::WriteFile(TestPath(), expected_data, expected_bytes));
+
+  // Run.
+  FileProxy proxy(file_task_runner());
+  CreateProxy(File::FLAG_OPEN | File::FLAG_READ, &proxy);
+
+  proxy.Read(0, 128,
+             BindOnce(&FileProxyTest::DidRead, weak_factory_.GetWeakPtr()));
+  RunLoop().Run();
+
+  // Verify.
+  EXPECT_EQ(File::FILE_OK, error_);
+  EXPECT_EQ(expected_bytes, static_cast<int>(buffer_.size()));
+  for (size_t i = 0; i < buffer_.size(); ++i) {
+    EXPECT_EQ(expected_data[i], buffer_[i]);
+  }
+}
+
+TEST_F(FileProxyTest, WriteAndFlush) {
+  FileProxy proxy(file_task_runner());
+  CreateProxy(File::FLAG_CREATE | File::FLAG_WRITE, &proxy);
+
+  const char data[] = "foo!";
+  int data_bytes = arraysize(data);
+  proxy.Write(0, data, data_bytes,
+              BindOnce(&FileProxyTest::DidWrite, weak_factory_.GetWeakPtr()));
+  RunLoop().Run();
+  EXPECT_EQ(File::FILE_OK, error_);
+  EXPECT_EQ(data_bytes, bytes_written_);
+
+  // Flush the written data.  (So that the following read should always
+  // succeed.  On some platforms it may work with or without this flush.)
+  proxy.Flush(BindOnce(&FileProxyTest::DidFinish, weak_factory_.GetWeakPtr()));
+  RunLoop().Run();
+  EXPECT_EQ(File::FILE_OK, error_);
+
+  // Verify the written data.
+  char buffer[10];
+  EXPECT_EQ(data_bytes, base::ReadFile(TestPath(), buffer, data_bytes));
+  for (int i = 0; i < data_bytes; ++i) {
+    EXPECT_EQ(data[i], buffer[i]);
+  }
+}
+
+#if defined(OS_ANDROID) || defined(OS_FUCHSIA)
+// Flaky on Android, see http://crbug.com/489602
+// TODO(crbug.com/851734): Implementation depends on stat, which is not
+// implemented on Fuchsia
+#define MAYBE_SetTimes DISABLED_SetTimes
+#else
+#define MAYBE_SetTimes SetTimes
+#endif
+TEST_F(FileProxyTest, MAYBE_SetTimes) {
+  FileProxy proxy(file_task_runner());
+  CreateProxy(
+      File::FLAG_CREATE | File::FLAG_WRITE | File::FLAG_WRITE_ATTRIBUTES,
+      &proxy);
+
+  Time last_accessed_time = Time::Now() - TimeDelta::FromDays(12345);
+  Time last_modified_time = Time::Now() - TimeDelta::FromHours(98765);
+
+  proxy.SetTimes(
+      last_accessed_time, last_modified_time,
+      BindOnce(&FileProxyTest::DidFinish, weak_factory_.GetWeakPtr()));
+  RunLoop().Run();
+  EXPECT_EQ(File::FILE_OK, error_);
+
+  File::Info info;
+  GetFileInfo(TestPath(), &info);
+
+  // The returned values may only have the seconds precision, so we cast
+  // the double values to int here.
+  EXPECT_EQ(static_cast<int>(last_modified_time.ToDoubleT()),
+            static_cast<int>(info.last_modified.ToDoubleT()));
+  EXPECT_EQ(static_cast<int>(last_accessed_time.ToDoubleT()),
+            static_cast<int>(info.last_accessed.ToDoubleT()));
+}
+
+TEST_F(FileProxyTest, SetLength_Shrink) {
+  // Setup.
+  const char kTestData[] = "0123456789";
+  ASSERT_EQ(10, base::WriteFile(TestPath(), kTestData, 10));
+  File::Info info;
+  GetFileInfo(TestPath(), &info);
+  ASSERT_EQ(10, info.size);
+
+  // Run.
+  FileProxy proxy(file_task_runner());
+  CreateProxy(File::FLAG_OPEN | File::FLAG_WRITE, &proxy);
+  proxy.SetLength(
+      7, BindOnce(&FileProxyTest::DidFinish, weak_factory_.GetWeakPtr()));
+  RunLoop().Run();
+
+  // Verify.
+  GetFileInfo(TestPath(), &info);
+  ASSERT_EQ(7, info.size);
+
+  char buffer[7];
+  EXPECT_EQ(7, base::ReadFile(TestPath(), buffer, 7));
+  int i = 0;
+  for (; i < 7; ++i)
+    EXPECT_EQ(kTestData[i], buffer[i]);
+}
+
+TEST_F(FileProxyTest, SetLength_Expand) {
+  // Setup.
+  const char kTestData[] = "9876543210";
+  ASSERT_EQ(10, base::WriteFile(TestPath(), kTestData, 10));
+  File::Info info;
+  GetFileInfo(TestPath(), &info);
+  ASSERT_EQ(10, info.size);
+
+  // Run.
+  FileProxy proxy(file_task_runner());
+  CreateProxy(File::FLAG_OPEN | File::FLAG_WRITE, &proxy);
+  proxy.SetLength(
+      53, BindOnce(&FileProxyTest::DidFinish, weak_factory_.GetWeakPtr()));
+  RunLoop().Run();
+
+  // Verify.
+  GetFileInfo(TestPath(), &info);
+  ASSERT_EQ(53, info.size);
+
+  char buffer[53];
+  EXPECT_EQ(53, base::ReadFile(TestPath(), buffer, 53));
+  int i = 0;
+  for (; i < 10; ++i)
+    EXPECT_EQ(kTestData[i], buffer[i]);
+  for (; i < 53; ++i)
+    EXPECT_EQ(0, buffer[i]);
+}
+
+}  // namespace base
diff --git a/base/files/file_util_android.cc b/base/files/file_util_android.cc
new file mode 100644
index 0000000..b8b3b37
--- /dev/null
+++ b/base/files/file_util_android.cc
@@ -0,0 +1,16 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/files/file_util.h"
+
+#include "base/files/file_path.h"
+#include "base/path_service.h"
+
+namespace base {
+
+bool GetShmemTempDir(bool executable, base::FilePath* path) {
+  return PathService::Get(base::DIR_CACHE, path);
+}
+
+}  // namespace base
diff --git a/base/files/file_util_unittest.cc b/base/files/file_util_unittest.cc
new file mode 100644
index 0000000..68abc7c
--- /dev/null
+++ b/base/files/file_util_unittest.cc
@@ -0,0 +1,3749 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include <algorithm>
+#include <fstream>
+#include <initializer_list>
+#include <memory>
+#include <set>
+#include <utility>
+#include <vector>
+
+#include "base/base_paths.h"
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback_helpers.h"
+#include "base/command_line.h"
+#include "base/environment.h"
+#include "base/files/file.h"
+#include "base/files/file_enumerator.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_file.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/guid.h"
+#include "base/macros.h"
+#include "base/path_service.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/multiprocess_test.h"
+#include "base/test/scoped_environment_variable_override.h"
+#include "base/test/test_file_util.h"
+#include "base/test/test_timeouts.h"
+#include "base/threading/platform_thread.h"
+#include "base/threading/thread.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/multiprocess_func_list.h"
+#include "testing/platform_test.h"
+
+#if defined(OS_WIN)
+#include <shellapi.h>
+#include <shlobj.h>
+#include <tchar.h>
+#include <windows.h>
+#include <winioctl.h>
+#include "base/strings/string_number_conversions.h"
+#include "base/win/scoped_handle.h"
+#include "base/win/win_util.h"
+#endif
+
+#if defined(OS_POSIX) || defined(OS_FUCHSIA)
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#endif
+
+#if defined(OS_LINUX)
+#include <linux/fs.h>
+#endif
+
+#if defined(OS_ANDROID)
+#include "base/android/content_uri_utils.h"
+#endif
+
+// This macro helps avoid wrapped lines in the test structs.
+#define FPL(x) FILE_PATH_LITERAL(x)
+
+namespace base {
+
+namespace {
+
+const size_t kLargeFileSize = (1 << 16) + 3;
+
+// To test that NormalizeFilePath() deals with NTFS reparse points correctly,
+// we need functions to create and delete reparse points.
+#if defined(OS_WIN)
+typedef struct _REPARSE_DATA_BUFFER {
+  ULONG  ReparseTag;
+  USHORT  ReparseDataLength;
+  USHORT  Reserved;
+  union {
+    struct {
+      USHORT SubstituteNameOffset;
+      USHORT SubstituteNameLength;
+      USHORT PrintNameOffset;
+      USHORT PrintNameLength;
+      ULONG Flags;
+      WCHAR PathBuffer[1];
+    } SymbolicLinkReparseBuffer;
+    struct {
+      USHORT SubstituteNameOffset;
+      USHORT SubstituteNameLength;
+      USHORT PrintNameOffset;
+      USHORT PrintNameLength;
+      WCHAR PathBuffer[1];
+    } MountPointReparseBuffer;
+    struct {
+      UCHAR DataBuffer[1];
+    } GenericReparseBuffer;
+  };
+} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
+
+// Sets a reparse point. |source| will now point to |target|. Returns true if
+// the call succeeds, false otherwise.
+bool SetReparsePoint(HANDLE source, const FilePath& target_path) {
+  std::wstring kPathPrefix = L"\\??\\";
+  std::wstring target_str;
+  // The juction will not work if the target path does not start with \??\ .
+  if (kPathPrefix != target_path.value().substr(0, kPathPrefix.size()))
+    target_str += kPathPrefix;
+  target_str += target_path.value();
+  const wchar_t* target = target_str.c_str();
+  USHORT size_target = static_cast<USHORT>(wcslen(target)) * sizeof(target[0]);
+  char buffer[2000] = {0};
+  DWORD returned;
+
+  REPARSE_DATA_BUFFER* data = reinterpret_cast<REPARSE_DATA_BUFFER*>(buffer);
+
+  data->ReparseTag = 0xa0000003;
+  memcpy(data->MountPointReparseBuffer.PathBuffer, target, size_target + 2);
+
+  data->MountPointReparseBuffer.SubstituteNameLength = size_target;
+  data->MountPointReparseBuffer.PrintNameOffset = size_target + 2;
+  data->ReparseDataLength = size_target + 4 + 8;
+
+  int data_size = data->ReparseDataLength + 8;
+
+  if (!DeviceIoControl(source, FSCTL_SET_REPARSE_POINT, &buffer, data_size,
+                       NULL, 0, &returned, NULL)) {
+    return false;
+  }
+  return true;
+}
+
+// Delete the reparse point referenced by |source|. Returns true if the call
+// succeeds, false otherwise.
+bool DeleteReparsePoint(HANDLE source) {
+  DWORD returned;
+  REPARSE_DATA_BUFFER data = {0};
+  data.ReparseTag = 0xa0000003;
+  if (!DeviceIoControl(source, FSCTL_DELETE_REPARSE_POINT, &data, 8, NULL, 0,
+                       &returned, NULL)) {
+    return false;
+  }
+  return true;
+}
+
+// Manages a reparse point for a test.
+class ReparsePoint {
+ public:
+  // Creates a reparse point from |source| (an empty directory) to |target|.
+  ReparsePoint(const FilePath& source, const FilePath& target) {
+    dir_.Set(
+      ::CreateFile(source.value().c_str(),
+                   GENERIC_READ | GENERIC_WRITE,
+                   FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                   NULL,
+                   OPEN_EXISTING,
+                   FILE_FLAG_BACKUP_SEMANTICS,  // Needed to open a directory.
+                   NULL));
+    created_ = dir_.IsValid() && SetReparsePoint(dir_.Get(), target);
+  }
+
+  ~ReparsePoint() {
+    if (created_)
+      DeleteReparsePoint(dir_.Get());
+  }
+
+  bool IsValid() { return created_; }
+
+ private:
+  win::ScopedHandle dir_;
+  bool created_;
+  DISALLOW_COPY_AND_ASSIGN(ReparsePoint);
+};
+
+#endif
+
+// Fuchsia doesn't support file permissions.
+#if !defined(OS_FUCHSIA)
+#if defined(OS_POSIX)
+// Provide a simple way to change the permissions bits on |path| in tests.
+// ASSERT failures will return, but not stop the test.  Caller should wrap
+// calls to this function in ASSERT_NO_FATAL_FAILURE().
+void ChangePosixFilePermissions(const FilePath& path,
+                                int mode_bits_to_set,
+                                int mode_bits_to_clear) {
+  ASSERT_FALSE(mode_bits_to_set & mode_bits_to_clear)
+      << "Can't set and clear the same bits.";
+
+  int mode = 0;
+  ASSERT_TRUE(GetPosixFilePermissions(path, &mode));
+  mode |= mode_bits_to_set;
+  mode &= ~mode_bits_to_clear;
+  ASSERT_TRUE(SetPosixFilePermissions(path, mode));
+}
+#endif  // defined(OS_POSIX)
+
+// Sets the source file to read-only.
+void SetReadOnly(const FilePath& path, bool read_only) {
+#if defined(OS_WIN)
+  // On Windows, it involves setting/removing the 'readonly' bit.
+  DWORD attrs = GetFileAttributes(path.value().c_str());
+  ASSERT_NE(INVALID_FILE_ATTRIBUTES, attrs);
+  ASSERT_TRUE(SetFileAttributes(
+      path.value().c_str(), read_only ? (attrs | FILE_ATTRIBUTE_READONLY)
+                                      : (attrs & ~FILE_ATTRIBUTE_READONLY)));
+
+  DWORD expected =
+      read_only
+          ? ((attrs & (FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_DIRECTORY)) |
+             FILE_ATTRIBUTE_READONLY)
+          : (attrs & (FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_DIRECTORY));
+
+  // Ignore FILE_ATTRIBUTE_NOT_CONTENT_INDEXED if present.
+  attrs = GetFileAttributes(path.value().c_str()) &
+          ~FILE_ATTRIBUTE_NOT_CONTENT_INDEXED;
+  ASSERT_EQ(expected, attrs);
+#else
+  // On all other platforms, it involves removing/setting the write bit.
+  mode_t mode = read_only ? S_IRUSR : (S_IRUSR | S_IWUSR);
+  EXPECT_TRUE(SetPosixFilePermissions(
+      path, DirectoryExists(path) ? (mode | S_IXUSR) : mode));
+#endif  // defined(OS_WIN)
+}
+
+bool IsReadOnly(const FilePath& path) {
+#if defined(OS_WIN)
+  DWORD attrs = GetFileAttributes(path.value().c_str());
+  EXPECT_NE(INVALID_FILE_ATTRIBUTES, attrs);
+  return attrs & FILE_ATTRIBUTE_READONLY;
+#else
+  int mode = 0;
+  EXPECT_TRUE(GetPosixFilePermissions(path, &mode));
+  return !(mode & S_IWUSR);
+#endif  // defined(OS_WIN)
+}
+
+#endif  // defined(OS_FUCHSIA)
+
+const wchar_t bogus_content[] = L"I'm cannon fodder.";
+
+const int FILES_AND_DIRECTORIES =
+    FileEnumerator::FILES | FileEnumerator::DIRECTORIES;
+
+// file_util winds up using autoreleased objects on the Mac, so this needs
+// to be a PlatformTest
+class FileUtilTest : public PlatformTest {
+ protected:
+  void SetUp() override {
+    PlatformTest::SetUp();
+    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+  }
+
+  ScopedTempDir temp_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:
+  explicit FindResultCollector(FileEnumerator* enumerator) {
+    FilePath cur_file;
+    while (!(cur_file = enumerator->Next()).value().empty()) {
+      FilePath::StringType path = cur_file.value();
+      // The file should not be returned twice.
+      EXPECT_TRUE(files_.end() == files_.find(path))
+          << "Same file returned twice";
+
+      // Save for later.
+      files_.insert(path);
+    }
+  }
+
+  // Returns true if the enumerator found the file.
+  bool HasFile(const FilePath& file) const {
+    return files_.find(file.value()) != files_.end();
+  }
+
+  int size() {
+    return static_cast<int>(files_.size());
+  }
+
+ private:
+  std::set<FilePath::StringType> files_;
+};
+
+// Simple function to dump some text into a new file.
+void CreateTextFile(const FilePath& filename,
+                    const std::wstring& contents) {
+  std::wofstream file;
+  file.open(filename.value().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 FilePath& filename) {
+  wchar_t contents[64];
+  std::wifstream file;
+  file.open(filename.value().c_str());
+  EXPECT_TRUE(file.is_open());
+  file.getline(contents, arraysize(contents));
+  file.close();
+  return std::wstring(contents);
+}
+
+// Sets |is_inheritable| to indicate whether or not |stream| is set up to be
+// inerhited into child processes (i.e., HANDLE_FLAG_INHERIT is set on the
+// underlying handle on Windows, or FD_CLOEXEC is not set on the underlying file
+// descriptor on POSIX). Calls to this function must be wrapped with
+// ASSERT_NO_FATAL_FAILURE to properly abort tests in case of fatal failure.
+void GetIsInheritable(FILE* stream, bool* is_inheritable) {
+#if defined(OS_WIN)
+  HANDLE handle = reinterpret_cast<HANDLE>(_get_osfhandle(_fileno(stream)));
+  ASSERT_NE(INVALID_HANDLE_VALUE, handle);
+
+  DWORD info = 0;
+  ASSERT_EQ(TRUE, ::GetHandleInformation(handle, &info));
+  *is_inheritable = ((info & HANDLE_FLAG_INHERIT) != 0);
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+  int fd = fileno(stream);
+  ASSERT_NE(-1, fd);
+  int flags = fcntl(fd, F_GETFD, 0);
+  ASSERT_NE(-1, flags);
+  *is_inheritable = ((flags & FD_CLOEXEC) == 0);
+#else
+#error Not implemented
+#endif
+}
+
+TEST_F(FileUtilTest, FileAndDirectorySize) {
+  // Create three files of 20, 30 and 3 chars (utf8). ComputeDirectorySize
+  // should return 53 bytes.
+  FilePath file_01 = temp_dir_.GetPath().Append(FPL("The file 01.txt"));
+  CreateTextFile(file_01, L"12345678901234567890");
+  int64_t size_f1 = 0;
+  ASSERT_TRUE(GetFileSize(file_01, &size_f1));
+  EXPECT_EQ(20ll, size_f1);
+
+  FilePath subdir_path = temp_dir_.GetPath().Append(FPL("Level2"));
+  CreateDirectory(subdir_path);
+
+  FilePath file_02 = subdir_path.Append(FPL("The file 02.txt"));
+  CreateTextFile(file_02, L"123456789012345678901234567890");
+  int64_t size_f2 = 0;
+  ASSERT_TRUE(GetFileSize(file_02, &size_f2));
+  EXPECT_EQ(30ll, size_f2);
+
+  FilePath subsubdir_path = subdir_path.Append(FPL("Level3"));
+  CreateDirectory(subsubdir_path);
+
+  FilePath file_03 = subsubdir_path.Append(FPL("The file 03.txt"));
+  CreateTextFile(file_03, L"123");
+
+  int64_t computed_size = ComputeDirectorySize(temp_dir_.GetPath());
+  EXPECT_EQ(size_f1 + size_f2 + 3, computed_size);
+}
+
+TEST_F(FileUtilTest, NormalizeFilePathBasic) {
+  // Create a directory under the test dir.  Because we create it,
+  // we know it is not a link.
+  FilePath file_a_path = temp_dir_.GetPath().Append(FPL("file_a"));
+  FilePath dir_path = temp_dir_.GetPath().Append(FPL("dir"));
+  FilePath file_b_path = dir_path.Append(FPL("file_b"));
+  CreateDirectory(dir_path);
+
+  FilePath normalized_file_a_path, normalized_file_b_path;
+  ASSERT_FALSE(PathExists(file_a_path));
+  ASSERT_FALSE(NormalizeFilePath(file_a_path, &normalized_file_a_path))
+    << "NormalizeFilePath() should fail on nonexistent paths.";
+
+  CreateTextFile(file_a_path, bogus_content);
+  ASSERT_TRUE(PathExists(file_a_path));
+  ASSERT_TRUE(NormalizeFilePath(file_a_path, &normalized_file_a_path));
+
+  CreateTextFile(file_b_path, bogus_content);
+  ASSERT_TRUE(PathExists(file_b_path));
+  ASSERT_TRUE(NormalizeFilePath(file_b_path, &normalized_file_b_path));
+
+  // Beacuse this test created |dir_path|, we know it is not a link
+  // or junction.  So, the real path of the directory holding file a
+  // must be the parent of the path holding file b.
+  ASSERT_TRUE(normalized_file_a_path.DirName()
+      .IsParent(normalized_file_b_path.DirName()));
+}
+
+#if defined(OS_WIN)
+
+TEST_F(FileUtilTest, NormalizeFilePathReparsePoints) {
+  // Build the following directory structure:
+  //
+  // temp_dir
+  // |-> base_a
+  // |   |-> sub_a
+  // |       |-> file.txt
+  // |       |-> long_name___... (Very long name.)
+  // |           |-> sub_long
+  // |              |-> deep.txt
+  // |-> base_b
+  //     |-> to_sub_a (reparse point to temp_dir\base_a\sub_a)
+  //     |-> to_base_b (reparse point to temp_dir\base_b)
+  //     |-> to_sub_long (reparse point to temp_dir\sub_a\long_name_\sub_long)
+
+  FilePath base_a = temp_dir_.GetPath().Append(FPL("base_a"));
+#if defined(OS_WIN)
+  // TEMP can have a lower case drive letter.
+  string16 temp_base_a = base_a.value();
+  ASSERT_FALSE(temp_base_a.empty());
+  *temp_base_a.begin() = ToUpperASCII(*temp_base_a.begin());
+  base_a = FilePath(temp_base_a);
+#endif
+  ASSERT_TRUE(CreateDirectory(base_a));
+
+  FilePath sub_a = base_a.Append(FPL("sub_a"));
+  ASSERT_TRUE(CreateDirectory(sub_a));
+
+  FilePath file_txt = sub_a.Append(FPL("file.txt"));
+  CreateTextFile(file_txt, bogus_content);
+
+  // Want a directory whose name is long enough to make the path to the file
+  // inside just under MAX_PATH chars.  This will be used to test that when
+  // a junction expands to a path over MAX_PATH chars in length,
+  // NormalizeFilePath() fails without crashing.
+  FilePath sub_long_rel(FPL("sub_long"));
+  FilePath deep_txt(FPL("deep.txt"));
+
+  int target_length = MAX_PATH;
+  target_length -= (sub_a.value().length() + 1);  // +1 for the sepperator '\'.
+  target_length -= (sub_long_rel.Append(deep_txt).value().length() + 1);
+  // Without making the path a bit shorter, CreateDirectory() fails.
+  // the resulting path is still long enough to hit the failing case in
+  // NormalizePath().
+  const int kCreateDirLimit = 4;
+  target_length -= kCreateDirLimit;
+  FilePath::StringType long_name_str = FPL("long_name_");
+  long_name_str.resize(target_length, '_');
+
+  FilePath long_name = sub_a.Append(FilePath(long_name_str));
+  FilePath deep_file = long_name.Append(sub_long_rel).Append(deep_txt);
+  ASSERT_EQ(static_cast<size_t>(MAX_PATH - kCreateDirLimit),
+            deep_file.value().length());
+
+  FilePath sub_long = deep_file.DirName();
+  ASSERT_TRUE(CreateDirectory(sub_long));
+  CreateTextFile(deep_file, bogus_content);
+
+  FilePath base_b = temp_dir_.GetPath().Append(FPL("base_b"));
+  ASSERT_TRUE(CreateDirectory(base_b));
+
+  FilePath to_sub_a = base_b.Append(FPL("to_sub_a"));
+  ASSERT_TRUE(CreateDirectory(to_sub_a));
+  FilePath normalized_path;
+  {
+    ReparsePoint reparse_to_sub_a(to_sub_a, sub_a);
+    ASSERT_TRUE(reparse_to_sub_a.IsValid());
+
+    FilePath to_base_b = base_b.Append(FPL("to_base_b"));
+    ASSERT_TRUE(CreateDirectory(to_base_b));
+    ReparsePoint reparse_to_base_b(to_base_b, base_b);
+    ASSERT_TRUE(reparse_to_base_b.IsValid());
+
+    FilePath to_sub_long = base_b.Append(FPL("to_sub_long"));
+    ASSERT_TRUE(CreateDirectory(to_sub_long));
+    ReparsePoint reparse_to_sub_long(to_sub_long, sub_long);
+    ASSERT_TRUE(reparse_to_sub_long.IsValid());
+
+    // Normalize a junction free path: base_a\sub_a\file.txt .
+    ASSERT_TRUE(NormalizeFilePath(file_txt, &normalized_path));
+    ASSERT_STREQ(file_txt.value().c_str(), normalized_path.value().c_str());
+
+    // Check that the path base_b\to_sub_a\file.txt can be normalized to exclude
+    // the junction to_sub_a.
+    ASSERT_TRUE(NormalizeFilePath(to_sub_a.Append(FPL("file.txt")),
+                                             &normalized_path));
+    ASSERT_STREQ(file_txt.value().c_str(), normalized_path.value().c_str());
+
+    // Check that the path base_b\to_base_b\to_base_b\to_sub_a\file.txt can be
+    // normalized to exclude junctions to_base_b and to_sub_a .
+    ASSERT_TRUE(NormalizeFilePath(base_b.Append(FPL("to_base_b"))
+                                                   .Append(FPL("to_base_b"))
+                                                   .Append(FPL("to_sub_a"))
+                                                   .Append(FPL("file.txt")),
+                                             &normalized_path));
+    ASSERT_STREQ(file_txt.value().c_str(), normalized_path.value().c_str());
+
+    // A long enough path will cause NormalizeFilePath() to fail.  Make a long
+    // path using to_base_b many times, and check that paths long enough to fail
+    // do not cause a crash.
+    FilePath long_path = base_b;
+    const int kLengthLimit = MAX_PATH + 200;
+    while (long_path.value().length() <= kLengthLimit) {
+      long_path = long_path.Append(FPL("to_base_b"));
+    }
+    long_path = long_path.Append(FPL("to_sub_a"))
+                         .Append(FPL("file.txt"));
+
+    ASSERT_FALSE(NormalizeFilePath(long_path, &normalized_path));
+
+    // Normalizing the junction to deep.txt should fail, because the expanded
+    // path to deep.txt is longer than MAX_PATH.
+    ASSERT_FALSE(NormalizeFilePath(to_sub_long.Append(deep_txt),
+                                              &normalized_path));
+
+    // Delete the reparse points, and see that NormalizeFilePath() fails
+    // to traverse them.
+  }
+
+  ASSERT_FALSE(NormalizeFilePath(to_sub_a.Append(FPL("file.txt")),
+                                            &normalized_path));
+}
+
+TEST_F(FileUtilTest, DevicePathToDriveLetter) {
+  // Get a drive letter.
+  string16 real_drive_letter =
+      ToUpperASCII(temp_dir_.GetPath().value().substr(0, 2));
+  if (!isalpha(real_drive_letter[0]) || ':' != real_drive_letter[1]) {
+    LOG(ERROR) << "Can't get a drive letter to test with.";
+    return;
+  }
+
+  // Get the NT style path to that drive.
+  wchar_t device_path[MAX_PATH] = {'\0'};
+  ASSERT_TRUE(
+      ::QueryDosDevice(real_drive_letter.c_str(), device_path, MAX_PATH));
+  FilePath actual_device_path(device_path);
+  FilePath win32_path;
+
+  // Run DevicePathToDriveLetterPath() on the NT style path we got from
+  // QueryDosDevice().  Expect the drive letter we started with.
+  ASSERT_TRUE(DevicePathToDriveLetterPath(actual_device_path, &win32_path));
+  ASSERT_EQ(real_drive_letter, win32_path.value());
+
+  // Add some directories to the path.  Expect those extra path componenets
+  // to be preserved.
+  FilePath kRelativePath(FPL("dir1\\dir2\\file.txt"));
+  ASSERT_TRUE(DevicePathToDriveLetterPath(
+      actual_device_path.Append(kRelativePath),
+      &win32_path));
+  EXPECT_EQ(FilePath(real_drive_letter + L"\\").Append(kRelativePath).value(),
+            win32_path.value());
+
+  // Deform the real path so that it is invalid by removing the last four
+  // characters.  The way windows names devices that are hard disks
+  // (\Device\HardDiskVolume${NUMBER}) guarantees that the string is longer
+  // than three characters.  The only way the truncated string could be a
+  // real drive is if more than 10^3 disks are mounted:
+  // \Device\HardDiskVolume10000 would be truncated to \Device\HardDiskVolume1
+  // Check that DevicePathToDriveLetterPath fails.
+  int path_length = actual_device_path.value().length();
+  int new_length = path_length - 4;
+  ASSERT_LT(0, new_length);
+  FilePath prefix_of_real_device_path(
+      actual_device_path.value().substr(0, new_length));
+  ASSERT_FALSE(DevicePathToDriveLetterPath(prefix_of_real_device_path,
+                                           &win32_path));
+
+  ASSERT_FALSE(DevicePathToDriveLetterPath(
+      prefix_of_real_device_path.Append(kRelativePath),
+      &win32_path));
+
+  // Deform the real path so that it is invalid by adding some characters. For
+  // example, if C: maps to \Device\HardDiskVolume8, then we simulate a
+  // request for the drive letter whose native path is
+  // \Device\HardDiskVolume812345 .  We assume such a device does not exist,
+  // because drives are numbered in order and mounting 112345 hard disks will
+  // never happen.
+  const FilePath::StringType kExtraChars = FPL("12345");
+
+  FilePath real_device_path_plus_numbers(
+      actual_device_path.value() + kExtraChars);
+
+  ASSERT_FALSE(DevicePathToDriveLetterPath(
+      real_device_path_plus_numbers,
+      &win32_path));
+
+  ASSERT_FALSE(DevicePathToDriveLetterPath(
+      real_device_path_plus_numbers.Append(kRelativePath),
+      &win32_path));
+}
+
+TEST_F(FileUtilTest, CreateTemporaryFileInDirLongPathTest) {
+  // Test that CreateTemporaryFileInDir() creates a path and returns a long path
+  // if it is available. This test requires that:
+  // - the filesystem at |temp_dir_| supports long filenames.
+  // - the account has FILE_LIST_DIRECTORY permission for all ancestor
+  //   directories of |temp_dir_|.
+  const FilePath::CharType kLongDirName[] = FPL("A long path");
+  const FilePath::CharType kTestSubDirName[] = FPL("test");
+  FilePath long_test_dir = temp_dir_.GetPath().Append(kLongDirName);
+  ASSERT_TRUE(CreateDirectory(long_test_dir));
+
+  // kLongDirName is not a 8.3 component. So GetShortName() should give us a
+  // different short name.
+  WCHAR path_buffer[MAX_PATH];
+  DWORD path_buffer_length = GetShortPathName(long_test_dir.value().c_str(),
+                                              path_buffer, MAX_PATH);
+  ASSERT_LT(path_buffer_length, DWORD(MAX_PATH));
+  ASSERT_NE(DWORD(0), path_buffer_length);
+  FilePath short_test_dir(path_buffer);
+  ASSERT_STRNE(kLongDirName, short_test_dir.BaseName().value().c_str());
+
+  FilePath temp_file;
+  ASSERT_TRUE(CreateTemporaryFileInDir(short_test_dir, &temp_file));
+  EXPECT_STREQ(kLongDirName, temp_file.DirName().BaseName().value().c_str());
+  EXPECT_TRUE(PathExists(temp_file));
+
+  // Create a subdirectory of |long_test_dir| and make |long_test_dir|
+  // unreadable. We should still be able to create a temp file in the
+  // subdirectory, but we won't be able to determine the long path for it. This
+  // mimics the environment that some users run where their user profiles reside
+  // in a location where the don't have full access to the higher level
+  // directories. (Note that this assumption is true for NTFS, but not for some
+  // network file systems. E.g. AFS).
+  FilePath access_test_dir = long_test_dir.Append(kTestSubDirName);
+  ASSERT_TRUE(CreateDirectory(access_test_dir));
+  FilePermissionRestorer long_test_dir_restorer(long_test_dir);
+  ASSERT_TRUE(MakeFileUnreadable(long_test_dir));
+
+  // Use the short form of the directory to create a temporary filename.
+  ASSERT_TRUE(CreateTemporaryFileInDir(
+      short_test_dir.Append(kTestSubDirName), &temp_file));
+  EXPECT_TRUE(PathExists(temp_file));
+  EXPECT_TRUE(short_test_dir.IsParent(temp_file.DirName()));
+
+  // Check that the long path can't be determined for |temp_file|.
+  path_buffer_length = GetLongPathName(temp_file.value().c_str(),
+                                       path_buffer, MAX_PATH);
+  EXPECT_EQ(DWORD(0), path_buffer_length);
+}
+
+#endif  // defined(OS_WIN)
+
+#if defined(OS_POSIX)
+
+TEST_F(FileUtilTest, CreateAndReadSymlinks) {
+  FilePath link_from = temp_dir_.GetPath().Append(FPL("from_file"));
+  FilePath link_to = temp_dir_.GetPath().Append(FPL("to_file"));
+  CreateTextFile(link_to, bogus_content);
+
+  ASSERT_TRUE(CreateSymbolicLink(link_to, link_from))
+    << "Failed to create file symlink.";
+
+  // If we created the link properly, we should be able to read the contents
+  // through it.
+  EXPECT_EQ(bogus_content, ReadTextFile(link_from));
+
+  FilePath result;
+  ASSERT_TRUE(ReadSymbolicLink(link_from, &result));
+  EXPECT_EQ(link_to.value(), result.value());
+
+  // Link to a directory.
+  link_from = temp_dir_.GetPath().Append(FPL("from_dir"));
+  link_to = temp_dir_.GetPath().Append(FPL("to_dir"));
+  ASSERT_TRUE(CreateDirectory(link_to));
+  ASSERT_TRUE(CreateSymbolicLink(link_to, link_from))
+    << "Failed to create directory symlink.";
+
+  // Test failures.
+  EXPECT_FALSE(CreateSymbolicLink(link_to, link_to));
+  EXPECT_FALSE(ReadSymbolicLink(link_to, &result));
+  FilePath missing = temp_dir_.GetPath().Append(FPL("missing"));
+  EXPECT_FALSE(ReadSymbolicLink(missing, &result));
+}
+
+// The following test of NormalizeFilePath() require that we create a symlink.
+// This can not be done on Windows before Vista.  On Vista, creating a symlink
+// requires privilege "SeCreateSymbolicLinkPrivilege".
+// TODO(skerner): Investigate the possibility of giving base_unittests the
+// privileges required to create a symlink.
+TEST_F(FileUtilTest, NormalizeFilePathSymlinks) {
+  // Link one file to another.
+  FilePath link_from = temp_dir_.GetPath().Append(FPL("from_file"));
+  FilePath link_to = temp_dir_.GetPath().Append(FPL("to_file"));
+  CreateTextFile(link_to, bogus_content);
+
+  ASSERT_TRUE(CreateSymbolicLink(link_to, link_from))
+    << "Failed to create file symlink.";
+
+  // Check that NormalizeFilePath sees the link.
+  FilePath normalized_path;
+  ASSERT_TRUE(NormalizeFilePath(link_from, &normalized_path));
+  EXPECT_NE(link_from, link_to);
+  EXPECT_EQ(link_to.BaseName().value(), normalized_path.BaseName().value());
+  EXPECT_EQ(link_to.BaseName().value(), normalized_path.BaseName().value());
+
+  // Link to a directory.
+  link_from = temp_dir_.GetPath().Append(FPL("from_dir"));
+  link_to = temp_dir_.GetPath().Append(FPL("to_dir"));
+  ASSERT_TRUE(CreateDirectory(link_to));
+  ASSERT_TRUE(CreateSymbolicLink(link_to, link_from))
+    << "Failed to create directory symlink.";
+
+  EXPECT_FALSE(NormalizeFilePath(link_from, &normalized_path))
+    << "Links to directories should return false.";
+
+  // Test that a loop in the links causes NormalizeFilePath() to return false.
+  link_from = temp_dir_.GetPath().Append(FPL("link_a"));
+  link_to = temp_dir_.GetPath().Append(FPL("link_b"));
+  ASSERT_TRUE(CreateSymbolicLink(link_to, link_from))
+    << "Failed to create loop symlink a.";
+  ASSERT_TRUE(CreateSymbolicLink(link_from, link_to))
+    << "Failed to create loop symlink b.";
+
+  // Infinite loop!
+  EXPECT_FALSE(NormalizeFilePath(link_from, &normalized_path));
+}
+
+TEST_F(FileUtilTest, DeleteSymlinkToExistentFile) {
+  // Create a file.
+  FilePath file_name = temp_dir_.GetPath().Append(FPL("Test DeleteFile 2.txt"));
+  CreateTextFile(file_name, bogus_content);
+  ASSERT_TRUE(PathExists(file_name));
+
+  // Create a symlink to the file.
+  FilePath file_link = temp_dir_.GetPath().Append("file_link_2");
+  ASSERT_TRUE(CreateSymbolicLink(file_name, file_link))
+      << "Failed to create symlink.";
+
+  // Delete the symbolic link.
+  EXPECT_TRUE(DeleteFile(file_link, false));
+
+  // Make sure original file is not deleted.
+  EXPECT_FALSE(PathExists(file_link));
+  EXPECT_TRUE(PathExists(file_name));
+}
+
+TEST_F(FileUtilTest, DeleteSymlinkToNonExistentFile) {
+  // Create a non-existent file path.
+  FilePath non_existent =
+      temp_dir_.GetPath().Append(FPL("Test DeleteFile 3.txt"));
+  EXPECT_FALSE(PathExists(non_existent));
+
+  // Create a symlink to the non-existent file.
+  FilePath file_link = temp_dir_.GetPath().Append("file_link_3");
+  ASSERT_TRUE(CreateSymbolicLink(non_existent, file_link))
+      << "Failed to create symlink.";
+
+  // Make sure the symbolic link is exist.
+  EXPECT_TRUE(IsLink(file_link));
+  EXPECT_FALSE(PathExists(file_link));
+
+  // Delete the symbolic link.
+  EXPECT_TRUE(DeleteFile(file_link, false));
+
+  // Make sure the symbolic link is deleted.
+  EXPECT_FALSE(IsLink(file_link));
+}
+
+TEST_F(FileUtilTest, CopyFileFollowsSymlinks) {
+  FilePath link_from = temp_dir_.GetPath().Append(FPL("from_file"));
+  FilePath link_to = temp_dir_.GetPath().Append(FPL("to_file"));
+  CreateTextFile(link_to, bogus_content);
+
+  ASSERT_TRUE(CreateSymbolicLink(link_to, link_from));
+
+  // If we created the link properly, we should be able to read the contents
+  // through it.
+  EXPECT_EQ(bogus_content, ReadTextFile(link_from));
+
+  FilePath result;
+  ASSERT_TRUE(ReadSymbolicLink(link_from, &result));
+  EXPECT_EQ(link_to.value(), result.value());
+
+  // Create another file and copy it to |link_from|.
+  FilePath src_file = temp_dir_.GetPath().Append(FPL("src.txt"));
+  const std::wstring file_contents(L"Gooooooooooooooooooooogle");
+  CreateTextFile(src_file, file_contents);
+  ASSERT_TRUE(CopyFile(src_file, link_from));
+
+  // Make sure |link_from| is still a symlink, and |link_to| has been written to
+  // by CopyFile().
+  EXPECT_TRUE(IsLink(link_from));
+  EXPECT_EQ(file_contents, ReadTextFile(link_from));
+  EXPECT_EQ(file_contents, ReadTextFile(link_to));
+}
+
+TEST_F(FileUtilTest, ChangeFilePermissionsAndRead) {
+  // Create a file path.
+  FilePath file_name =
+      temp_dir_.GetPath().Append(FPL("Test Readable File.txt"));
+  EXPECT_FALSE(PathExists(file_name));
+
+  static constexpr char kData[] = "hello";
+  static constexpr int kDataSize = sizeof(kData) - 1;
+  char buffer[kDataSize];
+
+  // Write file.
+  EXPECT_EQ(kDataSize, WriteFile(file_name, kData, kDataSize));
+  EXPECT_TRUE(PathExists(file_name));
+
+  // Make sure the file is readable.
+  int32_t mode = 0;
+  EXPECT_TRUE(GetPosixFilePermissions(file_name, &mode));
+  EXPECT_TRUE(mode & FILE_PERMISSION_READ_BY_USER);
+
+  // Get rid of the read permission.
+  EXPECT_TRUE(SetPosixFilePermissions(file_name, 0u));
+  EXPECT_TRUE(GetPosixFilePermissions(file_name, &mode));
+  EXPECT_FALSE(mode & FILE_PERMISSION_READ_BY_USER);
+  // Make sure the file can't be read.
+  EXPECT_EQ(-1, ReadFile(file_name, buffer, kDataSize));
+
+  // Give the read permission.
+  EXPECT_TRUE(SetPosixFilePermissions(file_name, FILE_PERMISSION_READ_BY_USER));
+  EXPECT_TRUE(GetPosixFilePermissions(file_name, &mode));
+  EXPECT_TRUE(mode & FILE_PERMISSION_READ_BY_USER);
+  // Make sure the file can be read.
+  EXPECT_EQ(kDataSize, ReadFile(file_name, buffer, kDataSize));
+
+  // Delete the file.
+  EXPECT_TRUE(DeleteFile(file_name, false));
+  EXPECT_FALSE(PathExists(file_name));
+}
+
+TEST_F(FileUtilTest, ChangeFilePermissionsAndWrite) {
+  // Create a file path.
+  FilePath file_name =
+      temp_dir_.GetPath().Append(FPL("Test Readable File.txt"));
+  EXPECT_FALSE(PathExists(file_name));
+
+  const std::string kData("hello");
+
+  // Write file.
+  EXPECT_EQ(static_cast<int>(kData.length()),
+            WriteFile(file_name, kData.data(), kData.length()));
+  EXPECT_TRUE(PathExists(file_name));
+
+  // Make sure the file is writable.
+  int mode = 0;
+  EXPECT_TRUE(GetPosixFilePermissions(file_name, &mode));
+  EXPECT_TRUE(mode & FILE_PERMISSION_WRITE_BY_USER);
+  EXPECT_TRUE(PathIsWritable(file_name));
+
+  // Get rid of the write permission.
+  EXPECT_TRUE(SetPosixFilePermissions(file_name, 0u));
+  EXPECT_TRUE(GetPosixFilePermissions(file_name, &mode));
+  EXPECT_FALSE(mode & FILE_PERMISSION_WRITE_BY_USER);
+  // Make sure the file can't be write.
+  EXPECT_EQ(-1, WriteFile(file_name, kData.data(), kData.length()));
+  EXPECT_FALSE(PathIsWritable(file_name));
+
+  // Give read permission.
+  EXPECT_TRUE(SetPosixFilePermissions(file_name,
+                                      FILE_PERMISSION_WRITE_BY_USER));
+  EXPECT_TRUE(GetPosixFilePermissions(file_name, &mode));
+  EXPECT_TRUE(mode & FILE_PERMISSION_WRITE_BY_USER);
+  // Make sure the file can be write.
+  EXPECT_EQ(static_cast<int>(kData.length()),
+            WriteFile(file_name, kData.data(), kData.length()));
+  EXPECT_TRUE(PathIsWritable(file_name));
+
+  // Delete the file.
+  EXPECT_TRUE(DeleteFile(file_name, false));
+  EXPECT_FALSE(PathExists(file_name));
+}
+
+TEST_F(FileUtilTest, ChangeDirectoryPermissionsAndEnumerate) {
+  // Create a directory path.
+  FilePath subdir_path = temp_dir_.GetPath().Append(FPL("PermissionTest1"));
+  CreateDirectory(subdir_path);
+  ASSERT_TRUE(PathExists(subdir_path));
+
+  // Create a dummy file to enumerate.
+  FilePath file_name = subdir_path.Append(FPL("Test Readable File.txt"));
+  EXPECT_FALSE(PathExists(file_name));
+  const std::string kData("hello");
+  EXPECT_EQ(static_cast<int>(kData.length()),
+            WriteFile(file_name, kData.data(), kData.length()));
+  EXPECT_TRUE(PathExists(file_name));
+
+  // Make sure the directory has the all permissions.
+  int mode = 0;
+  EXPECT_TRUE(GetPosixFilePermissions(subdir_path, &mode));
+  EXPECT_EQ(FILE_PERMISSION_USER_MASK, mode & FILE_PERMISSION_USER_MASK);
+
+  // Get rid of the permissions from the directory.
+  EXPECT_TRUE(SetPosixFilePermissions(subdir_path, 0u));
+  EXPECT_TRUE(GetPosixFilePermissions(subdir_path, &mode));
+  EXPECT_FALSE(mode & FILE_PERMISSION_USER_MASK);
+
+  // Make sure the file in the directory can't be enumerated.
+  FileEnumerator f1(subdir_path, true, FileEnumerator::FILES);
+  EXPECT_TRUE(PathExists(subdir_path));
+  FindResultCollector c1(&f1);
+  EXPECT_EQ(0, c1.size());
+  EXPECT_FALSE(GetPosixFilePermissions(file_name, &mode));
+
+  // Give the permissions to the directory.
+  EXPECT_TRUE(SetPosixFilePermissions(subdir_path, FILE_PERMISSION_USER_MASK));
+  EXPECT_TRUE(GetPosixFilePermissions(subdir_path, &mode));
+  EXPECT_EQ(FILE_PERMISSION_USER_MASK, mode & FILE_PERMISSION_USER_MASK);
+
+  // Make sure the file in the directory can be enumerated.
+  FileEnumerator f2(subdir_path, true, FileEnumerator::FILES);
+  FindResultCollector c2(&f2);
+  EXPECT_TRUE(c2.HasFile(file_name));
+  EXPECT_EQ(1, c2.size());
+
+  // Delete the file.
+  EXPECT_TRUE(DeleteFile(subdir_path, true));
+  EXPECT_FALSE(PathExists(subdir_path));
+}
+
+TEST_F(FileUtilTest, ExecutableExistsInPath) {
+  // Create two directories that we will put in our PATH
+  const FilePath::CharType kDir1[] = FPL("dir1");
+  const FilePath::CharType kDir2[] = FPL("dir2");
+
+  FilePath dir1 = temp_dir_.GetPath().Append(kDir1);
+  FilePath dir2 = temp_dir_.GetPath().Append(kDir2);
+  ASSERT_TRUE(CreateDirectory(dir1));
+  ASSERT_TRUE(CreateDirectory(dir2));
+
+  test::ScopedEnvironmentVariableOverride scoped_env(
+      "PATH", dir1.value() + ":" + dir2.value());
+  ASSERT_TRUE(scoped_env.IsOverridden());
+
+  const FilePath::CharType kRegularFileName[] = FPL("regular_file");
+  const FilePath::CharType kExeFileName[] = FPL("exe");
+  const FilePath::CharType kDneFileName[] = FPL("does_not_exist");
+
+  const FilePath kExePath = dir1.Append(kExeFileName);
+  const FilePath kRegularFilePath = dir2.Append(kRegularFileName);
+
+  // Write file.
+  const std::string kData("hello");
+  ASSERT_EQ(static_cast<int>(kData.length()),
+            WriteFile(kExePath, kData.data(), kData.length()));
+  ASSERT_TRUE(PathExists(kExePath));
+  ASSERT_EQ(static_cast<int>(kData.length()),
+            WriteFile(kRegularFilePath, kData.data(), kData.length()));
+  ASSERT_TRUE(PathExists(kRegularFilePath));
+
+  ASSERT_TRUE(SetPosixFilePermissions(dir1.Append(kExeFileName),
+                                      FILE_PERMISSION_EXECUTE_BY_USER));
+
+  EXPECT_TRUE(ExecutableExistsInPath(scoped_env.GetEnv(), kExeFileName));
+  EXPECT_FALSE(ExecutableExistsInPath(scoped_env.GetEnv(), kRegularFileName));
+  EXPECT_FALSE(ExecutableExistsInPath(scoped_env.GetEnv(), kDneFileName));
+}
+
+TEST_F(FileUtilTest, CopyDirectoryPermissions) {
+  // Create a directory.
+  FilePath dir_name_from =
+      temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_From_Subdir"));
+  CreateDirectory(dir_name_from);
+  ASSERT_TRUE(PathExists(dir_name_from));
+
+  // Create some regular files under the directory with various permissions.
+  FilePath file_name_from =
+      dir_name_from.Append(FILE_PATH_LITERAL("Reggy-1.txt"));
+  CreateTextFile(file_name_from, L"Mordecai");
+  ASSERT_TRUE(PathExists(file_name_from));
+  ASSERT_TRUE(SetPosixFilePermissions(file_name_from, 0755));
+
+  FilePath file2_name_from =
+      dir_name_from.Append(FILE_PATH_LITERAL("Reggy-2.txt"));
+  CreateTextFile(file2_name_from, L"Rigby");
+  ASSERT_TRUE(PathExists(file2_name_from));
+  ASSERT_TRUE(SetPosixFilePermissions(file2_name_from, 0777));
+
+  FilePath file3_name_from =
+      dir_name_from.Append(FILE_PATH_LITERAL("Reggy-3.txt"));
+  CreateTextFile(file3_name_from, L"Benson");
+  ASSERT_TRUE(PathExists(file3_name_from));
+  ASSERT_TRUE(SetPosixFilePermissions(file3_name_from, 0400));
+
+  // Copy the directory recursively.
+  FilePath dir_name_to =
+      temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_To_Subdir"));
+  FilePath file_name_to =
+      dir_name_to.Append(FILE_PATH_LITERAL("Reggy-1.txt"));
+  FilePath file2_name_to =
+      dir_name_to.Append(FILE_PATH_LITERAL("Reggy-2.txt"));
+  FilePath file3_name_to =
+      dir_name_to.Append(FILE_PATH_LITERAL("Reggy-3.txt"));
+
+  ASSERT_FALSE(PathExists(dir_name_to));
+
+  EXPECT_TRUE(CopyDirectory(dir_name_from, dir_name_to, true));
+  ASSERT_TRUE(PathExists(file_name_to));
+  ASSERT_TRUE(PathExists(file2_name_to));
+  ASSERT_TRUE(PathExists(file3_name_to));
+
+  int mode = 0;
+  int expected_mode;
+  ASSERT_TRUE(GetPosixFilePermissions(file_name_to, &mode));
+#if defined(OS_MACOSX)
+  expected_mode = 0755;
+#elif defined(OS_CHROMEOS)
+  expected_mode = 0644;
+#else
+  expected_mode = 0600;
+#endif
+  EXPECT_EQ(expected_mode, mode);
+
+  ASSERT_TRUE(GetPosixFilePermissions(file2_name_to, &mode));
+#if defined(OS_MACOSX)
+  expected_mode = 0755;
+#elif defined(OS_CHROMEOS)
+  expected_mode = 0644;
+#else
+  expected_mode = 0600;
+#endif
+  EXPECT_EQ(expected_mode, mode);
+
+  ASSERT_TRUE(GetPosixFilePermissions(file3_name_to, &mode));
+#if defined(OS_MACOSX)
+  expected_mode = 0600;
+#elif defined(OS_CHROMEOS)
+  expected_mode = 0644;
+#else
+  expected_mode = 0600;
+#endif
+  EXPECT_EQ(expected_mode, mode);
+}
+
+TEST_F(FileUtilTest, CopyDirectoryPermissionsOverExistingFile) {
+  // Create a directory.
+  FilePath dir_name_from =
+      temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_From_Subdir"));
+  CreateDirectory(dir_name_from);
+  ASSERT_TRUE(PathExists(dir_name_from));
+
+  // Create a file under the directory.
+  FilePath file_name_from =
+      dir_name_from.Append(FILE_PATH_LITERAL("Reggy-1.txt"));
+  CreateTextFile(file_name_from, L"Mordecai");
+  ASSERT_TRUE(PathExists(file_name_from));
+  ASSERT_TRUE(SetPosixFilePermissions(file_name_from, 0644));
+
+  // Create a directory.
+  FilePath dir_name_to =
+      temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_To_Subdir"));
+  CreateDirectory(dir_name_to);
+  ASSERT_TRUE(PathExists(dir_name_to));
+
+  // Create a file under the directory with wider permissions.
+  FilePath file_name_to =
+      dir_name_to.Append(FILE_PATH_LITERAL("Reggy-1.txt"));
+  CreateTextFile(file_name_to, L"Rigby");
+  ASSERT_TRUE(PathExists(file_name_to));
+  ASSERT_TRUE(SetPosixFilePermissions(file_name_to, 0777));
+
+  // Ensure that when we copy the directory, the file contents are copied
+  // but the permissions on the destination are left alone.
+  EXPECT_TRUE(CopyDirectory(dir_name_from, dir_name_to, false));
+  ASSERT_TRUE(PathExists(file_name_to));
+  ASSERT_EQ(L"Mordecai", ReadTextFile(file_name_to));
+
+  int mode = 0;
+  ASSERT_TRUE(GetPosixFilePermissions(file_name_to, &mode));
+  EXPECT_EQ(0777, mode);
+}
+
+TEST_F(FileUtilTest, CopyDirectoryExclDoesNotOverwrite) {
+  // Create source directory.
+  FilePath dir_name_from =
+      temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_From_Subdir"));
+  CreateDirectory(dir_name_from);
+  ASSERT_TRUE(PathExists(dir_name_from));
+
+  // Create a file under the directory.
+  FilePath file_name_from =
+      dir_name_from.Append(FILE_PATH_LITERAL("Reggy-1.txt"));
+  CreateTextFile(file_name_from, L"Mordecai");
+  ASSERT_TRUE(PathExists(file_name_from));
+
+  // Create destination directory.
+  FilePath dir_name_to =
+      temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_To_Subdir"));
+  CreateDirectory(dir_name_to);
+  ASSERT_TRUE(PathExists(dir_name_to));
+
+  // Create a file under the directory with the same name.
+  FilePath file_name_to = dir_name_to.Append(FILE_PATH_LITERAL("Reggy-1.txt"));
+  CreateTextFile(file_name_to, L"Rigby");
+  ASSERT_TRUE(PathExists(file_name_to));
+
+  // Ensure that copying failed and the file was not overwritten.
+  EXPECT_FALSE(CopyDirectoryExcl(dir_name_from, dir_name_to, false));
+  ASSERT_TRUE(PathExists(file_name_to));
+  ASSERT_EQ(L"Rigby", ReadTextFile(file_name_to));
+}
+
+TEST_F(FileUtilTest, CopyDirectoryExclDirectoryOverExistingFile) {
+  // Create source directory.
+  FilePath dir_name_from =
+      temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_From_Subdir"));
+  CreateDirectory(dir_name_from);
+  ASSERT_TRUE(PathExists(dir_name_from));
+
+  // Create a subdirectory.
+  FilePath subdir_name_from = dir_name_from.Append(FILE_PATH_LITERAL("Subsub"));
+  CreateDirectory(subdir_name_from);
+  ASSERT_TRUE(PathExists(subdir_name_from));
+
+  // Create destination directory.
+  FilePath dir_name_to =
+      temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_To_Subdir"));
+  CreateDirectory(dir_name_to);
+  ASSERT_TRUE(PathExists(dir_name_to));
+
+  // Create a regular file under the directory with the same name.
+  FilePath file_name_to = dir_name_to.Append(FILE_PATH_LITERAL("Subsub"));
+  CreateTextFile(file_name_to, L"Rigby");
+  ASSERT_TRUE(PathExists(file_name_to));
+
+  // Ensure that copying failed and the file was not overwritten.
+  EXPECT_FALSE(CopyDirectoryExcl(dir_name_from, dir_name_to, false));
+  ASSERT_TRUE(PathExists(file_name_to));
+  ASSERT_EQ(L"Rigby", ReadTextFile(file_name_to));
+}
+
+TEST_F(FileUtilTest, CopyDirectoryExclDirectoryOverExistingDirectory) {
+  // Create source directory.
+  FilePath dir_name_from =
+      temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_From_Subdir"));
+  CreateDirectory(dir_name_from);
+  ASSERT_TRUE(PathExists(dir_name_from));
+
+  // Create a subdirectory.
+  FilePath subdir_name_from = dir_name_from.Append(FILE_PATH_LITERAL("Subsub"));
+  CreateDirectory(subdir_name_from);
+  ASSERT_TRUE(PathExists(subdir_name_from));
+
+  // Create destination directory.
+  FilePath dir_name_to =
+      temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_To_Subdir"));
+  CreateDirectory(dir_name_to);
+  ASSERT_TRUE(PathExists(dir_name_to));
+
+  // Create a subdirectory under the directory with the same name.
+  FilePath subdir_name_to = dir_name_to.Append(FILE_PATH_LITERAL("Subsub"));
+  CreateDirectory(subdir_name_to);
+  ASSERT_TRUE(PathExists(subdir_name_to));
+
+  // Ensure that copying failed and the file was not overwritten.
+  EXPECT_FALSE(CopyDirectoryExcl(dir_name_from, dir_name_to, false));
+}
+
+TEST_F(FileUtilTest, CopyFileExecutablePermission) {
+  FilePath src = temp_dir_.GetPath().Append(FPL("src.txt"));
+  const std::wstring file_contents(L"Gooooooooooooooooooooogle");
+  CreateTextFile(src, file_contents);
+
+  ASSERT_TRUE(SetPosixFilePermissions(src, 0755));
+  int mode = 0;
+  ASSERT_TRUE(GetPosixFilePermissions(src, &mode));
+  EXPECT_EQ(0755, mode);
+
+  FilePath dst = temp_dir_.GetPath().Append(FPL("dst.txt"));
+  ASSERT_TRUE(CopyFile(src, dst));
+  EXPECT_EQ(file_contents, ReadTextFile(dst));
+
+  ASSERT_TRUE(GetPosixFilePermissions(dst, &mode));
+  int expected_mode;
+#if defined(OS_MACOSX)
+  expected_mode = 0755;
+#elif defined(OS_CHROMEOS)
+  expected_mode = 0644;
+#else
+  expected_mode = 0600;
+#endif
+  EXPECT_EQ(expected_mode, mode);
+  ASSERT_TRUE(DeleteFile(dst, false));
+
+  ASSERT_TRUE(SetPosixFilePermissions(src, 0777));
+  ASSERT_TRUE(GetPosixFilePermissions(src, &mode));
+  EXPECT_EQ(0777, mode);
+
+  ASSERT_TRUE(CopyFile(src, dst));
+  EXPECT_EQ(file_contents, ReadTextFile(dst));
+
+  ASSERT_TRUE(GetPosixFilePermissions(dst, &mode));
+#if defined(OS_MACOSX)
+  expected_mode = 0755;
+#elif defined(OS_CHROMEOS)
+  expected_mode = 0644;
+#else
+  expected_mode = 0600;
+#endif
+  EXPECT_EQ(expected_mode, mode);
+  ASSERT_TRUE(DeleteFile(dst, false));
+
+  ASSERT_TRUE(SetPosixFilePermissions(src, 0400));
+  ASSERT_TRUE(GetPosixFilePermissions(src, &mode));
+  EXPECT_EQ(0400, mode);
+
+  ASSERT_TRUE(CopyFile(src, dst));
+  EXPECT_EQ(file_contents, ReadTextFile(dst));
+
+  ASSERT_TRUE(GetPosixFilePermissions(dst, &mode));
+#if defined(OS_MACOSX)
+  expected_mode = 0600;
+#elif defined(OS_CHROMEOS)
+  expected_mode = 0644;
+#else
+  expected_mode = 0600;
+#endif
+  EXPECT_EQ(expected_mode, mode);
+
+  // This time, do not delete |dst|. Instead set its permissions to 0777.
+  ASSERT_TRUE(SetPosixFilePermissions(dst, 0777));
+  ASSERT_TRUE(GetPosixFilePermissions(dst, &mode));
+  EXPECT_EQ(0777, mode);
+
+  // Overwrite it and check the permissions again.
+  ASSERT_TRUE(CopyFile(src, dst));
+  EXPECT_EQ(file_contents, ReadTextFile(dst));
+  ASSERT_TRUE(GetPosixFilePermissions(dst, &mode));
+  EXPECT_EQ(0777, mode);
+}
+
+#endif  // defined(OS_POSIX)
+
+#if !defined(OS_FUCHSIA)
+
+TEST_F(FileUtilTest, CopyFileACL) {
+  // While FileUtilTest.CopyFile asserts the content is correctly copied over,
+  // this test case asserts the access control bits are meeting expectations in
+  // CopyFile().
+  FilePath src = temp_dir_.GetPath().Append(FILE_PATH_LITERAL("src.txt"));
+  const std::wstring file_contents(L"Gooooooooooooooooooooogle");
+  CreateTextFile(src, file_contents);
+
+  // Set the source file to read-only.
+  ASSERT_FALSE(IsReadOnly(src));
+  SetReadOnly(src, true);
+  ASSERT_TRUE(IsReadOnly(src));
+
+  // Copy the file.
+  FilePath dst = temp_dir_.GetPath().Append(FILE_PATH_LITERAL("dst.txt"));
+  ASSERT_TRUE(CopyFile(src, dst));
+  EXPECT_EQ(file_contents, ReadTextFile(dst));
+
+  ASSERT_FALSE(IsReadOnly(dst));
+}
+
+TEST_F(FileUtilTest, CopyDirectoryACL) {
+  // Create source directories.
+  FilePath src = temp_dir_.GetPath().Append(FILE_PATH_LITERAL("src"));
+  FilePath src_subdir = src.Append(FILE_PATH_LITERAL("subdir"));
+  CreateDirectory(src_subdir);
+  ASSERT_TRUE(PathExists(src_subdir));
+
+  // Create a file under the directory.
+  FilePath src_file = src.Append(FILE_PATH_LITERAL("src.txt"));
+  CreateTextFile(src_file, L"Gooooooooooooooooooooogle");
+  SetReadOnly(src_file, true);
+  ASSERT_TRUE(IsReadOnly(src_file));
+
+  // Make directory read-only.
+  SetReadOnly(src_subdir, true);
+  ASSERT_TRUE(IsReadOnly(src_subdir));
+
+  // Copy the directory recursively.
+  FilePath dst = temp_dir_.GetPath().Append(FILE_PATH_LITERAL("dst"));
+  FilePath dst_file = dst.Append(FILE_PATH_LITERAL("src.txt"));
+  EXPECT_TRUE(CopyDirectory(src, dst, true));
+
+  FilePath dst_subdir = dst.Append(FILE_PATH_LITERAL("subdir"));
+  ASSERT_FALSE(IsReadOnly(dst_subdir));
+  ASSERT_FALSE(IsReadOnly(dst_file));
+
+  // Give write permissions to allow deletion.
+  SetReadOnly(src_subdir, false);
+  ASSERT_FALSE(IsReadOnly(src_subdir));
+}
+
+#endif  // !defined(OS_FUCHSIA)
+
+TEST_F(FileUtilTest, DeleteNonExistent) {
+  FilePath non_existent =
+      temp_dir_.GetPath().AppendASCII("bogus_file_dne.foobar");
+  ASSERT_FALSE(PathExists(non_existent));
+
+  EXPECT_TRUE(DeleteFile(non_existent, false));
+  ASSERT_FALSE(PathExists(non_existent));
+  EXPECT_TRUE(DeleteFile(non_existent, true));
+  ASSERT_FALSE(PathExists(non_existent));
+}
+
+TEST_F(FileUtilTest, DeleteNonExistentWithNonExistentParent) {
+  FilePath non_existent = temp_dir_.GetPath().AppendASCII("bogus_topdir");
+  non_existent = non_existent.AppendASCII("bogus_subdir");
+  ASSERT_FALSE(PathExists(non_existent));
+
+  EXPECT_TRUE(DeleteFile(non_existent, false));
+  ASSERT_FALSE(PathExists(non_existent));
+  EXPECT_TRUE(DeleteFile(non_existent, true));
+  ASSERT_FALSE(PathExists(non_existent));
+}
+
+TEST_F(FileUtilTest, DeleteFile) {
+  // Create a file
+  FilePath file_name = temp_dir_.GetPath().Append(FPL("Test DeleteFile 1.txt"));
+  CreateTextFile(file_name, bogus_content);
+  ASSERT_TRUE(PathExists(file_name));
+
+  // Make sure it's deleted
+  EXPECT_TRUE(DeleteFile(file_name, false));
+  EXPECT_FALSE(PathExists(file_name));
+
+  // Test recursive case, create a new file
+  file_name = temp_dir_.GetPath().Append(FPL("Test DeleteFile 2.txt"));
+  CreateTextFile(file_name, bogus_content);
+  ASSERT_TRUE(PathExists(file_name));
+
+  // Make sure it's deleted
+  EXPECT_TRUE(DeleteFile(file_name, true));
+  EXPECT_FALSE(PathExists(file_name));
+}
+
+#if defined(OS_WIN)
+// Tests that the Delete function works for wild cards, especially
+// with the recursion flag.  Also coincidentally tests PathExists.
+// TODO(erikkay): see if anyone's actually using this feature of the API
+TEST_F(FileUtilTest, DeleteWildCard) {
+  // Create a file and a directory
+  FilePath file_name =
+      temp_dir_.GetPath().Append(FPL("Test DeleteWildCard.txt"));
+  CreateTextFile(file_name, bogus_content);
+  ASSERT_TRUE(PathExists(file_name));
+
+  FilePath subdir_path = temp_dir_.GetPath().Append(FPL("DeleteWildCardDir"));
+  CreateDirectory(subdir_path);
+  ASSERT_TRUE(PathExists(subdir_path));
+
+  // Create the wildcard path
+  FilePath directory_contents = temp_dir_.GetPath();
+  directory_contents = directory_contents.Append(FPL("*"));
+
+  // Delete non-recursively and check that only the file is deleted
+  EXPECT_TRUE(DeleteFile(directory_contents, false));
+  EXPECT_FALSE(PathExists(file_name));
+  EXPECT_TRUE(PathExists(subdir_path));
+
+  // Delete recursively and make sure all contents are deleted
+  EXPECT_TRUE(DeleteFile(directory_contents, true));
+  EXPECT_FALSE(PathExists(file_name));
+  EXPECT_FALSE(PathExists(subdir_path));
+}
+
+// TODO(erikkay): see if anyone's actually using this feature of the API
+TEST_F(FileUtilTest, DeleteNonExistantWildCard) {
+  // Create a file and a directory
+  FilePath subdir_path =
+      temp_dir_.GetPath().Append(FPL("DeleteNonExistantWildCard"));
+  CreateDirectory(subdir_path);
+  ASSERT_TRUE(PathExists(subdir_path));
+
+  // Create the wildcard path
+  FilePath directory_contents = subdir_path;
+  directory_contents = directory_contents.Append(FPL("*"));
+
+  // Delete non-recursively and check nothing got deleted
+  EXPECT_TRUE(DeleteFile(directory_contents, false));
+  EXPECT_TRUE(PathExists(subdir_path));
+
+  // Delete recursively and check nothing got deleted
+  EXPECT_TRUE(DeleteFile(directory_contents, true));
+  EXPECT_TRUE(PathExists(subdir_path));
+}
+#endif
+
+// Tests non-recursive Delete() for a directory.
+TEST_F(FileUtilTest, DeleteDirNonRecursive) {
+  // Create a subdirectory and put a file and two directories inside.
+  FilePath test_subdir =
+      temp_dir_.GetPath().Append(FPL("DeleteDirNonRecursive"));
+  CreateDirectory(test_subdir);
+  ASSERT_TRUE(PathExists(test_subdir));
+
+  FilePath file_name = test_subdir.Append(FPL("Test DeleteDir.txt"));
+  CreateTextFile(file_name, bogus_content);
+  ASSERT_TRUE(PathExists(file_name));
+
+  FilePath subdir_path1 = test_subdir.Append(FPL("TestSubDir1"));
+  CreateDirectory(subdir_path1);
+  ASSERT_TRUE(PathExists(subdir_path1));
+
+  FilePath subdir_path2 = test_subdir.Append(FPL("TestSubDir2"));
+  CreateDirectory(subdir_path2);
+  ASSERT_TRUE(PathExists(subdir_path2));
+
+  // Delete non-recursively and check that the empty dir got deleted
+  EXPECT_TRUE(DeleteFile(subdir_path2, false));
+  EXPECT_FALSE(PathExists(subdir_path2));
+
+  // Delete non-recursively and check that nothing got deleted
+  EXPECT_FALSE(DeleteFile(test_subdir, false));
+  EXPECT_TRUE(PathExists(test_subdir));
+  EXPECT_TRUE(PathExists(file_name));
+  EXPECT_TRUE(PathExists(subdir_path1));
+}
+
+// Tests recursive Delete() for a directory.
+TEST_F(FileUtilTest, DeleteDirRecursive) {
+  // Create a subdirectory and put a file and two directories inside.
+  FilePath test_subdir = temp_dir_.GetPath().Append(FPL("DeleteDirRecursive"));
+  CreateDirectory(test_subdir);
+  ASSERT_TRUE(PathExists(test_subdir));
+
+  FilePath file_name = test_subdir.Append(FPL("Test DeleteDirRecursive.txt"));
+  CreateTextFile(file_name, bogus_content);
+  ASSERT_TRUE(PathExists(file_name));
+
+  FilePath subdir_path1 = test_subdir.Append(FPL("TestSubDir1"));
+  CreateDirectory(subdir_path1);
+  ASSERT_TRUE(PathExists(subdir_path1));
+
+  FilePath subdir_path2 = test_subdir.Append(FPL("TestSubDir2"));
+  CreateDirectory(subdir_path2);
+  ASSERT_TRUE(PathExists(subdir_path2));
+
+  // Delete recursively and check that the empty dir got deleted
+  EXPECT_TRUE(DeleteFile(subdir_path2, true));
+  EXPECT_FALSE(PathExists(subdir_path2));
+
+  // Delete recursively and check that everything got deleted
+  EXPECT_TRUE(DeleteFile(test_subdir, true));
+  EXPECT_FALSE(PathExists(file_name));
+  EXPECT_FALSE(PathExists(subdir_path1));
+  EXPECT_FALSE(PathExists(test_subdir));
+}
+
+// Tests recursive Delete() for a directory.
+TEST_F(FileUtilTest, DeleteDirRecursiveWithOpenFile) {
+  // Create a subdirectory and put a file and two directories inside.
+  FilePath test_subdir = temp_dir_.GetPath().Append(FPL("DeleteWithOpenFile"));
+  CreateDirectory(test_subdir);
+  ASSERT_TRUE(PathExists(test_subdir));
+
+  FilePath file_name1 = test_subdir.Append(FPL("Undeletebable File1.txt"));
+  File file1(file_name1,
+             File::FLAG_CREATE | File::FLAG_READ | File::FLAG_WRITE);
+  ASSERT_TRUE(PathExists(file_name1));
+
+  FilePath file_name2 = test_subdir.Append(FPL("Deleteable File2.txt"));
+  CreateTextFile(file_name2, bogus_content);
+  ASSERT_TRUE(PathExists(file_name2));
+
+  FilePath file_name3 = test_subdir.Append(FPL("Undeletebable File3.txt"));
+  File file3(file_name3,
+             File::FLAG_CREATE | File::FLAG_READ | File::FLAG_WRITE);
+  ASSERT_TRUE(PathExists(file_name3));
+
+#if defined(OS_LINUX)
+  // On Windows, holding the file open in sufficient to make it un-deletable.
+  // The POSIX code is verifiable on Linux by creating an "immutable" file but
+  // this is best-effort because it's not supported by all file systems. Both
+  // files will have the same flags so no need to get them individually.
+  int flags;
+  bool file_attrs_supported =
+      ioctl(file1.GetPlatformFile(), FS_IOC_GETFLAGS, &flags) == 0;
+  // Some filesystems (e.g. tmpfs) don't support file attributes.
+  if (file_attrs_supported) {
+    flags |= FS_IMMUTABLE_FL;
+    ioctl(file1.GetPlatformFile(), FS_IOC_SETFLAGS, &flags);
+    ioctl(file3.GetPlatformFile(), FS_IOC_SETFLAGS, &flags);
+  }
+#endif
+
+  // Delete recursively and check that at least the second file got deleted.
+  // This ensures that un-deletable files don't impact those that can be.
+  DeleteFile(test_subdir, true);
+  EXPECT_FALSE(PathExists(file_name2));
+
+#if defined(OS_LINUX)
+  // Make sure that the test can clean up after itself.
+  if (file_attrs_supported) {
+    flags &= ~FS_IMMUTABLE_FL;
+    ioctl(file1.GetPlatformFile(), FS_IOC_SETFLAGS, &flags);
+    ioctl(file3.GetPlatformFile(), FS_IOC_SETFLAGS, &flags);
+  }
+#endif
+}
+
+TEST_F(FileUtilTest, MoveFileNew) {
+  // Create a file
+  FilePath file_name_from =
+      temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Move_Test_File.txt"));
+  CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle");
+  ASSERT_TRUE(PathExists(file_name_from));
+
+  // The destination.
+  FilePath file_name_to = temp_dir_.GetPath().Append(
+      FILE_PATH_LITERAL("Move_Test_File_Destination.txt"));
+  ASSERT_FALSE(PathExists(file_name_to));
+
+  EXPECT_TRUE(Move(file_name_from, file_name_to));
+
+  // Check everything has been moved.
+  EXPECT_FALSE(PathExists(file_name_from));
+  EXPECT_TRUE(PathExists(file_name_to));
+}
+
+TEST_F(FileUtilTest, MoveFileExists) {
+  // Create a file
+  FilePath file_name_from =
+      temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Move_Test_File.txt"));
+  CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle");
+  ASSERT_TRUE(PathExists(file_name_from));
+
+  // The destination name.
+  FilePath file_name_to = temp_dir_.GetPath().Append(
+      FILE_PATH_LITERAL("Move_Test_File_Destination.txt"));
+  CreateTextFile(file_name_to, L"Old file content");
+  ASSERT_TRUE(PathExists(file_name_to));
+
+  EXPECT_TRUE(Move(file_name_from, file_name_to));
+
+  // Check everything has been moved.
+  EXPECT_FALSE(PathExists(file_name_from));
+  EXPECT_TRUE(PathExists(file_name_to));
+  EXPECT_TRUE(L"Gooooooooooooooooooooogle" == ReadTextFile(file_name_to));
+}
+
+TEST_F(FileUtilTest, MoveFileDirExists) {
+  // Create a file
+  FilePath file_name_from =
+      temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Move_Test_File.txt"));
+  CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle");
+  ASSERT_TRUE(PathExists(file_name_from));
+
+  // The destination directory
+  FilePath dir_name_to =
+      temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Destination"));
+  CreateDirectory(dir_name_to);
+  ASSERT_TRUE(PathExists(dir_name_to));
+
+  EXPECT_FALSE(Move(file_name_from, dir_name_to));
+}
+
+
+TEST_F(FileUtilTest, MoveNew) {
+  // Create a directory
+  FilePath dir_name_from =
+      temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Move_From_Subdir"));
+  CreateDirectory(dir_name_from);
+  ASSERT_TRUE(PathExists(dir_name_from));
+
+  // Create a file under the directory
+  FilePath txt_file_name(FILE_PATH_LITERAL("Move_Test_File.txt"));
+  FilePath file_name_from = dir_name_from.Append(txt_file_name);
+  CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle");
+  ASSERT_TRUE(PathExists(file_name_from));
+
+  // Move the directory.
+  FilePath dir_name_to =
+      temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Move_To_Subdir"));
+  FilePath file_name_to =
+      dir_name_to.Append(FILE_PATH_LITERAL("Move_Test_File.txt"));
+
+  ASSERT_FALSE(PathExists(dir_name_to));
+
+  EXPECT_TRUE(Move(dir_name_from, dir_name_to));
+
+  // Check everything has been moved.
+  EXPECT_FALSE(PathExists(dir_name_from));
+  EXPECT_FALSE(PathExists(file_name_from));
+  EXPECT_TRUE(PathExists(dir_name_to));
+  EXPECT_TRUE(PathExists(file_name_to));
+
+  // Test path traversal.
+  file_name_from = dir_name_to.Append(txt_file_name);
+  file_name_to = dir_name_to.Append(FILE_PATH_LITERAL(".."));
+  file_name_to = file_name_to.Append(txt_file_name);
+  EXPECT_FALSE(Move(file_name_from, file_name_to));
+  EXPECT_TRUE(PathExists(file_name_from));
+  EXPECT_FALSE(PathExists(file_name_to));
+  EXPECT_TRUE(internal::MoveUnsafe(file_name_from, file_name_to));
+  EXPECT_FALSE(PathExists(file_name_from));
+  EXPECT_TRUE(PathExists(file_name_to));
+}
+
+TEST_F(FileUtilTest, MoveExist) {
+  // Create a directory
+  FilePath dir_name_from =
+      temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Move_From_Subdir"));
+  CreateDirectory(dir_name_from);
+  ASSERT_TRUE(PathExists(dir_name_from));
+
+  // Create a file under the directory
+  FilePath file_name_from =
+      dir_name_from.Append(FILE_PATH_LITERAL("Move_Test_File.txt"));
+  CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle");
+  ASSERT_TRUE(PathExists(file_name_from));
+
+  // Move the directory
+  FilePath dir_name_exists =
+      temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Destination"));
+
+  FilePath dir_name_to =
+      dir_name_exists.Append(FILE_PATH_LITERAL("Move_To_Subdir"));
+  FilePath file_name_to =
+      dir_name_to.Append(FILE_PATH_LITERAL("Move_Test_File.txt"));
+
+  // Create the destination directory.
+  CreateDirectory(dir_name_exists);
+  ASSERT_TRUE(PathExists(dir_name_exists));
+
+  EXPECT_TRUE(Move(dir_name_from, dir_name_to));
+
+  // Check everything has been moved.
+  EXPECT_FALSE(PathExists(dir_name_from));
+  EXPECT_FALSE(PathExists(file_name_from));
+  EXPECT_TRUE(PathExists(dir_name_to));
+  EXPECT_TRUE(PathExists(file_name_to));
+}
+
+TEST_F(FileUtilTest, CopyDirectoryRecursivelyNew) {
+  // Create a directory.
+  FilePath dir_name_from =
+      temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_From_Subdir"));
+  CreateDirectory(dir_name_from);
+  ASSERT_TRUE(PathExists(dir_name_from));
+
+  // Create a file under the directory.
+  FilePath file_name_from =
+      dir_name_from.Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
+  CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle");
+  ASSERT_TRUE(PathExists(file_name_from));
+
+  // Create a subdirectory.
+  FilePath subdir_name_from =
+      dir_name_from.Append(FILE_PATH_LITERAL("Subdir"));
+  CreateDirectory(subdir_name_from);
+  ASSERT_TRUE(PathExists(subdir_name_from));
+
+  // Create a file under the subdirectory.
+  FilePath file_name2_from =
+      subdir_name_from.Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
+  CreateTextFile(file_name2_from, L"Gooooooooooooooooooooogle");
+  ASSERT_TRUE(PathExists(file_name2_from));
+
+  // Copy the directory recursively.
+  FilePath dir_name_to =
+      temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_To_Subdir"));
+  FilePath file_name_to =
+      dir_name_to.Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
+  FilePath subdir_name_to =
+      dir_name_to.Append(FILE_PATH_LITERAL("Subdir"));
+  FilePath file_name2_to =
+      subdir_name_to.Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
+
+  ASSERT_FALSE(PathExists(dir_name_to));
+
+  EXPECT_TRUE(CopyDirectory(dir_name_from, dir_name_to, true));
+
+  // Check everything has been copied.
+  EXPECT_TRUE(PathExists(dir_name_from));
+  EXPECT_TRUE(PathExists(file_name_from));
+  EXPECT_TRUE(PathExists(subdir_name_from));
+  EXPECT_TRUE(PathExists(file_name2_from));
+  EXPECT_TRUE(PathExists(dir_name_to));
+  EXPECT_TRUE(PathExists(file_name_to));
+  EXPECT_TRUE(PathExists(subdir_name_to));
+  EXPECT_TRUE(PathExists(file_name2_to));
+}
+
+TEST_F(FileUtilTest, CopyDirectoryRecursivelyExists) {
+  // Create a directory.
+  FilePath dir_name_from =
+      temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_From_Subdir"));
+  CreateDirectory(dir_name_from);
+  ASSERT_TRUE(PathExists(dir_name_from));
+
+  // Create a file under the directory.
+  FilePath file_name_from =
+      dir_name_from.Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
+  CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle");
+  ASSERT_TRUE(PathExists(file_name_from));
+
+  // Create a subdirectory.
+  FilePath subdir_name_from =
+      dir_name_from.Append(FILE_PATH_LITERAL("Subdir"));
+  CreateDirectory(subdir_name_from);
+  ASSERT_TRUE(PathExists(subdir_name_from));
+
+  // Create a file under the subdirectory.
+  FilePath file_name2_from =
+      subdir_name_from.Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
+  CreateTextFile(file_name2_from, L"Gooooooooooooooooooooogle");
+  ASSERT_TRUE(PathExists(file_name2_from));
+
+  // Copy the directory recursively.
+  FilePath dir_name_exists =
+      temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Destination"));
+
+  FilePath dir_name_to =
+      dir_name_exists.Append(FILE_PATH_LITERAL("Copy_From_Subdir"));
+  FilePath file_name_to =
+      dir_name_to.Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
+  FilePath subdir_name_to =
+      dir_name_to.Append(FILE_PATH_LITERAL("Subdir"));
+  FilePath file_name2_to =
+      subdir_name_to.Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
+
+  // Create the destination directory.
+  CreateDirectory(dir_name_exists);
+  ASSERT_TRUE(PathExists(dir_name_exists));
+
+  EXPECT_TRUE(CopyDirectory(dir_name_from, dir_name_exists, true));
+
+  // Check everything has been copied.
+  EXPECT_TRUE(PathExists(dir_name_from));
+  EXPECT_TRUE(PathExists(file_name_from));
+  EXPECT_TRUE(PathExists(subdir_name_from));
+  EXPECT_TRUE(PathExists(file_name2_from));
+  EXPECT_TRUE(PathExists(dir_name_to));
+  EXPECT_TRUE(PathExists(file_name_to));
+  EXPECT_TRUE(PathExists(subdir_name_to));
+  EXPECT_TRUE(PathExists(file_name2_to));
+}
+
+TEST_F(FileUtilTest, CopyDirectoryNew) {
+  // Create a directory.
+  FilePath dir_name_from =
+      temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_From_Subdir"));
+  CreateDirectory(dir_name_from);
+  ASSERT_TRUE(PathExists(dir_name_from));
+
+  // Create a file under the directory.
+  FilePath file_name_from =
+      dir_name_from.Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
+  CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle");
+  ASSERT_TRUE(PathExists(file_name_from));
+
+  // Create a subdirectory.
+  FilePath subdir_name_from =
+      dir_name_from.Append(FILE_PATH_LITERAL("Subdir"));
+  CreateDirectory(subdir_name_from);
+  ASSERT_TRUE(PathExists(subdir_name_from));
+
+  // Create a file under the subdirectory.
+  FilePath file_name2_from =
+      subdir_name_from.Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
+  CreateTextFile(file_name2_from, L"Gooooooooooooooooooooogle");
+  ASSERT_TRUE(PathExists(file_name2_from));
+
+  // Copy the directory not recursively.
+  FilePath dir_name_to =
+      temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_To_Subdir"));
+  FilePath file_name_to =
+      dir_name_to.Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
+  FilePath subdir_name_to =
+      dir_name_to.Append(FILE_PATH_LITERAL("Subdir"));
+
+  ASSERT_FALSE(PathExists(dir_name_to));
+
+  EXPECT_TRUE(CopyDirectory(dir_name_from, dir_name_to, false));
+
+  // Check everything has been copied.
+  EXPECT_TRUE(PathExists(dir_name_from));
+  EXPECT_TRUE(PathExists(file_name_from));
+  EXPECT_TRUE(PathExists(subdir_name_from));
+  EXPECT_TRUE(PathExists(file_name2_from));
+  EXPECT_TRUE(PathExists(dir_name_to));
+  EXPECT_TRUE(PathExists(file_name_to));
+  EXPECT_FALSE(PathExists(subdir_name_to));
+}
+
+TEST_F(FileUtilTest, CopyDirectoryExists) {
+  // Create a directory.
+  FilePath dir_name_from =
+      temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_From_Subdir"));
+  CreateDirectory(dir_name_from);
+  ASSERT_TRUE(PathExists(dir_name_from));
+
+  // Create a file under the directory.
+  FilePath file_name_from =
+      dir_name_from.Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
+  CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle");
+  ASSERT_TRUE(PathExists(file_name_from));
+
+  // Create a subdirectory.
+  FilePath subdir_name_from =
+      dir_name_from.Append(FILE_PATH_LITERAL("Subdir"));
+  CreateDirectory(subdir_name_from);
+  ASSERT_TRUE(PathExists(subdir_name_from));
+
+  // Create a file under the subdirectory.
+  FilePath file_name2_from =
+      subdir_name_from.Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
+  CreateTextFile(file_name2_from, L"Gooooooooooooooooooooogle");
+  ASSERT_TRUE(PathExists(file_name2_from));
+
+  // Copy the directory not recursively.
+  FilePath dir_name_to =
+      temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_To_Subdir"));
+  FilePath file_name_to =
+      dir_name_to.Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
+  FilePath subdir_name_to =
+      dir_name_to.Append(FILE_PATH_LITERAL("Subdir"));
+
+  // Create the destination directory.
+  CreateDirectory(dir_name_to);
+  ASSERT_TRUE(PathExists(dir_name_to));
+
+  EXPECT_TRUE(CopyDirectory(dir_name_from, dir_name_to, false));
+
+  // Check everything has been copied.
+  EXPECT_TRUE(PathExists(dir_name_from));
+  EXPECT_TRUE(PathExists(file_name_from));
+  EXPECT_TRUE(PathExists(subdir_name_from));
+  EXPECT_TRUE(PathExists(file_name2_from));
+  EXPECT_TRUE(PathExists(dir_name_to));
+  EXPECT_TRUE(PathExists(file_name_to));
+  EXPECT_FALSE(PathExists(subdir_name_to));
+}
+
+TEST_F(FileUtilTest, CopyFileWithCopyDirectoryRecursiveToNew) {
+  // Create a file
+  FilePath file_name_from =
+      temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
+  CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle");
+  ASSERT_TRUE(PathExists(file_name_from));
+
+  // The destination name
+  FilePath file_name_to = temp_dir_.GetPath().Append(
+      FILE_PATH_LITERAL("Copy_Test_File_Destination.txt"));
+  ASSERT_FALSE(PathExists(file_name_to));
+
+  EXPECT_TRUE(CopyDirectory(file_name_from, file_name_to, true));
+
+  // Check the has been copied
+  EXPECT_TRUE(PathExists(file_name_to));
+}
+
+TEST_F(FileUtilTest, CopyFileWithCopyDirectoryRecursiveToExisting) {
+  // Create a file
+  FilePath file_name_from =
+      temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
+  CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle");
+  ASSERT_TRUE(PathExists(file_name_from));
+
+  // The destination name
+  FilePath file_name_to = temp_dir_.GetPath().Append(
+      FILE_PATH_LITERAL("Copy_Test_File_Destination.txt"));
+  CreateTextFile(file_name_to, L"Old file content");
+  ASSERT_TRUE(PathExists(file_name_to));
+
+  EXPECT_TRUE(CopyDirectory(file_name_from, file_name_to, true));
+
+  // Check the has been copied
+  EXPECT_TRUE(PathExists(file_name_to));
+  EXPECT_TRUE(L"Gooooooooooooooooooooogle" == ReadTextFile(file_name_to));
+}
+
+TEST_F(FileUtilTest, CopyFileWithCopyDirectoryRecursiveToExistingDirectory) {
+  // Create a file
+  FilePath file_name_from =
+      temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
+  CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle");
+  ASSERT_TRUE(PathExists(file_name_from));
+
+  // The destination
+  FilePath dir_name_to =
+      temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Destination"));
+  CreateDirectory(dir_name_to);
+  ASSERT_TRUE(PathExists(dir_name_to));
+  FilePath file_name_to =
+      dir_name_to.Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
+
+  EXPECT_TRUE(CopyDirectory(file_name_from, dir_name_to, true));
+
+  // Check the has been copied
+  EXPECT_TRUE(PathExists(file_name_to));
+}
+
+TEST_F(FileUtilTest, CopyFileFailureWithCopyDirectoryExcl) {
+  // Create a file
+  FilePath file_name_from =
+      temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
+  CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle");
+  ASSERT_TRUE(PathExists(file_name_from));
+
+  // Make a destination file.
+  FilePath file_name_to = temp_dir_.GetPath().Append(
+      FILE_PATH_LITERAL("Copy_Test_File_Destination.txt"));
+  CreateTextFile(file_name_to, L"Old file content");
+  ASSERT_TRUE(PathExists(file_name_to));
+
+  // Overwriting the destination should fail.
+  EXPECT_FALSE(CopyDirectoryExcl(file_name_from, file_name_to, true));
+  EXPECT_EQ(L"Old file content", ReadTextFile(file_name_to));
+}
+
+TEST_F(FileUtilTest, CopyDirectoryWithTrailingSeparators) {
+  // Create a directory.
+  FilePath dir_name_from =
+      temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_From_Subdir"));
+  CreateDirectory(dir_name_from);
+  ASSERT_TRUE(PathExists(dir_name_from));
+
+  // Create a file under the directory.
+  FilePath file_name_from =
+      dir_name_from.Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
+  CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle");
+  ASSERT_TRUE(PathExists(file_name_from));
+
+  // Copy the directory recursively.
+  FilePath dir_name_to =
+      temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_To_Subdir"));
+  FilePath file_name_to =
+      dir_name_to.Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
+
+  // Create from path with trailing separators.
+#if defined(OS_WIN)
+  FilePath from_path =
+      temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_From_Subdir\\\\\\"));
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+  FilePath from_path =
+      temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_From_Subdir///"));
+#endif
+
+  EXPECT_TRUE(CopyDirectory(from_path, dir_name_to, true));
+
+  // Check everything has been copied.
+  EXPECT_TRUE(PathExists(dir_name_from));
+  EXPECT_TRUE(PathExists(file_name_from));
+  EXPECT_TRUE(PathExists(dir_name_to));
+  EXPECT_TRUE(PathExists(file_name_to));
+}
+
+#if defined(OS_POSIX)
+TEST_F(FileUtilTest, CopyDirectoryWithNonRegularFiles) {
+  // Create a directory.
+  FilePath dir_name_from =
+      temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_From_Subdir"));
+  ASSERT_TRUE(CreateDirectory(dir_name_from));
+  ASSERT_TRUE(PathExists(dir_name_from));
+
+  // Create a file under the directory.
+  FilePath file_name_from =
+      dir_name_from.Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
+  CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle");
+  ASSERT_TRUE(PathExists(file_name_from));
+
+  // Create a symbolic link under the directory pointing to that file.
+  FilePath symlink_name_from =
+      dir_name_from.Append(FILE_PATH_LITERAL("Symlink"));
+  ASSERT_TRUE(CreateSymbolicLink(file_name_from, symlink_name_from));
+  ASSERT_TRUE(PathExists(symlink_name_from));
+
+  // Create a fifo under the directory.
+  FilePath fifo_name_from =
+      dir_name_from.Append(FILE_PATH_LITERAL("Fifo"));
+  ASSERT_EQ(0, mkfifo(fifo_name_from.value().c_str(), 0644));
+  ASSERT_TRUE(PathExists(fifo_name_from));
+
+  // Copy the directory.
+  FilePath dir_name_to =
+      temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_To_Subdir"));
+  FilePath file_name_to =
+      dir_name_to.Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
+  FilePath symlink_name_to =
+      dir_name_to.Append(FILE_PATH_LITERAL("Symlink"));
+  FilePath fifo_name_to =
+      dir_name_to.Append(FILE_PATH_LITERAL("Fifo"));
+
+  ASSERT_FALSE(PathExists(dir_name_to));
+
+  EXPECT_TRUE(CopyDirectory(dir_name_from, dir_name_to, false));
+
+  // Check that only directories and regular files are copied.
+  EXPECT_TRUE(PathExists(dir_name_from));
+  EXPECT_TRUE(PathExists(file_name_from));
+  EXPECT_TRUE(PathExists(symlink_name_from));
+  EXPECT_TRUE(PathExists(fifo_name_from));
+  EXPECT_TRUE(PathExists(dir_name_to));
+  EXPECT_TRUE(PathExists(file_name_to));
+  EXPECT_FALSE(PathExists(symlink_name_to));
+  EXPECT_FALSE(PathExists(fifo_name_to));
+}
+
+TEST_F(FileUtilTest, CopyDirectoryExclFileOverSymlink) {
+  // Create a directory.
+  FilePath dir_name_from =
+      temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_From_Subdir"));
+  ASSERT_TRUE(CreateDirectory(dir_name_from));
+  ASSERT_TRUE(PathExists(dir_name_from));
+
+  // Create a file under the directory.
+  FilePath file_name_from =
+      dir_name_from.Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
+  CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle");
+  ASSERT_TRUE(PathExists(file_name_from));
+
+  // Create a destination directory with a symlink of the same name.
+  FilePath dir_name_to =
+      temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_To_Subdir"));
+  ASSERT_TRUE(CreateDirectory(dir_name_to));
+  ASSERT_TRUE(PathExists(dir_name_to));
+
+  FilePath symlink_target =
+      dir_name_to.Append(FILE_PATH_LITERAL("Symlink_Target.txt"));
+  CreateTextFile(symlink_target, L"asdf");
+  ASSERT_TRUE(PathExists(symlink_target));
+
+  FilePath symlink_name_to =
+      dir_name_to.Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
+  ASSERT_TRUE(CreateSymbolicLink(symlink_target, symlink_name_to));
+  ASSERT_TRUE(PathExists(symlink_name_to));
+
+  // Check that copying fails.
+  EXPECT_FALSE(CopyDirectoryExcl(dir_name_from, dir_name_to, false));
+}
+
+TEST_F(FileUtilTest, CopyDirectoryExclDirectoryOverSymlink) {
+  // Create a directory.
+  FilePath dir_name_from =
+      temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_From_Subdir"));
+  ASSERT_TRUE(CreateDirectory(dir_name_from));
+  ASSERT_TRUE(PathExists(dir_name_from));
+
+  // Create a subdirectory.
+  FilePath subdir_name_from = dir_name_from.Append(FILE_PATH_LITERAL("Subsub"));
+  CreateDirectory(subdir_name_from);
+  ASSERT_TRUE(PathExists(subdir_name_from));
+
+  // Create a destination directory with a symlink of the same name.
+  FilePath dir_name_to =
+      temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_To_Subdir"));
+  ASSERT_TRUE(CreateDirectory(dir_name_to));
+  ASSERT_TRUE(PathExists(dir_name_to));
+
+  FilePath symlink_target = dir_name_to.Append(FILE_PATH_LITERAL("Subsub"));
+  CreateTextFile(symlink_target, L"asdf");
+  ASSERT_TRUE(PathExists(symlink_target));
+
+  FilePath symlink_name_to =
+      dir_name_to.Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
+  ASSERT_TRUE(CreateSymbolicLink(symlink_target, symlink_name_to));
+  ASSERT_TRUE(PathExists(symlink_name_to));
+
+  // Check that copying fails.
+  EXPECT_FALSE(CopyDirectoryExcl(dir_name_from, dir_name_to, false));
+}
+
+TEST_F(FileUtilTest, CopyDirectoryExclFileOverDanglingSymlink) {
+  // Create a directory.
+  FilePath dir_name_from =
+      temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_From_Subdir"));
+  ASSERT_TRUE(CreateDirectory(dir_name_from));
+  ASSERT_TRUE(PathExists(dir_name_from));
+
+  // Create a file under the directory.
+  FilePath file_name_from =
+      dir_name_from.Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
+  CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle");
+  ASSERT_TRUE(PathExists(file_name_from));
+
+  // Create a destination directory with a dangling symlink of the same name.
+  FilePath dir_name_to =
+      temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_To_Subdir"));
+  ASSERT_TRUE(CreateDirectory(dir_name_to));
+  ASSERT_TRUE(PathExists(dir_name_to));
+
+  FilePath symlink_target =
+      dir_name_to.Append(FILE_PATH_LITERAL("Symlink_Target.txt"));
+  CreateTextFile(symlink_target, L"asdf");
+  ASSERT_TRUE(PathExists(symlink_target));
+
+  FilePath symlink_name_to =
+      dir_name_to.Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
+  ASSERT_TRUE(CreateSymbolicLink(symlink_target, symlink_name_to));
+  ASSERT_TRUE(PathExists(symlink_name_to));
+  ASSERT_TRUE(DeleteFile(symlink_target, false));
+
+  // Check that copying fails and that no file was created for the symlink's
+  // referent.
+  EXPECT_FALSE(CopyDirectoryExcl(dir_name_from, dir_name_to, false));
+  EXPECT_FALSE(PathExists(symlink_target));
+}
+
+TEST_F(FileUtilTest, CopyDirectoryExclDirectoryOverDanglingSymlink) {
+  // Create a directory.
+  FilePath dir_name_from =
+      temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_From_Subdir"));
+  ASSERT_TRUE(CreateDirectory(dir_name_from));
+  ASSERT_TRUE(PathExists(dir_name_from));
+
+  // Create a subdirectory.
+  FilePath subdir_name_from = dir_name_from.Append(FILE_PATH_LITERAL("Subsub"));
+  CreateDirectory(subdir_name_from);
+  ASSERT_TRUE(PathExists(subdir_name_from));
+
+  // Create a destination directory with a dangling symlink of the same name.
+  FilePath dir_name_to =
+      temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_To_Subdir"));
+  ASSERT_TRUE(CreateDirectory(dir_name_to));
+  ASSERT_TRUE(PathExists(dir_name_to));
+
+  FilePath symlink_target =
+      dir_name_to.Append(FILE_PATH_LITERAL("Symlink_Target.txt"));
+  CreateTextFile(symlink_target, L"asdf");
+  ASSERT_TRUE(PathExists(symlink_target));
+
+  FilePath symlink_name_to =
+      dir_name_to.Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
+  ASSERT_TRUE(CreateSymbolicLink(symlink_target, symlink_name_to));
+  ASSERT_TRUE(PathExists(symlink_name_to));
+  ASSERT_TRUE(DeleteFile(symlink_target, false));
+
+  // Check that copying fails and that no directory was created for the
+  // symlink's referent.
+  EXPECT_FALSE(CopyDirectoryExcl(dir_name_from, dir_name_to, false));
+  EXPECT_FALSE(PathExists(symlink_target));
+}
+
+TEST_F(FileUtilTest, CopyDirectoryExclFileOverFifo) {
+  // Create a directory.
+  FilePath dir_name_from =
+      temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_From_Subdir"));
+  ASSERT_TRUE(CreateDirectory(dir_name_from));
+  ASSERT_TRUE(PathExists(dir_name_from));
+
+  // Create a file under the directory.
+  FilePath file_name_from =
+      dir_name_from.Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
+  CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle");
+  ASSERT_TRUE(PathExists(file_name_from));
+
+  // Create a destination directory with a fifo of the same name.
+  FilePath dir_name_to =
+      temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_To_Subdir"));
+  ASSERT_TRUE(CreateDirectory(dir_name_to));
+  ASSERT_TRUE(PathExists(dir_name_to));
+
+  FilePath fifo_name_to =
+      dir_name_to.Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
+  ASSERT_EQ(0, mkfifo(fifo_name_to.value().c_str(), 0644));
+  ASSERT_TRUE(PathExists(fifo_name_to));
+
+  // Check that copying fails.
+  EXPECT_FALSE(CopyDirectoryExcl(dir_name_from, dir_name_to, false));
+}
+#endif  // defined(OS_POSIX)
+
+TEST_F(FileUtilTest, CopyFile) {
+  // Create a directory
+  FilePath dir_name_from =
+      temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_From_Subdir"));
+  ASSERT_TRUE(CreateDirectory(dir_name_from));
+  ASSERT_TRUE(DirectoryExists(dir_name_from));
+
+  // Create a file under the directory
+  FilePath file_name_from =
+      dir_name_from.Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
+  const std::wstring file_contents(L"Gooooooooooooooooooooogle");
+  CreateTextFile(file_name_from, file_contents);
+  ASSERT_TRUE(PathExists(file_name_from));
+
+  // Copy the file.
+  FilePath dest_file = dir_name_from.Append(FILE_PATH_LITERAL("DestFile.txt"));
+  ASSERT_TRUE(CopyFile(file_name_from, dest_file));
+
+  // Try to copy the file to another location using '..' in the path.
+  FilePath dest_file2(dir_name_from);
+  dest_file2 = dest_file2.AppendASCII("..");
+  dest_file2 = dest_file2.AppendASCII("DestFile.txt");
+  ASSERT_FALSE(CopyFile(file_name_from, dest_file2));
+
+  FilePath dest_file2_test(dir_name_from);
+  dest_file2_test = dest_file2_test.DirName();
+  dest_file2_test = dest_file2_test.AppendASCII("DestFile.txt");
+
+  // Check expected copy results.
+  EXPECT_TRUE(PathExists(file_name_from));
+  EXPECT_TRUE(PathExists(dest_file));
+  EXPECT_EQ(file_contents, ReadTextFile(dest_file));
+  EXPECT_FALSE(PathExists(dest_file2_test));
+  EXPECT_FALSE(PathExists(dest_file2));
+
+  // Change |file_name_from| contents.
+  const std::wstring new_file_contents(L"Moogle");
+  CreateTextFile(file_name_from, new_file_contents);
+  ASSERT_TRUE(PathExists(file_name_from));
+  EXPECT_EQ(new_file_contents, ReadTextFile(file_name_from));
+
+  // Overwrite |dest_file|.
+  ASSERT_TRUE(CopyFile(file_name_from, dest_file));
+  EXPECT_TRUE(PathExists(dest_file));
+  EXPECT_EQ(new_file_contents, ReadTextFile(dest_file));
+
+  // Create another directory.
+  FilePath dest_dir = temp_dir_.GetPath().Append(FPL("dest_dir"));
+  ASSERT_TRUE(CreateDirectory(dest_dir));
+  EXPECT_TRUE(DirectoryExists(dest_dir));
+  EXPECT_TRUE(IsDirectoryEmpty(dest_dir));
+
+  // Make sure CopyFile() cannot overwrite a directory.
+  ASSERT_FALSE(CopyFile(file_name_from, dest_dir));
+  EXPECT_TRUE(DirectoryExists(dest_dir));
+  EXPECT_TRUE(IsDirectoryEmpty(dest_dir));
+}
+
+// file_util winds up using autoreleased objects on the Mac, so this needs
+// to be a PlatformTest.
+typedef PlatformTest ReadOnlyFileUtilTest;
+
+TEST_F(ReadOnlyFileUtilTest, ContentsEqual) {
+  FilePath data_dir;
+  ASSERT_TRUE(PathService::Get(DIR_TEST_DATA, &data_dir));
+  data_dir = data_dir.AppendASCII("file_util");
+  ASSERT_TRUE(PathExists(data_dir));
+
+  FilePath original_file =
+      data_dir.Append(FILE_PATH_LITERAL("original.txt"));
+  FilePath same_file =
+      data_dir.Append(FILE_PATH_LITERAL("same.txt"));
+  FilePath same_length_file =
+      data_dir.Append(FILE_PATH_LITERAL("same_length.txt"));
+  FilePath different_file =
+      data_dir.Append(FILE_PATH_LITERAL("different.txt"));
+  FilePath different_first_file =
+      data_dir.Append(FILE_PATH_LITERAL("different_first.txt"));
+  FilePath different_last_file =
+      data_dir.Append(FILE_PATH_LITERAL("different_last.txt"));
+  FilePath empty1_file =
+      data_dir.Append(FILE_PATH_LITERAL("empty1.txt"));
+  FilePath empty2_file =
+      data_dir.Append(FILE_PATH_LITERAL("empty2.txt"));
+  FilePath shortened_file =
+      data_dir.Append(FILE_PATH_LITERAL("shortened.txt"));
+  FilePath binary_file =
+      data_dir.Append(FILE_PATH_LITERAL("binary_file.bin"));
+  FilePath binary_file_same =
+      data_dir.Append(FILE_PATH_LITERAL("binary_file_same.bin"));
+  FilePath binary_file_diff =
+      data_dir.Append(FILE_PATH_LITERAL("binary_file_diff.bin"));
+
+  EXPECT_TRUE(ContentsEqual(original_file, original_file));
+  EXPECT_TRUE(ContentsEqual(original_file, same_file));
+  EXPECT_FALSE(ContentsEqual(original_file, same_length_file));
+  EXPECT_FALSE(ContentsEqual(original_file, different_file));
+  EXPECT_FALSE(ContentsEqual(FilePath(FILE_PATH_LITERAL("bogusname")),
+                             FilePath(FILE_PATH_LITERAL("bogusname"))));
+  EXPECT_FALSE(ContentsEqual(original_file, different_first_file));
+  EXPECT_FALSE(ContentsEqual(original_file, different_last_file));
+  EXPECT_TRUE(ContentsEqual(empty1_file, empty2_file));
+  EXPECT_FALSE(ContentsEqual(original_file, shortened_file));
+  EXPECT_FALSE(ContentsEqual(shortened_file, original_file));
+  EXPECT_TRUE(ContentsEqual(binary_file, binary_file_same));
+  EXPECT_FALSE(ContentsEqual(binary_file, binary_file_diff));
+}
+
+TEST_F(ReadOnlyFileUtilTest, TextContentsEqual) {
+  FilePath data_dir;
+  ASSERT_TRUE(PathService::Get(DIR_TEST_DATA, &data_dir));
+  data_dir = data_dir.AppendASCII("file_util");
+  ASSERT_TRUE(PathExists(data_dir));
+
+  FilePath original_file =
+      data_dir.Append(FILE_PATH_LITERAL("original.txt"));
+  FilePath same_file =
+      data_dir.Append(FILE_PATH_LITERAL("same.txt"));
+  FilePath crlf_file =
+      data_dir.Append(FILE_PATH_LITERAL("crlf.txt"));
+  FilePath shortened_file =
+      data_dir.Append(FILE_PATH_LITERAL("shortened.txt"));
+  FilePath different_file =
+      data_dir.Append(FILE_PATH_LITERAL("different.txt"));
+  FilePath different_first_file =
+      data_dir.Append(FILE_PATH_LITERAL("different_first.txt"));
+  FilePath different_last_file =
+      data_dir.Append(FILE_PATH_LITERAL("different_last.txt"));
+  FilePath first1_file =
+      data_dir.Append(FILE_PATH_LITERAL("first1.txt"));
+  FilePath first2_file =
+      data_dir.Append(FILE_PATH_LITERAL("first2.txt"));
+  FilePath empty1_file =
+      data_dir.Append(FILE_PATH_LITERAL("empty1.txt"));
+  FilePath empty2_file =
+      data_dir.Append(FILE_PATH_LITERAL("empty2.txt"));
+  FilePath blank_line_file =
+      data_dir.Append(FILE_PATH_LITERAL("blank_line.txt"));
+  FilePath blank_line_crlf_file =
+      data_dir.Append(FILE_PATH_LITERAL("blank_line_crlf.txt"));
+
+  EXPECT_TRUE(TextContentsEqual(original_file, same_file));
+  EXPECT_TRUE(TextContentsEqual(original_file, crlf_file));
+  EXPECT_FALSE(TextContentsEqual(original_file, shortened_file));
+  EXPECT_FALSE(TextContentsEqual(original_file, different_file));
+  EXPECT_FALSE(TextContentsEqual(original_file, different_first_file));
+  EXPECT_FALSE(TextContentsEqual(original_file, different_last_file));
+  EXPECT_FALSE(TextContentsEqual(first1_file, first2_file));
+  EXPECT_TRUE(TextContentsEqual(empty1_file, empty2_file));
+  EXPECT_FALSE(TextContentsEqual(original_file, empty1_file));
+  EXPECT_TRUE(TextContentsEqual(blank_line_file, blank_line_crlf_file));
+}
+
+// We don't need equivalent functionality outside of Windows.
+#if defined(OS_WIN)
+TEST_F(FileUtilTest, CopyAndDeleteDirectoryTest) {
+  // Create a directory
+  FilePath dir_name_from = temp_dir_.GetPath().Append(
+      FILE_PATH_LITERAL("CopyAndDelete_From_Subdir"));
+  CreateDirectory(dir_name_from);
+  ASSERT_TRUE(PathExists(dir_name_from));
+
+  // Create a file under the directory
+  FilePath file_name_from =
+      dir_name_from.Append(FILE_PATH_LITERAL("CopyAndDelete_Test_File.txt"));
+  CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle");
+  ASSERT_TRUE(PathExists(file_name_from));
+
+  // Move the directory by using CopyAndDeleteDirectory
+  FilePath dir_name_to =
+      temp_dir_.GetPath().Append(FILE_PATH_LITERAL("CopyAndDelete_To_Subdir"));
+  FilePath file_name_to =
+      dir_name_to.Append(FILE_PATH_LITERAL("CopyAndDelete_Test_File.txt"));
+
+  ASSERT_FALSE(PathExists(dir_name_to));
+
+  EXPECT_TRUE(internal::CopyAndDeleteDirectory(dir_name_from,
+                                                     dir_name_to));
+
+  // Check everything has been moved.
+  EXPECT_FALSE(PathExists(dir_name_from));
+  EXPECT_FALSE(PathExists(file_name_from));
+  EXPECT_TRUE(PathExists(dir_name_to));
+  EXPECT_TRUE(PathExists(file_name_to));
+}
+
+TEST_F(FileUtilTest, GetTempDirTest) {
+  static const TCHAR* kTmpKey = _T("TMP");
+  static const TCHAR* kTmpValues[] = {
+    _T(""), _T("C:"), _T("C:\\"), _T("C:\\tmp"), _T("C:\\tmp\\")
+  };
+  // Save the original $TMP.
+  size_t original_tmp_size;
+  TCHAR* original_tmp;
+  ASSERT_EQ(0, ::_tdupenv_s(&original_tmp, &original_tmp_size, kTmpKey));
+  // original_tmp may be NULL.
+
+  for (unsigned int i = 0; i < arraysize(kTmpValues); ++i) {
+    FilePath path;
+    ::_tputenv_s(kTmpKey, kTmpValues[i]);
+    GetTempDir(&path);
+    EXPECT_TRUE(path.IsAbsolute()) << "$TMP=" << kTmpValues[i] <<
+        " result=" << path.value();
+  }
+
+  // Restore the original $TMP.
+  if (original_tmp) {
+    ::_tputenv_s(kTmpKey, original_tmp);
+    free(original_tmp);
+  } else {
+    ::_tputenv_s(kTmpKey, _T(""));
+  }
+}
+#endif  // OS_WIN
+
+// Test that files opened by OpenFile are not set up for inheritance into child
+// procs.
+TEST_F(FileUtilTest, OpenFileNoInheritance) {
+  FilePath file_path(temp_dir_.GetPath().Append(FPL("a_file")));
+
+  for (const char* mode : {"wb", "r,ccs=UTF-8"}) {
+    SCOPED_TRACE(mode);
+    ASSERT_NO_FATAL_FAILURE(CreateTextFile(file_path, L"Geepers"));
+    FILE* file = OpenFile(file_path, mode);
+    ASSERT_NE(nullptr, file);
+    {
+      ScopedClosureRunner file_closer(Bind(IgnoreResult(&CloseFile), file));
+      bool is_inheritable = true;
+      ASSERT_NO_FATAL_FAILURE(GetIsInheritable(file, &is_inheritable));
+      EXPECT_FALSE(is_inheritable);
+    }
+    ASSERT_TRUE(DeleteFile(file_path, false));
+  }
+}
+
+TEST_F(FileUtilTest, CreateTemporaryFileTest) {
+  FilePath temp_files[3];
+  for (int i = 0; i < 3; i++) {
+    ASSERT_TRUE(CreateTemporaryFile(&(temp_files[i])));
+    EXPECT_TRUE(PathExists(temp_files[i]));
+    EXPECT_FALSE(DirectoryExists(temp_files[i]));
+  }
+  for (int i = 0; i < 3; i++)
+    EXPECT_FALSE(temp_files[i] == temp_files[(i+1)%3]);
+  for (int i = 0; i < 3; i++)
+    EXPECT_TRUE(DeleteFile(temp_files[i], false));
+}
+
+TEST_F(FileUtilTest, CreateAndOpenTemporaryFileTest) {
+  FilePath names[3];
+  FILE* fps[3];
+  int i;
+
+  // Create; make sure they are open and exist.
+  for (i = 0; i < 3; ++i) {
+    fps[i] = CreateAndOpenTemporaryFile(&(names[i]));
+    ASSERT_TRUE(fps[i]);
+    EXPECT_TRUE(PathExists(names[i]));
+  }
+
+  // Make sure all names are unique.
+  for (i = 0; i < 3; ++i) {
+    EXPECT_FALSE(names[i] == names[(i+1)%3]);
+  }
+
+  // Close and delete.
+  for (i = 0; i < 3; ++i) {
+    EXPECT_TRUE(CloseFile(fps[i]));
+    EXPECT_TRUE(DeleteFile(names[i], false));
+  }
+}
+
+#if defined(OS_FUCHSIA)
+// TODO(crbug.com/851747): Re-enable when the Fuchsia-side fix for fdopen has
+// been rolled into Chromium.
+#define MAYBE_FileToFILE DISABLED_FileToFILE
+#else
+#define MAYBE_FileToFILE FileToFILE
+#endif
+TEST_F(FileUtilTest, MAYBE_FileToFILE) {
+  File file;
+  FILE* stream = FileToFILE(std::move(file), "w");
+  EXPECT_FALSE(stream);
+
+  FilePath file_name = temp_dir_.GetPath().Append(FPL("The file.txt"));
+  file = File(file_name, File::FLAG_CREATE | File::FLAG_WRITE);
+  EXPECT_TRUE(file.IsValid());
+
+  stream = FileToFILE(std::move(file), "w");
+  EXPECT_TRUE(stream);
+  EXPECT_FALSE(file.IsValid());
+  EXPECT_TRUE(CloseFile(stream));
+}
+
+TEST_F(FileUtilTest, CreateNewTempDirectoryTest) {
+  FilePath temp_dir;
+  ASSERT_TRUE(CreateNewTempDirectory(FilePath::StringType(), &temp_dir));
+  EXPECT_TRUE(PathExists(temp_dir));
+  EXPECT_TRUE(DeleteFile(temp_dir, false));
+}
+
+TEST_F(FileUtilTest, CreateNewTemporaryDirInDirTest) {
+  FilePath new_dir;
+  ASSERT_TRUE(CreateTemporaryDirInDir(
+      temp_dir_.GetPath(), FILE_PATH_LITERAL("CreateNewTemporaryDirInDirTest"),
+      &new_dir));
+  EXPECT_TRUE(PathExists(new_dir));
+  EXPECT_TRUE(temp_dir_.GetPath().IsParent(new_dir));
+  EXPECT_TRUE(DeleteFile(new_dir, false));
+}
+
+#if defined(OS_POSIX) || defined(OS_FUCHSIA)
+TEST_F(FileUtilTest, GetShmemTempDirTest) {
+  FilePath dir;
+  EXPECT_TRUE(GetShmemTempDir(false, &dir));
+  EXPECT_TRUE(DirectoryExists(dir));
+}
+#endif
+
+TEST_F(FileUtilTest, GetHomeDirTest) {
+#if !defined(OS_ANDROID)  // Not implemented on Android.
+  // We don't actually know what the home directory is supposed to be without
+  // calling some OS functions which would just duplicate the implementation.
+  // So here we just test that it returns something "reasonable".
+  FilePath home = GetHomeDir();
+  ASSERT_FALSE(home.empty());
+  ASSERT_TRUE(home.IsAbsolute());
+#endif
+}
+
+TEST_F(FileUtilTest, CreateDirectoryTest) {
+  FilePath test_root =
+      temp_dir_.GetPath().Append(FILE_PATH_LITERAL("create_directory_test"));
+#if defined(OS_WIN)
+  FilePath test_path =
+      test_root.Append(FILE_PATH_LITERAL("dir\\tree\\likely\\doesnt\\exist\\"));
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+  FilePath test_path =
+      test_root.Append(FILE_PATH_LITERAL("dir/tree/likely/doesnt/exist/"));
+#endif
+
+  EXPECT_FALSE(PathExists(test_path));
+  EXPECT_TRUE(CreateDirectory(test_path));
+  EXPECT_TRUE(PathExists(test_path));
+  // CreateDirectory returns true if the DirectoryExists returns true.
+  EXPECT_TRUE(CreateDirectory(test_path));
+
+  // Doesn't work to create it on top of a non-dir
+  test_path = test_path.Append(FILE_PATH_LITERAL("foobar.txt"));
+  EXPECT_FALSE(PathExists(test_path));
+  CreateTextFile(test_path, L"test file");
+  EXPECT_TRUE(PathExists(test_path));
+  EXPECT_FALSE(CreateDirectory(test_path));
+
+  EXPECT_TRUE(DeleteFile(test_root, true));
+  EXPECT_FALSE(PathExists(test_root));
+  EXPECT_FALSE(PathExists(test_path));
+
+  // Verify assumptions made by the Windows implementation:
+  // 1. The current directory always exists.
+  // 2. The root directory always exists.
+  ASSERT_TRUE(DirectoryExists(FilePath(FilePath::kCurrentDirectory)));
+  FilePath top_level = test_root;
+  while (top_level != top_level.DirName()) {
+    top_level = top_level.DirName();
+  }
+  ASSERT_TRUE(DirectoryExists(top_level));
+
+  // Given these assumptions hold, it should be safe to
+  // test that "creating" these directories succeeds.
+  EXPECT_TRUE(CreateDirectory(
+      FilePath(FilePath::kCurrentDirectory)));
+  EXPECT_TRUE(CreateDirectory(top_level));
+
+#if defined(OS_WIN)
+  FilePath invalid_drive(FILE_PATH_LITERAL("o:\\"));
+  FilePath invalid_path =
+      invalid_drive.Append(FILE_PATH_LITERAL("some\\inaccessible\\dir"));
+  if (!PathExists(invalid_drive)) {
+    EXPECT_FALSE(CreateDirectory(invalid_path));
+  }
+#endif
+}
+
+TEST_F(FileUtilTest, DetectDirectoryTest) {
+  // Check a directory
+  FilePath test_root =
+      temp_dir_.GetPath().Append(FILE_PATH_LITERAL("detect_directory_test"));
+  EXPECT_FALSE(PathExists(test_root));
+  EXPECT_TRUE(CreateDirectory(test_root));
+  EXPECT_TRUE(PathExists(test_root));
+  EXPECT_TRUE(DirectoryExists(test_root));
+  // Check a file
+  FilePath test_path =
+      test_root.Append(FILE_PATH_LITERAL("foobar.txt"));
+  EXPECT_FALSE(PathExists(test_path));
+  CreateTextFile(test_path, L"test file");
+  EXPECT_TRUE(PathExists(test_path));
+  EXPECT_FALSE(DirectoryExists(test_path));
+  EXPECT_TRUE(DeleteFile(test_path, false));
+
+  EXPECT_TRUE(DeleteFile(test_root, true));
+}
+
+TEST_F(FileUtilTest, FileEnumeratorTest) {
+  // Test an empty directory.
+  FileEnumerator f0(temp_dir_.GetPath(), true, FILES_AND_DIRECTORIES);
+  EXPECT_EQ(FPL(""), f0.Next().value());
+  EXPECT_EQ(FPL(""), f0.Next().value());
+
+  // Test an empty directory, non-recursively, including "..".
+  FileEnumerator f0_dotdot(
+      temp_dir_.GetPath(), false,
+      FILES_AND_DIRECTORIES | FileEnumerator::INCLUDE_DOT_DOT);
+  EXPECT_EQ(temp_dir_.GetPath().Append(FPL("..")).value(),
+            f0_dotdot.Next().value());
+  EXPECT_EQ(FPL(""), f0_dotdot.Next().value());
+
+  // create the directories
+  FilePath dir1 = temp_dir_.GetPath().Append(FPL("dir1"));
+  EXPECT_TRUE(CreateDirectory(dir1));
+  FilePath dir2 = temp_dir_.GetPath().Append(FPL("dir2"));
+  EXPECT_TRUE(CreateDirectory(dir2));
+  FilePath dir2inner = dir2.Append(FPL("inner"));
+  EXPECT_TRUE(CreateDirectory(dir2inner));
+
+  // create the files
+  FilePath dir2file = dir2.Append(FPL("dir2file.txt"));
+  CreateTextFile(dir2file, std::wstring());
+  FilePath dir2innerfile = dir2inner.Append(FPL("innerfile.txt"));
+  CreateTextFile(dir2innerfile, std::wstring());
+  FilePath file1 = temp_dir_.GetPath().Append(FPL("file1.txt"));
+  CreateTextFile(file1, std::wstring());
+  FilePath file2_rel = dir2.Append(FilePath::kParentDirectory)
+      .Append(FPL("file2.txt"));
+  CreateTextFile(file2_rel, std::wstring());
+  FilePath file2_abs = temp_dir_.GetPath().Append(FPL("file2.txt"));
+
+  // Only enumerate files.
+  FileEnumerator f1(temp_dir_.GetPath(), true, FileEnumerator::FILES);
+  FindResultCollector c1(&f1);
+  EXPECT_TRUE(c1.HasFile(file1));
+  EXPECT_TRUE(c1.HasFile(file2_abs));
+  EXPECT_TRUE(c1.HasFile(dir2file));
+  EXPECT_TRUE(c1.HasFile(dir2innerfile));
+  EXPECT_EQ(4, c1.size());
+
+  // Only enumerate directories.
+  FileEnumerator f2(temp_dir_.GetPath(), true, FileEnumerator::DIRECTORIES);
+  FindResultCollector c2(&f2);
+  EXPECT_TRUE(c2.HasFile(dir1));
+  EXPECT_TRUE(c2.HasFile(dir2));
+  EXPECT_TRUE(c2.HasFile(dir2inner));
+  EXPECT_EQ(3, c2.size());
+
+  // Only enumerate directories non-recursively.
+  FileEnumerator f2_non_recursive(temp_dir_.GetPath(), false,
+                                  FileEnumerator::DIRECTORIES);
+  FindResultCollector c2_non_recursive(&f2_non_recursive);
+  EXPECT_TRUE(c2_non_recursive.HasFile(dir1));
+  EXPECT_TRUE(c2_non_recursive.HasFile(dir2));
+  EXPECT_EQ(2, c2_non_recursive.size());
+
+  // Only enumerate directories, non-recursively, including "..".
+  FileEnumerator f2_dotdot(
+      temp_dir_.GetPath(), false,
+      FileEnumerator::DIRECTORIES | FileEnumerator::INCLUDE_DOT_DOT);
+  FindResultCollector c2_dotdot(&f2_dotdot);
+  EXPECT_TRUE(c2_dotdot.HasFile(dir1));
+  EXPECT_TRUE(c2_dotdot.HasFile(dir2));
+  EXPECT_TRUE(c2_dotdot.HasFile(temp_dir_.GetPath().Append(FPL(".."))));
+  EXPECT_EQ(3, c2_dotdot.size());
+
+  // Enumerate files and directories.
+  FileEnumerator f3(temp_dir_.GetPath(), true, FILES_AND_DIRECTORIES);
+  FindResultCollector c3(&f3);
+  EXPECT_TRUE(c3.HasFile(dir1));
+  EXPECT_TRUE(c3.HasFile(dir2));
+  EXPECT_TRUE(c3.HasFile(file1));
+  EXPECT_TRUE(c3.HasFile(file2_abs));
+  EXPECT_TRUE(c3.HasFile(dir2file));
+  EXPECT_TRUE(c3.HasFile(dir2inner));
+  EXPECT_TRUE(c3.HasFile(dir2innerfile));
+  EXPECT_EQ(7, c3.size());
+
+  // Non-recursive operation.
+  FileEnumerator f4(temp_dir_.GetPath(), false, FILES_AND_DIRECTORIES);
+  FindResultCollector c4(&f4);
+  EXPECT_TRUE(c4.HasFile(dir2));
+  EXPECT_TRUE(c4.HasFile(dir2));
+  EXPECT_TRUE(c4.HasFile(file1));
+  EXPECT_TRUE(c4.HasFile(file2_abs));
+  EXPECT_EQ(4, c4.size());
+
+  // Enumerate with a pattern.
+  FileEnumerator f5(temp_dir_.GetPath(), true, FILES_AND_DIRECTORIES,
+                    FPL("dir*"));
+  FindResultCollector c5(&f5);
+  EXPECT_TRUE(c5.HasFile(dir1));
+  EXPECT_TRUE(c5.HasFile(dir2));
+  EXPECT_TRUE(c5.HasFile(dir2file));
+  EXPECT_TRUE(c5.HasFile(dir2inner));
+  EXPECT_TRUE(c5.HasFile(dir2innerfile));
+  EXPECT_EQ(5, c5.size());
+
+#if defined(OS_WIN)
+  {
+    // Make dir1 point to dir2.
+    ReparsePoint reparse_point(dir1, dir2);
+    EXPECT_TRUE(reparse_point.IsValid());
+
+    // There can be a delay for the enumeration code to see the change on
+    // the file system so skip this test for XP.
+    // Enumerate the reparse point.
+    FileEnumerator f6(dir1, true, FILES_AND_DIRECTORIES);
+    FindResultCollector c6(&f6);
+    FilePath inner2 = dir1.Append(FPL("inner"));
+    EXPECT_TRUE(c6.HasFile(inner2));
+    EXPECT_TRUE(c6.HasFile(inner2.Append(FPL("innerfile.txt"))));
+    EXPECT_TRUE(c6.HasFile(dir1.Append(FPL("dir2file.txt"))));
+    EXPECT_EQ(3, c6.size());
+
+    // No changes for non recursive operation.
+    FileEnumerator f7(temp_dir_.GetPath(), false, FILES_AND_DIRECTORIES);
+    FindResultCollector c7(&f7);
+    EXPECT_TRUE(c7.HasFile(dir2));
+    EXPECT_TRUE(c7.HasFile(dir2));
+    EXPECT_TRUE(c7.HasFile(file1));
+    EXPECT_TRUE(c7.HasFile(file2_abs));
+    EXPECT_EQ(4, c7.size());
+
+    // Should not enumerate inside dir1 when using recursion.
+    FileEnumerator f8(temp_dir_.GetPath(), true, FILES_AND_DIRECTORIES);
+    FindResultCollector c8(&f8);
+    EXPECT_TRUE(c8.HasFile(dir1));
+    EXPECT_TRUE(c8.HasFile(dir2));
+    EXPECT_TRUE(c8.HasFile(file1));
+    EXPECT_TRUE(c8.HasFile(file2_abs));
+    EXPECT_TRUE(c8.HasFile(dir2file));
+    EXPECT_TRUE(c8.HasFile(dir2inner));
+    EXPECT_TRUE(c8.HasFile(dir2innerfile));
+    EXPECT_EQ(7, c8.size());
+  }
+#endif
+
+  // Make sure the destructor closes the find handle while in the middle of a
+  // query to allow TearDown to delete the directory.
+  FileEnumerator f9(temp_dir_.GetPath(), true, FILES_AND_DIRECTORIES);
+  EXPECT_FALSE(f9.Next().value().empty());  // Should have found something
+                                            // (we don't care what).
+}
+
+TEST_F(FileUtilTest, AppendToFile) {
+  FilePath data_dir =
+      temp_dir_.GetPath().Append(FILE_PATH_LITERAL("FilePathTest"));
+
+  // Create a fresh, empty copy of this directory.
+  if (PathExists(data_dir)) {
+    ASSERT_TRUE(DeleteFile(data_dir, true));
+  }
+  ASSERT_TRUE(CreateDirectory(data_dir));
+
+  // Create a fresh, empty copy of this directory.
+  if (PathExists(data_dir)) {
+    ASSERT_TRUE(DeleteFile(data_dir, true));
+  }
+  ASSERT_TRUE(CreateDirectory(data_dir));
+  FilePath foobar(data_dir.Append(FILE_PATH_LITERAL("foobar.txt")));
+
+  std::string data("hello");
+  EXPECT_FALSE(AppendToFile(foobar, data.c_str(), data.size()));
+  EXPECT_EQ(static_cast<int>(data.length()),
+            WriteFile(foobar, data.c_str(), data.length()));
+  EXPECT_TRUE(AppendToFile(foobar, data.c_str(), data.size()));
+
+  const std::wstring read_content = ReadTextFile(foobar);
+  EXPECT_EQ(L"hellohello", read_content);
+}
+
+TEST_F(FileUtilTest, ReadFile) {
+  // Create a test file to be read.
+  const std::string kTestData("The quick brown fox jumps over the lazy dog.");
+  FilePath file_path =
+      temp_dir_.GetPath().Append(FILE_PATH_LITERAL("ReadFileTest"));
+
+  ASSERT_EQ(static_cast<int>(kTestData.size()),
+            WriteFile(file_path, kTestData.data(), kTestData.size()));
+
+  // Make buffers with various size.
+  std::vector<char> small_buffer(kTestData.size() / 2);
+  std::vector<char> exact_buffer(kTestData.size());
+  std::vector<char> large_buffer(kTestData.size() * 2);
+
+  // Read the file with smaller buffer.
+  int bytes_read_small = ReadFile(
+      file_path, &small_buffer[0], static_cast<int>(small_buffer.size()));
+  EXPECT_EQ(static_cast<int>(small_buffer.size()), bytes_read_small);
+  EXPECT_EQ(
+      std::string(kTestData.begin(), kTestData.begin() + small_buffer.size()),
+      std::string(small_buffer.begin(), small_buffer.end()));
+
+  // Read the file with buffer which have exactly same size.
+  int bytes_read_exact = ReadFile(
+      file_path, &exact_buffer[0], static_cast<int>(exact_buffer.size()));
+  EXPECT_EQ(static_cast<int>(kTestData.size()), bytes_read_exact);
+  EXPECT_EQ(kTestData, std::string(exact_buffer.begin(), exact_buffer.end()));
+
+  // Read the file with larger buffer.
+  int bytes_read_large = ReadFile(
+      file_path, &large_buffer[0], static_cast<int>(large_buffer.size()));
+  EXPECT_EQ(static_cast<int>(kTestData.size()), bytes_read_large);
+  EXPECT_EQ(kTestData, std::string(large_buffer.begin(),
+                                   large_buffer.begin() + kTestData.size()));
+
+  // Make sure the return value is -1 if the file doesn't exist.
+  FilePath file_path_not_exist =
+      temp_dir_.GetPath().Append(FILE_PATH_LITERAL("ReadFileNotExistTest"));
+  EXPECT_EQ(-1,
+            ReadFile(file_path_not_exist,
+                     &exact_buffer[0],
+                     static_cast<int>(exact_buffer.size())));
+}
+
+TEST_F(FileUtilTest, ReadFileToString) {
+  const char kTestData[] = "0123";
+  std::string data;
+
+  FilePath file_path =
+      temp_dir_.GetPath().Append(FILE_PATH_LITERAL("ReadFileToStringTest"));
+  FilePath file_path_dangerous =
+      temp_dir_.GetPath()
+          .Append(FILE_PATH_LITERAL(".."))
+          .Append(temp_dir_.GetPath().BaseName())
+          .Append(FILE_PATH_LITERAL("ReadFileToStringTest"));
+
+  // Create test file.
+  ASSERT_EQ(static_cast<int>(strlen(kTestData)),
+            WriteFile(file_path, kTestData, strlen(kTestData)));
+
+  EXPECT_TRUE(ReadFileToString(file_path, &data));
+  EXPECT_EQ(kTestData, data);
+
+  data = "temp";
+  EXPECT_FALSE(ReadFileToStringWithMaxSize(file_path, &data, 0));
+  EXPECT_EQ(0u, data.length());
+
+  data = "temp";
+  EXPECT_FALSE(ReadFileToStringWithMaxSize(file_path, &data, 2));
+  EXPECT_EQ("01", data);
+
+  data = "temp";
+  EXPECT_FALSE(ReadFileToStringWithMaxSize(file_path, &data, 3));
+  EXPECT_EQ("012", data);
+
+  data = "temp";
+  EXPECT_TRUE(ReadFileToStringWithMaxSize(file_path, &data, 4));
+  EXPECT_EQ("0123", data);
+
+  data = "temp";
+  EXPECT_TRUE(ReadFileToStringWithMaxSize(file_path, &data, 6));
+  EXPECT_EQ("0123", data);
+
+  EXPECT_TRUE(ReadFileToStringWithMaxSize(file_path, nullptr, 6));
+
+  EXPECT_TRUE(ReadFileToString(file_path, nullptr));
+
+  data = "temp";
+  EXPECT_FALSE(ReadFileToString(file_path_dangerous, &data));
+  EXPECT_EQ(0u, data.length());
+
+  // Delete test file.
+  EXPECT_TRUE(DeleteFile(file_path, false));
+
+  data = "temp";
+  EXPECT_FALSE(ReadFileToString(file_path, &data));
+  EXPECT_EQ(0u, data.length());
+
+  data = "temp";
+  EXPECT_FALSE(ReadFileToStringWithMaxSize(file_path, &data, 6));
+  EXPECT_EQ(0u, data.length());
+}
+
+#if !defined(OS_WIN)
+TEST_F(FileUtilTest, ReadFileToStringWithUnknownFileSize) {
+  FilePath file_path("/dev/zero");
+  std::string data = "temp";
+
+  EXPECT_FALSE(ReadFileToStringWithMaxSize(file_path, &data, 0));
+  EXPECT_EQ(0u, data.length());
+
+  data = "temp";
+  EXPECT_FALSE(ReadFileToStringWithMaxSize(file_path, &data, 2));
+  EXPECT_EQ(std::string(2, '\0'), data);
+
+  EXPECT_FALSE(ReadFileToStringWithMaxSize(file_path, nullptr, 6));
+
+  // Read more than buffer size.
+  data = "temp";
+  EXPECT_FALSE(ReadFileToStringWithMaxSize(file_path, &data, kLargeFileSize));
+  EXPECT_EQ(kLargeFileSize, data.length());
+  EXPECT_EQ(std::string(kLargeFileSize, '\0'), data);
+
+  EXPECT_FALSE(ReadFileToStringWithMaxSize(file_path, nullptr, kLargeFileSize));
+}
+#endif  // !defined(OS_WIN)
+
+#if !defined(OS_WIN) && !defined(OS_NACL) && !defined(OS_FUCHSIA) && \
+    !defined(OS_IOS)
+#define ChildMain WriteToPipeChildMain
+#define ChildMainString "WriteToPipeChildMain"
+
+MULTIPROCESS_TEST_MAIN(ChildMain) {
+  const char kTestData[] = "0123";
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+  const FilePath pipe_path = command_line->GetSwitchValuePath("pipe-path");
+
+  int fd = open(pipe_path.value().c_str(), O_WRONLY);
+  CHECK_NE(-1, fd);
+  size_t written = 0;
+  while (written < strlen(kTestData)) {
+    ssize_t res = write(fd, kTestData + written, strlen(kTestData) - written);
+    if (res == -1)
+      break;
+    written += res;
+  }
+  CHECK_EQ(strlen(kTestData), written);
+  CHECK_EQ(0, close(fd));
+  return 0;
+}
+
+#define MoreThanBufferSizeChildMain WriteToPipeMoreThanBufferSizeChildMain
+#define MoreThanBufferSizeChildMainString \
+  "WriteToPipeMoreThanBufferSizeChildMain"
+
+MULTIPROCESS_TEST_MAIN(MoreThanBufferSizeChildMain) {
+  std::string data(kLargeFileSize, 'c');
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+  const FilePath pipe_path = command_line->GetSwitchValuePath("pipe-path");
+
+  int fd = open(pipe_path.value().c_str(), O_WRONLY);
+  CHECK_NE(-1, fd);
+
+  size_t written = 0;
+  while (written < data.size()) {
+    ssize_t res = write(fd, data.c_str() + written, data.size() - written);
+    if (res == -1) {
+      // We are unable to write because reading process has already read
+      // requested number of bytes and closed pipe.
+      break;
+    }
+    written += res;
+  }
+  CHECK_EQ(0, close(fd));
+  return 0;
+}
+
+TEST_F(FileUtilTest, ReadFileToStringWithNamedPipe) {
+  FilePath pipe_path =
+      temp_dir_.GetPath().Append(FILE_PATH_LITERAL("test_pipe"));
+  ASSERT_EQ(0, mkfifo(pipe_path.value().c_str(), 0600));
+
+  base::CommandLine child_command_line(
+      base::GetMultiProcessTestChildBaseCommandLine());
+  child_command_line.AppendSwitchPath("pipe-path", pipe_path);
+
+  {
+    base::Process child_process = base::SpawnMultiProcessTestChild(
+        ChildMainString, child_command_line, base::LaunchOptions());
+    ASSERT_TRUE(child_process.IsValid());
+
+    std::string data = "temp";
+    EXPECT_FALSE(ReadFileToStringWithMaxSize(pipe_path, &data, 2));
+    EXPECT_EQ("01", data);
+
+    int rv = -1;
+    ASSERT_TRUE(WaitForMultiprocessTestChildExit(
+        child_process, TestTimeouts::action_timeout(), &rv));
+    ASSERT_EQ(0, rv);
+  }
+  {
+    base::Process child_process = base::SpawnMultiProcessTestChild(
+        ChildMainString, child_command_line, base::LaunchOptions());
+    ASSERT_TRUE(child_process.IsValid());
+
+    std::string data = "temp";
+    EXPECT_TRUE(ReadFileToStringWithMaxSize(pipe_path, &data, 6));
+    EXPECT_EQ("0123", data);
+
+    int rv = -1;
+    ASSERT_TRUE(WaitForMultiprocessTestChildExit(
+        child_process, TestTimeouts::action_timeout(), &rv));
+    ASSERT_EQ(0, rv);
+  }
+  {
+    base::Process child_process = base::SpawnMultiProcessTestChild(
+        MoreThanBufferSizeChildMainString, child_command_line,
+        base::LaunchOptions());
+    ASSERT_TRUE(child_process.IsValid());
+
+    std::string data = "temp";
+    EXPECT_FALSE(ReadFileToStringWithMaxSize(pipe_path, &data, 6));
+    EXPECT_EQ("cccccc", data);
+
+    int rv = -1;
+    ASSERT_TRUE(WaitForMultiprocessTestChildExit(
+        child_process, TestTimeouts::action_timeout(), &rv));
+    ASSERT_EQ(0, rv);
+  }
+  {
+    base::Process child_process = base::SpawnMultiProcessTestChild(
+        MoreThanBufferSizeChildMainString, child_command_line,
+        base::LaunchOptions());
+    ASSERT_TRUE(child_process.IsValid());
+
+    std::string data = "temp";
+    EXPECT_FALSE(
+        ReadFileToStringWithMaxSize(pipe_path, &data, kLargeFileSize - 1));
+    EXPECT_EQ(std::string(kLargeFileSize - 1, 'c'), data);
+
+    int rv = -1;
+    ASSERT_TRUE(WaitForMultiprocessTestChildExit(
+        child_process, TestTimeouts::action_timeout(), &rv));
+    ASSERT_EQ(0, rv);
+  }
+  {
+    base::Process child_process = base::SpawnMultiProcessTestChild(
+        MoreThanBufferSizeChildMainString, child_command_line,
+        base::LaunchOptions());
+    ASSERT_TRUE(child_process.IsValid());
+
+    std::string data = "temp";
+    EXPECT_TRUE(ReadFileToStringWithMaxSize(pipe_path, &data, kLargeFileSize));
+    EXPECT_EQ(std::string(kLargeFileSize, 'c'), data);
+
+    int rv = -1;
+    ASSERT_TRUE(WaitForMultiprocessTestChildExit(
+        child_process, TestTimeouts::action_timeout(), &rv));
+    ASSERT_EQ(0, rv);
+  }
+  {
+    base::Process child_process = base::SpawnMultiProcessTestChild(
+        MoreThanBufferSizeChildMainString, child_command_line,
+        base::LaunchOptions());
+    ASSERT_TRUE(child_process.IsValid());
+
+    std::string data = "temp";
+    EXPECT_TRUE(
+        ReadFileToStringWithMaxSize(pipe_path, &data, kLargeFileSize * 5));
+    EXPECT_EQ(std::string(kLargeFileSize, 'c'), data);
+
+    int rv = -1;
+    ASSERT_TRUE(WaitForMultiprocessTestChildExit(
+        child_process, TestTimeouts::action_timeout(), &rv));
+    ASSERT_EQ(0, rv);
+  }
+
+  ASSERT_EQ(0, unlink(pipe_path.value().c_str()));
+}
+#endif  // !defined(OS_WIN) && !defined(OS_NACL) && !defined(OS_FUCHSIA) &&
+        // !defined(OS_IOS)
+
+#if defined(OS_WIN)
+#define ChildMain WriteToPipeChildMain
+#define ChildMainString "WriteToPipeChildMain"
+
+MULTIPROCESS_TEST_MAIN(ChildMain) {
+  const char kTestData[] = "0123";
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+  const FilePath pipe_path = command_line->GetSwitchValuePath("pipe-path");
+  std::string switch_string = command_line->GetSwitchValueASCII("sync_event");
+  EXPECT_FALSE(switch_string.empty());
+  unsigned int switch_uint = 0;
+  EXPECT_TRUE(StringToUint(switch_string, &switch_uint));
+  win::ScopedHandle sync_event(win::Uint32ToHandle(switch_uint));
+
+  HANDLE ph = CreateNamedPipe(pipe_path.value().c_str(), PIPE_ACCESS_OUTBOUND,
+                              PIPE_WAIT, 1, 0, 0, 0, NULL);
+  EXPECT_NE(ph, INVALID_HANDLE_VALUE);
+  EXPECT_TRUE(SetEvent(sync_event.Get()));
+  EXPECT_TRUE(ConnectNamedPipe(ph, NULL));
+
+  DWORD written;
+  EXPECT_TRUE(::WriteFile(ph, kTestData, strlen(kTestData), &written, NULL));
+  EXPECT_EQ(strlen(kTestData), written);
+  CloseHandle(ph);
+  return 0;
+}
+
+#define MoreThanBufferSizeChildMain WriteToPipeMoreThanBufferSizeChildMain
+#define MoreThanBufferSizeChildMainString \
+  "WriteToPipeMoreThanBufferSizeChildMain"
+
+MULTIPROCESS_TEST_MAIN(MoreThanBufferSizeChildMain) {
+  std::string data(kLargeFileSize, 'c');
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+  const FilePath pipe_path = command_line->GetSwitchValuePath("pipe-path");
+  std::string switch_string = command_line->GetSwitchValueASCII("sync_event");
+  EXPECT_FALSE(switch_string.empty());
+  unsigned int switch_uint = 0;
+  EXPECT_TRUE(StringToUint(switch_string, &switch_uint));
+  win::ScopedHandle sync_event(win::Uint32ToHandle(switch_uint));
+
+  HANDLE ph = CreateNamedPipe(pipe_path.value().c_str(), PIPE_ACCESS_OUTBOUND,
+                              PIPE_WAIT, 1, data.size(), data.size(), 0, NULL);
+  EXPECT_NE(ph, INVALID_HANDLE_VALUE);
+  EXPECT_TRUE(SetEvent(sync_event.Get()));
+  EXPECT_TRUE(ConnectNamedPipe(ph, NULL));
+
+  DWORD written;
+  EXPECT_TRUE(::WriteFile(ph, data.c_str(), data.size(), &written, NULL));
+  EXPECT_EQ(data.size(), written);
+  CloseHandle(ph);
+  return 0;
+}
+
+TEST_F(FileUtilTest, ReadFileToStringWithNamedPipe) {
+  FilePath pipe_path(FILE_PATH_LITERAL("\\\\.\\pipe\\test_pipe"));
+  win::ScopedHandle sync_event(CreateEvent(0, false, false, nullptr));
+
+  base::CommandLine child_command_line(
+      base::GetMultiProcessTestChildBaseCommandLine());
+  child_command_line.AppendSwitchPath("pipe-path", pipe_path);
+  child_command_line.AppendSwitchASCII(
+      "sync_event", UintToString(win::HandleToUint32(sync_event.Get())));
+
+  base::LaunchOptions options;
+  options.handles_to_inherit.push_back(sync_event.Get());
+
+  {
+    base::Process child_process = base::SpawnMultiProcessTestChild(
+        ChildMainString, child_command_line, options);
+    ASSERT_TRUE(child_process.IsValid());
+    // Wait for pipe creation in child process.
+    EXPECT_EQ(WAIT_OBJECT_0, WaitForSingleObject(sync_event.Get(), INFINITE));
+
+    std::string data = "temp";
+    EXPECT_FALSE(ReadFileToStringWithMaxSize(pipe_path, &data, 2));
+    EXPECT_EQ("01", data);
+
+    int rv = -1;
+    ASSERT_TRUE(WaitForMultiprocessTestChildExit(
+        child_process, TestTimeouts::action_timeout(), &rv));
+    ASSERT_EQ(0, rv);
+  }
+  {
+    base::Process child_process = base::SpawnMultiProcessTestChild(
+        ChildMainString, child_command_line, options);
+    ASSERT_TRUE(child_process.IsValid());
+    // Wait for pipe creation in child process.
+    EXPECT_EQ(WAIT_OBJECT_0, WaitForSingleObject(sync_event.Get(), INFINITE));
+
+    std::string data = "temp";
+    EXPECT_TRUE(ReadFileToStringWithMaxSize(pipe_path, &data, 6));
+    EXPECT_EQ("0123", data);
+
+    int rv = -1;
+    ASSERT_TRUE(WaitForMultiprocessTestChildExit(
+        child_process, TestTimeouts::action_timeout(), &rv));
+    ASSERT_EQ(0, rv);
+  }
+  {
+    base::Process child_process = base::SpawnMultiProcessTestChild(
+        MoreThanBufferSizeChildMainString, child_command_line, options);
+    ASSERT_TRUE(child_process.IsValid());
+    // Wait for pipe creation in child process.
+    EXPECT_EQ(WAIT_OBJECT_0, WaitForSingleObject(sync_event.Get(), INFINITE));
+
+    std::string data = "temp";
+    EXPECT_FALSE(ReadFileToStringWithMaxSize(pipe_path, &data, 6));
+    EXPECT_EQ("cccccc", data);
+
+    int rv = -1;
+    ASSERT_TRUE(WaitForMultiprocessTestChildExit(
+        child_process, TestTimeouts::action_timeout(), &rv));
+    ASSERT_EQ(0, rv);
+  }
+  {
+    base::Process child_process = base::SpawnMultiProcessTestChild(
+        MoreThanBufferSizeChildMainString, child_command_line, options);
+    ASSERT_TRUE(child_process.IsValid());
+    // Wait for pipe creation in child process.
+    EXPECT_EQ(WAIT_OBJECT_0, WaitForSingleObject(sync_event.Get(), INFINITE));
+
+    std::string data = "temp";
+    EXPECT_FALSE(
+        ReadFileToStringWithMaxSize(pipe_path, &data, kLargeFileSize - 1));
+    EXPECT_EQ(std::string(kLargeFileSize - 1, 'c'), data);
+
+    int rv = -1;
+    ASSERT_TRUE(WaitForMultiprocessTestChildExit(
+        child_process, TestTimeouts::action_timeout(), &rv));
+    ASSERT_EQ(0, rv);
+  }
+  {
+    base::Process child_process = base::SpawnMultiProcessTestChild(
+        MoreThanBufferSizeChildMainString, child_command_line, options);
+    ASSERT_TRUE(child_process.IsValid());
+    // Wait for pipe creation in child process.
+    EXPECT_EQ(WAIT_OBJECT_0, WaitForSingleObject(sync_event.Get(), INFINITE));
+
+    std::string data = "temp";
+    EXPECT_TRUE(ReadFileToStringWithMaxSize(pipe_path, &data, kLargeFileSize));
+    EXPECT_EQ(std::string(kLargeFileSize, 'c'), data);
+
+    int rv = -1;
+    ASSERT_TRUE(WaitForMultiprocessTestChildExit(
+        child_process, TestTimeouts::action_timeout(), &rv));
+    ASSERT_EQ(0, rv);
+  }
+  {
+    base::Process child_process = base::SpawnMultiProcessTestChild(
+        MoreThanBufferSizeChildMainString, child_command_line, options);
+    ASSERT_TRUE(child_process.IsValid());
+    // Wait for pipe creation in child process.
+    EXPECT_EQ(WAIT_OBJECT_0, WaitForSingleObject(sync_event.Get(), INFINITE));
+
+    std::string data = "temp";
+    EXPECT_TRUE(
+        ReadFileToStringWithMaxSize(pipe_path, &data, kLargeFileSize * 5));
+    EXPECT_EQ(std::string(kLargeFileSize, 'c'), data);
+
+    int rv = -1;
+    ASSERT_TRUE(WaitForMultiprocessTestChildExit(
+        child_process, TestTimeouts::action_timeout(), &rv));
+    ASSERT_EQ(0, rv);
+  }
+}
+#endif  // defined(OS_WIN)
+
+#if defined(OS_POSIX) && !defined(OS_MACOSX)
+TEST_F(FileUtilTest, ReadFileToStringWithProcFileSystem) {
+  FilePath file_path("/proc/cpuinfo");
+  std::string data = "temp";
+
+  EXPECT_FALSE(ReadFileToStringWithMaxSize(file_path, &data, 0));
+  EXPECT_EQ(0u, data.length());
+
+  data = "temp";
+  EXPECT_FALSE(ReadFileToStringWithMaxSize(file_path, &data, 2));
+  EXPECT_TRUE(EqualsCaseInsensitiveASCII("pr", data));
+
+  data = "temp";
+  EXPECT_FALSE(ReadFileToStringWithMaxSize(file_path, &data, 4));
+  EXPECT_TRUE(EqualsCaseInsensitiveASCII("proc", data));
+
+  EXPECT_FALSE(ReadFileToStringWithMaxSize(file_path, nullptr, 4));
+}
+#endif  // defined(OS_POSIX) && !defined(OS_MACOSX)
+
+TEST_F(FileUtilTest, ReadFileToStringWithLargeFile) {
+  std::string data(kLargeFileSize, 'c');
+
+  FilePath file_path =
+      temp_dir_.GetPath().Append(FILE_PATH_LITERAL("ReadFileToStringTest"));
+
+  // Create test file.
+  ASSERT_EQ(static_cast<int>(kLargeFileSize),
+            WriteFile(file_path, data.c_str(), kLargeFileSize));
+
+  std::string actual_data = "temp";
+  EXPECT_TRUE(ReadFileToString(file_path, &actual_data));
+  EXPECT_EQ(data, actual_data);
+
+  actual_data = "temp";
+  EXPECT_FALSE(ReadFileToStringWithMaxSize(file_path, &actual_data, 0));
+  EXPECT_EQ(0u, actual_data.length());
+
+  // Read more than buffer size.
+  actual_data = "temp";
+  EXPECT_FALSE(
+      ReadFileToStringWithMaxSize(file_path, &actual_data, kLargeFileSize - 1));
+  EXPECT_EQ(std::string(kLargeFileSize - 1, 'c'), actual_data);
+}
+
+TEST_F(FileUtilTest, TouchFile) {
+  FilePath data_dir =
+      temp_dir_.GetPath().Append(FILE_PATH_LITERAL("FilePathTest"));
+
+  // Create a fresh, empty copy of this directory.
+  if (PathExists(data_dir)) {
+    ASSERT_TRUE(DeleteFile(data_dir, true));
+  }
+  ASSERT_TRUE(CreateDirectory(data_dir));
+
+  FilePath foobar(data_dir.Append(FILE_PATH_LITERAL("foobar.txt")));
+  std::string data("hello");
+  ASSERT_EQ(static_cast<int>(data.length()),
+            WriteFile(foobar, data.c_str(), data.length()));
+
+  Time access_time;
+  // This timestamp is divisible by one day (in local timezone),
+  // to make it work on FAT too.
+  ASSERT_TRUE(Time::FromString("Wed, 16 Nov 1994, 00:00:00",
+                               &access_time));
+
+  Time modification_time;
+  // Note that this timestamp is divisible by two (seconds) - FAT stores
+  // modification times with 2s resolution.
+  ASSERT_TRUE(Time::FromString("Tue, 15 Nov 1994, 12:45:26 GMT",
+              &modification_time));
+
+  ASSERT_TRUE(TouchFile(foobar, access_time, modification_time));
+  File::Info file_info;
+  ASSERT_TRUE(GetFileInfo(foobar, &file_info));
+#if !defined(OS_FUCHSIA)
+  // Access time is not supported on Fuchsia, see https://crbug.com/735233.
+  EXPECT_EQ(access_time.ToInternalValue(),
+            file_info.last_accessed.ToInternalValue());
+#endif
+  EXPECT_EQ(modification_time.ToInternalValue(),
+            file_info.last_modified.ToInternalValue());
+}
+
+TEST_F(FileUtilTest, IsDirectoryEmpty) {
+  FilePath empty_dir =
+      temp_dir_.GetPath().Append(FILE_PATH_LITERAL("EmptyDir"));
+
+  ASSERT_FALSE(PathExists(empty_dir));
+
+  ASSERT_TRUE(CreateDirectory(empty_dir));
+
+  EXPECT_TRUE(IsDirectoryEmpty(empty_dir));
+
+  FilePath foo(empty_dir.Append(FILE_PATH_LITERAL("foo.txt")));
+  std::string bar("baz");
+  ASSERT_EQ(static_cast<int>(bar.length()),
+            WriteFile(foo, bar.c_str(), bar.length()));
+
+  EXPECT_FALSE(IsDirectoryEmpty(empty_dir));
+}
+
+#if defined(OS_POSIX) || defined(OS_FUCHSIA)
+
+TEST_F(FileUtilTest, SetNonBlocking) {
+  const int kInvalidFd = 99999;
+  EXPECT_FALSE(SetNonBlocking(kInvalidFd));
+
+  base::FilePath path;
+  ASSERT_TRUE(PathService::Get(base::DIR_TEST_DATA, &path));
+  path = path.Append(FPL("file_util")).Append(FPL("original.txt"));
+  ScopedFD fd(open(path.value().c_str(), O_RDONLY));
+  ASSERT_GE(fd.get(), 0);
+  EXPECT_TRUE(SetNonBlocking(fd.get()));
+}
+
+TEST_F(FileUtilTest, SetCloseOnExec) {
+  const int kInvalidFd = 99999;
+  EXPECT_FALSE(SetCloseOnExec(kInvalidFd));
+
+  base::FilePath path;
+  ASSERT_TRUE(PathService::Get(base::DIR_TEST_DATA, &path));
+  path = path.Append(FPL("file_util")).Append(FPL("original.txt"));
+  ScopedFD fd(open(path.value().c_str(), O_RDONLY));
+  ASSERT_GE(fd.get(), 0);
+  EXPECT_TRUE(SetCloseOnExec(fd.get()));
+}
+
+#endif
+
+#if defined(OS_POSIX)
+
+// Testing VerifyPathControlledByAdmin() is hard, because there is no
+// way a test can make a file owned by root, or change file paths
+// at the root of the file system.  VerifyPathControlledByAdmin()
+// is implemented as a call to VerifyPathControlledByUser, which gives
+// us the ability to test with paths under the test's temp directory,
+// using a user id we control.
+// Pull tests of VerifyPathControlledByUserTest() into a separate test class
+// with a common SetUp() method.
+class VerifyPathControlledByUserTest : public FileUtilTest {
+ protected:
+  void SetUp() override {
+    FileUtilTest::SetUp();
+
+    // Create a basic structure used by each test.
+    // base_dir_
+    //  |-> sub_dir_
+    //       |-> text_file_
+
+    base_dir_ = temp_dir_.GetPath().AppendASCII("base_dir");
+    ASSERT_TRUE(CreateDirectory(base_dir_));
+
+    sub_dir_ = base_dir_.AppendASCII("sub_dir");
+    ASSERT_TRUE(CreateDirectory(sub_dir_));
+
+    text_file_ = sub_dir_.AppendASCII("file.txt");
+    CreateTextFile(text_file_, L"This text file has some text in it.");
+
+    // Get the user and group files are created with from |base_dir_|.
+    struct stat stat_buf;
+    ASSERT_EQ(0, stat(base_dir_.value().c_str(), &stat_buf));
+    uid_ = stat_buf.st_uid;
+    ok_gids_.insert(stat_buf.st_gid);
+    bad_gids_.insert(stat_buf.st_gid + 1);
+
+    ASSERT_EQ(uid_, getuid());  // This process should be the owner.
+
+    // To ensure that umask settings do not cause the initial state
+    // of permissions to be different from what we expect, explicitly
+    // set permissions on the directories we create.
+    // Make all files and directories non-world-writable.
+
+    // Users and group can read, write, traverse
+    int enabled_permissions =
+        FILE_PERMISSION_USER_MASK | FILE_PERMISSION_GROUP_MASK;
+    // Other users can't read, write, traverse
+    int disabled_permissions = FILE_PERMISSION_OTHERS_MASK;
+
+    ASSERT_NO_FATAL_FAILURE(
+        ChangePosixFilePermissions(
+            base_dir_, enabled_permissions, disabled_permissions));
+    ASSERT_NO_FATAL_FAILURE(
+        ChangePosixFilePermissions(
+            sub_dir_, enabled_permissions, disabled_permissions));
+  }
+
+  FilePath base_dir_;
+  FilePath sub_dir_;
+  FilePath text_file_;
+  uid_t uid_;
+
+  std::set<gid_t> ok_gids_;
+  std::set<gid_t> bad_gids_;
+};
+
+TEST_F(VerifyPathControlledByUserTest, BadPaths) {
+  // File does not exist.
+  FilePath does_not_exist = base_dir_.AppendASCII("does")
+                                     .AppendASCII("not")
+                                     .AppendASCII("exist");
+  EXPECT_FALSE(
+      VerifyPathControlledByUser(base_dir_, does_not_exist, uid_, ok_gids_));
+
+  // |base| not a subpath of |path|.
+  EXPECT_FALSE(VerifyPathControlledByUser(sub_dir_, base_dir_, uid_, ok_gids_));
+
+  // An empty base path will fail to be a prefix for any path.
+  FilePath empty;
+  EXPECT_FALSE(VerifyPathControlledByUser(empty, base_dir_, uid_, ok_gids_));
+
+  // Finding that a bad call fails proves nothing unless a good call succeeds.
+  EXPECT_TRUE(VerifyPathControlledByUser(base_dir_, sub_dir_, uid_, ok_gids_));
+}
+
+TEST_F(VerifyPathControlledByUserTest, Symlinks) {
+  // Symlinks in the path should cause failure.
+
+  // Symlink to the file at the end of the path.
+  FilePath file_link =  base_dir_.AppendASCII("file_link");
+  ASSERT_TRUE(CreateSymbolicLink(text_file_, file_link))
+      << "Failed to create symlink.";
+
+  EXPECT_FALSE(
+      VerifyPathControlledByUser(base_dir_, file_link, uid_, ok_gids_));
+  EXPECT_FALSE(
+      VerifyPathControlledByUser(file_link, file_link, uid_, ok_gids_));
+
+  // Symlink from one directory to another within the path.
+  FilePath link_to_sub_dir =  base_dir_.AppendASCII("link_to_sub_dir");
+  ASSERT_TRUE(CreateSymbolicLink(sub_dir_, link_to_sub_dir))
+    << "Failed to create symlink.";
+
+  FilePath file_path_with_link = link_to_sub_dir.AppendASCII("file.txt");
+  ASSERT_TRUE(PathExists(file_path_with_link));
+
+  EXPECT_FALSE(VerifyPathControlledByUser(base_dir_, file_path_with_link, uid_,
+                                          ok_gids_));
+
+  EXPECT_FALSE(VerifyPathControlledByUser(link_to_sub_dir, file_path_with_link,
+                                          uid_, ok_gids_));
+
+  // Symlinks in parents of base path are allowed.
+  EXPECT_TRUE(VerifyPathControlledByUser(file_path_with_link,
+                                         file_path_with_link, uid_, ok_gids_));
+}
+
+TEST_F(VerifyPathControlledByUserTest, OwnershipChecks) {
+  // Get a uid that is not the uid of files we create.
+  uid_t bad_uid = uid_ + 1;
+
+  // Make all files and directories non-world-writable.
+  ASSERT_NO_FATAL_FAILURE(
+      ChangePosixFilePermissions(base_dir_, 0u, S_IWOTH));
+  ASSERT_NO_FATAL_FAILURE(
+      ChangePosixFilePermissions(sub_dir_, 0u, S_IWOTH));
+  ASSERT_NO_FATAL_FAILURE(
+      ChangePosixFilePermissions(text_file_, 0u, S_IWOTH));
+
+  // We control these paths.
+  EXPECT_TRUE(VerifyPathControlledByUser(base_dir_, sub_dir_, uid_, ok_gids_));
+  EXPECT_TRUE(
+      VerifyPathControlledByUser(base_dir_, text_file_, uid_, ok_gids_));
+  EXPECT_TRUE(VerifyPathControlledByUser(sub_dir_, text_file_, uid_, ok_gids_));
+
+  // Another user does not control these paths.
+  EXPECT_FALSE(
+      VerifyPathControlledByUser(base_dir_, sub_dir_, bad_uid, ok_gids_));
+  EXPECT_FALSE(
+      VerifyPathControlledByUser(base_dir_, text_file_, bad_uid, ok_gids_));
+  EXPECT_FALSE(
+      VerifyPathControlledByUser(sub_dir_, text_file_, bad_uid, ok_gids_));
+
+  // Another group does not control the paths.
+  EXPECT_FALSE(
+      VerifyPathControlledByUser(base_dir_, sub_dir_, uid_, bad_gids_));
+  EXPECT_FALSE(
+      VerifyPathControlledByUser(base_dir_, text_file_, uid_, bad_gids_));
+  EXPECT_FALSE(
+      VerifyPathControlledByUser(sub_dir_, text_file_, uid_, bad_gids_));
+}
+
+TEST_F(VerifyPathControlledByUserTest, GroupWriteTest) {
+  // Make all files and directories writable only by their owner.
+  ASSERT_NO_FATAL_FAILURE(
+      ChangePosixFilePermissions(base_dir_, 0u, S_IWOTH|S_IWGRP));
+  ASSERT_NO_FATAL_FAILURE(
+      ChangePosixFilePermissions(sub_dir_, 0u, S_IWOTH|S_IWGRP));
+  ASSERT_NO_FATAL_FAILURE(
+      ChangePosixFilePermissions(text_file_, 0u, S_IWOTH|S_IWGRP));
+
+  // Any group is okay because the path is not group-writable.
+  EXPECT_TRUE(VerifyPathControlledByUser(base_dir_, sub_dir_, uid_, ok_gids_));
+  EXPECT_TRUE(
+      VerifyPathControlledByUser(base_dir_, text_file_, uid_, ok_gids_));
+  EXPECT_TRUE(VerifyPathControlledByUser(sub_dir_, text_file_, uid_, ok_gids_));
+
+  EXPECT_TRUE(VerifyPathControlledByUser(base_dir_, sub_dir_, uid_, bad_gids_));
+  EXPECT_TRUE(
+      VerifyPathControlledByUser(base_dir_, text_file_, uid_, bad_gids_));
+  EXPECT_TRUE(
+      VerifyPathControlledByUser(sub_dir_, text_file_, uid_, bad_gids_));
+
+  // No group is okay, because we don't check the group
+  // if no group can write.
+  std::set<gid_t> no_gids;  // Empty set of gids.
+  EXPECT_TRUE(VerifyPathControlledByUser(base_dir_, sub_dir_, uid_, no_gids));
+  EXPECT_TRUE(VerifyPathControlledByUser(base_dir_, text_file_, uid_, no_gids));
+  EXPECT_TRUE(VerifyPathControlledByUser(sub_dir_, text_file_, uid_, no_gids));
+
+  // Make all files and directories writable by their group.
+  ASSERT_NO_FATAL_FAILURE(ChangePosixFilePermissions(base_dir_, S_IWGRP, 0u));
+  ASSERT_NO_FATAL_FAILURE(ChangePosixFilePermissions(sub_dir_, S_IWGRP, 0u));
+  ASSERT_NO_FATAL_FAILURE(ChangePosixFilePermissions(text_file_, S_IWGRP, 0u));
+
+  // Now |ok_gids_| works, but |bad_gids_| fails.
+  EXPECT_TRUE(VerifyPathControlledByUser(base_dir_, sub_dir_, uid_, ok_gids_));
+  EXPECT_TRUE(
+      VerifyPathControlledByUser(base_dir_, text_file_, uid_, ok_gids_));
+  EXPECT_TRUE(VerifyPathControlledByUser(sub_dir_, text_file_, uid_, ok_gids_));
+
+  EXPECT_FALSE(
+      VerifyPathControlledByUser(base_dir_, sub_dir_, uid_, bad_gids_));
+  EXPECT_FALSE(
+      VerifyPathControlledByUser(base_dir_, text_file_, uid_, bad_gids_));
+  EXPECT_FALSE(
+      VerifyPathControlledByUser(sub_dir_, text_file_, uid_, bad_gids_));
+
+  // Because any group in the group set is allowed,
+  // the union of good and bad gids passes.
+
+  std::set<gid_t> multiple_gids;
+  std::set_union(
+      ok_gids_.begin(), ok_gids_.end(),
+      bad_gids_.begin(), bad_gids_.end(),
+      std::inserter(multiple_gids, multiple_gids.begin()));
+
+  EXPECT_TRUE(
+      VerifyPathControlledByUser(base_dir_, sub_dir_, uid_, multiple_gids));
+  EXPECT_TRUE(
+      VerifyPathControlledByUser(base_dir_, text_file_, uid_, multiple_gids));
+  EXPECT_TRUE(
+      VerifyPathControlledByUser(sub_dir_, text_file_, uid_, multiple_gids));
+}
+
+TEST_F(VerifyPathControlledByUserTest, WriteBitChecks) {
+  // Make all files and directories non-world-writable.
+  ASSERT_NO_FATAL_FAILURE(
+      ChangePosixFilePermissions(base_dir_, 0u, S_IWOTH));
+  ASSERT_NO_FATAL_FAILURE(
+      ChangePosixFilePermissions(sub_dir_, 0u, S_IWOTH));
+  ASSERT_NO_FATAL_FAILURE(
+      ChangePosixFilePermissions(text_file_, 0u, S_IWOTH));
+
+  // Initialy, we control all parts of the path.
+  EXPECT_TRUE(VerifyPathControlledByUser(base_dir_, sub_dir_, uid_, ok_gids_));
+  EXPECT_TRUE(
+      VerifyPathControlledByUser(base_dir_, text_file_, uid_, ok_gids_));
+  EXPECT_TRUE(VerifyPathControlledByUser(sub_dir_, text_file_, uid_, ok_gids_));
+
+  // Make base_dir_ world-writable.
+  ASSERT_NO_FATAL_FAILURE(
+      ChangePosixFilePermissions(base_dir_, S_IWOTH, 0u));
+  EXPECT_FALSE(VerifyPathControlledByUser(base_dir_, sub_dir_, uid_, ok_gids_));
+  EXPECT_FALSE(
+      VerifyPathControlledByUser(base_dir_, text_file_, uid_, ok_gids_));
+  EXPECT_TRUE(VerifyPathControlledByUser(sub_dir_, text_file_, uid_, ok_gids_));
+
+  // Make sub_dir_ world writable.
+  ASSERT_NO_FATAL_FAILURE(
+      ChangePosixFilePermissions(sub_dir_, S_IWOTH, 0u));
+  EXPECT_FALSE(VerifyPathControlledByUser(base_dir_, sub_dir_, uid_, ok_gids_));
+  EXPECT_FALSE(
+      VerifyPathControlledByUser(base_dir_, text_file_, uid_, ok_gids_));
+  EXPECT_FALSE(
+      VerifyPathControlledByUser(sub_dir_, text_file_, uid_, ok_gids_));
+
+  // Make text_file_ world writable.
+  ASSERT_NO_FATAL_FAILURE(
+      ChangePosixFilePermissions(text_file_, S_IWOTH, 0u));
+  EXPECT_FALSE(VerifyPathControlledByUser(base_dir_, sub_dir_, uid_, ok_gids_));
+  EXPECT_FALSE(
+      VerifyPathControlledByUser(base_dir_, text_file_, uid_, ok_gids_));
+  EXPECT_FALSE(
+      VerifyPathControlledByUser(sub_dir_, text_file_, uid_, ok_gids_));
+
+  // Make sub_dir_ non-world writable.
+  ASSERT_NO_FATAL_FAILURE(
+      ChangePosixFilePermissions(sub_dir_, 0u, S_IWOTH));
+  EXPECT_FALSE(VerifyPathControlledByUser(base_dir_, sub_dir_, uid_, ok_gids_));
+  EXPECT_FALSE(
+      VerifyPathControlledByUser(base_dir_, text_file_, uid_, ok_gids_));
+  EXPECT_FALSE(
+      VerifyPathControlledByUser(sub_dir_, text_file_, uid_, ok_gids_));
+
+  // Make base_dir_ non-world-writable.
+  ASSERT_NO_FATAL_FAILURE(
+      ChangePosixFilePermissions(base_dir_, 0u, S_IWOTH));
+  EXPECT_TRUE(VerifyPathControlledByUser(base_dir_, sub_dir_, uid_, ok_gids_));
+  EXPECT_FALSE(
+      VerifyPathControlledByUser(base_dir_, text_file_, uid_, ok_gids_));
+  EXPECT_FALSE(
+      VerifyPathControlledByUser(sub_dir_, text_file_, uid_, ok_gids_));
+
+  // Back to the initial state: Nothing is writable, so every path
+  // should pass.
+  ASSERT_NO_FATAL_FAILURE(
+      ChangePosixFilePermissions(text_file_, 0u, S_IWOTH));
+  EXPECT_TRUE(VerifyPathControlledByUser(base_dir_, sub_dir_, uid_, ok_gids_));
+  EXPECT_TRUE(
+      VerifyPathControlledByUser(base_dir_, text_file_, uid_, ok_gids_));
+  EXPECT_TRUE(VerifyPathControlledByUser(sub_dir_, text_file_, uid_, ok_gids_));
+}
+
+#endif  // defined(OS_POSIX)
+
+#if defined(OS_ANDROID)
+TEST_F(FileUtilTest, ValidContentUriTest) {
+  // Get the test image path.
+  FilePath data_dir;
+  ASSERT_TRUE(PathService::Get(DIR_TEST_DATA, &data_dir));
+  data_dir = data_dir.AppendASCII("file_util");
+  ASSERT_TRUE(PathExists(data_dir));
+  FilePath image_file = data_dir.Append(FILE_PATH_LITERAL("red.png"));
+  int64_t image_size;
+  GetFileSize(image_file, &image_size);
+  ASSERT_GT(image_size, 0);
+
+  // Insert the image into MediaStore. MediaStore will do some conversions, and
+  // return the content URI.
+  FilePath path = InsertImageIntoMediaStore(image_file);
+  EXPECT_TRUE(path.IsContentUri());
+  EXPECT_TRUE(PathExists(path));
+  // The file size may not equal to the input image as MediaStore may convert
+  // the image.
+  int64_t content_uri_size;
+  GetFileSize(path, &content_uri_size);
+  EXPECT_EQ(image_size, content_uri_size);
+
+  // We should be able to read the file.
+  File file = OpenContentUriForRead(path);
+  EXPECT_TRUE(file.IsValid());
+  auto buffer = std::make_unique<char[]>(image_size);
+  EXPECT_TRUE(file.ReadAtCurrentPos(buffer.get(), image_size));
+}
+
+TEST_F(FileUtilTest, NonExistentContentUriTest) {
+  FilePath path("content://foo.bar");
+  EXPECT_TRUE(path.IsContentUri());
+  EXPECT_FALSE(PathExists(path));
+  // Size should be smaller than 0.
+  int64_t size;
+  EXPECT_FALSE(GetFileSize(path, &size));
+
+  // We should not be able to read the file.
+  File file = OpenContentUriForRead(path);
+  EXPECT_FALSE(file.IsValid());
+}
+#endif
+
+// Test that temp files obtained racily are all unique (no interference between
+// threads). Mimics file operations in DoLaunchChildTestProcess() to rule out
+// thread-safety issues @ https://crbug.com/826408#c17.
+#if defined(OS_FUCHSIA)
+// TODO(crbug.com/844416): Too slow to run on infra due to QEMU overloads.
+#define MAYBE_MultiThreadedTempFiles DISABLED_MultiThreadedTempFiles
+#else
+#define MAYBE_MultiThreadedTempFiles MultiThreadedTempFiles
+#endif
+TEST(FileUtilMultiThreadedTest, MAYBE_MultiThreadedTempFiles) {
+  constexpr int kNumThreads = 64;
+  constexpr int kNumWritesPerThread = 32;
+
+  std::unique_ptr<Thread> threads[kNumThreads];
+  for (auto& thread : threads) {
+    thread = std::make_unique<Thread>("test worker");
+    thread->Start();
+  }
+
+  // Wait until all threads are started for max parallelism.
+  for (auto& thread : threads)
+    thread->WaitUntilThreadStarted();
+
+  const RepeatingClosure open_write_close_read = BindRepeating([]() {
+    FilePath output_filename;
+    ScopedFILE output_file(CreateAndOpenTemporaryFile(&output_filename));
+    EXPECT_TRUE(output_file);
+
+    const std::string content = GenerateGUID();
+#if defined(OS_WIN)
+    HANDLE handle =
+        reinterpret_cast<HANDLE>(_get_osfhandle(_fileno(output_file.get())));
+    DWORD bytes_written = 0;
+    ::WriteFile(handle, content.c_str(), content.length(), &bytes_written,
+                NULL);
+#else
+    size_t bytes_written =
+        ::write(::fileno(output_file.get()), content.c_str(), content.length());
+#endif
+    EXPECT_EQ(content.length(), bytes_written);
+    ::fflush(output_file.get());
+    output_file.reset();
+
+    std::string output_file_contents;
+    EXPECT_TRUE(ReadFileToString(output_filename, &output_file_contents))
+        << output_filename;
+
+    EXPECT_EQ(content, output_file_contents);
+
+    DeleteFile(output_filename, false);
+  });
+
+  // Post tasks to each thread in a round-robin fashion to ensure as much
+  // parallelism as possible.
+  for (int i = 0; i < kNumWritesPerThread; ++i) {
+    for (auto& thread : threads) {
+      thread->task_runner()->PostTask(FROM_HERE, open_write_close_read);
+    }
+  }
+
+  for (auto& thread : threads)
+    thread->Stop();
+}
+
+#if defined(OS_POSIX) || defined(OS_FUCHSIA)
+
+TEST(ScopedFD, ScopedFDDoesClose) {
+  int fds[2];
+  char c = 0;
+  ASSERT_EQ(0, pipe(fds));
+  const int write_end = fds[1];
+  ScopedFD read_end_closer(fds[0]);
+  {
+    ScopedFD write_end_closer(fds[1]);
+  }
+  // This is the only thread. This file descriptor should no longer be valid.
+  int ret = close(write_end);
+  EXPECT_EQ(-1, ret);
+  EXPECT_EQ(EBADF, errno);
+  // Make sure read(2) won't block.
+  ASSERT_EQ(0, fcntl(fds[0], F_SETFL, O_NONBLOCK));
+  // Reading the pipe should EOF.
+  EXPECT_EQ(0, read(fds[0], &c, 1));
+}
+
+#if defined(GTEST_HAS_DEATH_TEST)
+void CloseWithScopedFD(int fd) {
+  ScopedFD fd_closer(fd);
+}
+#endif
+
+TEST(ScopedFD, ScopedFDCrashesOnCloseFailure) {
+  int fds[2];
+  ASSERT_EQ(0, pipe(fds));
+  ScopedFD read_end_closer(fds[0]);
+  EXPECT_EQ(0, IGNORE_EINTR(close(fds[1])));
+#if defined(GTEST_HAS_DEATH_TEST)
+  // This is the only thread. This file descriptor should no longer be valid.
+  // Trying to close it should crash. This is important for security.
+  EXPECT_DEATH(CloseWithScopedFD(fds[1]), "");
+#endif
+}
+
+#endif  // defined(OS_POSIX) || defined(OS_FUCHSIA)
+
+}  // namespace
+
+}  // namespace base
diff --git a/base/files/memory_mapped_file_unittest.cc b/base/files/memory_mapped_file_unittest.cc
new file mode 100644
index 0000000..b7acc61
--- /dev/null
+++ b/base/files/memory_mapped_file_unittest.cc
@@ -0,0 +1,243 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/files/memory_mapped_file.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <utility>
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/platform_test.h"
+
+namespace base {
+
+namespace {
+
+// Create a temporary buffer and fill it with a watermark sequence.
+std::unique_ptr<uint8_t[]> CreateTestBuffer(size_t size, size_t offset) {
+  std::unique_ptr<uint8_t[]> buf(new uint8_t[size]);
+  for (size_t i = 0; i < size; ++i)
+    buf.get()[i] = static_cast<uint8_t>((offset + i) % 253);
+  return buf;
+}
+
+// Check that the watermark sequence is consistent with the |offset| provided.
+bool CheckBufferContents(const uint8_t* data, size_t size, size_t offset) {
+  std::unique_ptr<uint8_t[]> test_data(CreateTestBuffer(size, offset));
+  return memcmp(test_data.get(), data, size) == 0;
+}
+
+class MemoryMappedFileTest : public PlatformTest {
+ protected:
+  void SetUp() override {
+    PlatformTest::SetUp();
+    CreateTemporaryFile(&temp_file_path_);
+  }
+
+  void TearDown() override { EXPECT_TRUE(DeleteFile(temp_file_path_, false)); }
+
+  void CreateTemporaryTestFile(size_t size) {
+    File file(temp_file_path_,
+              File::FLAG_CREATE_ALWAYS | File::FLAG_READ | File::FLAG_WRITE);
+    EXPECT_TRUE(file.IsValid());
+
+    std::unique_ptr<uint8_t[]> test_data(CreateTestBuffer(size, 0));
+    size_t bytes_written =
+        file.Write(0, reinterpret_cast<char*>(test_data.get()), size);
+    EXPECT_EQ(size, bytes_written);
+    file.Close();
+  }
+
+  const FilePath temp_file_path() const { return temp_file_path_; }
+
+ private:
+  FilePath temp_file_path_;
+};
+
+TEST_F(MemoryMappedFileTest, MapWholeFileByPath) {
+  const size_t kFileSize = 68 * 1024;
+  CreateTemporaryTestFile(kFileSize);
+  MemoryMappedFile map;
+  map.Initialize(temp_file_path());
+  ASSERT_EQ(kFileSize, map.length());
+  ASSERT_TRUE(map.data() != nullptr);
+  EXPECT_TRUE(map.IsValid());
+  ASSERT_TRUE(CheckBufferContents(map.data(), kFileSize, 0));
+}
+
+TEST_F(MemoryMappedFileTest, MapWholeFileByFD) {
+  const size_t kFileSize = 68 * 1024;
+  CreateTemporaryTestFile(kFileSize);
+  MemoryMappedFile map;
+  map.Initialize(File(temp_file_path(), File::FLAG_OPEN | File::FLAG_READ));
+  ASSERT_EQ(kFileSize, map.length());
+  ASSERT_TRUE(map.data() != nullptr);
+  EXPECT_TRUE(map.IsValid());
+  ASSERT_TRUE(CheckBufferContents(map.data(), kFileSize, 0));
+}
+
+TEST_F(MemoryMappedFileTest, MapSmallFile) {
+  const size_t kFileSize = 127;
+  CreateTemporaryTestFile(kFileSize);
+  MemoryMappedFile map;
+  map.Initialize(temp_file_path());
+  ASSERT_EQ(kFileSize, map.length());
+  ASSERT_TRUE(map.data() != nullptr);
+  EXPECT_TRUE(map.IsValid());
+  ASSERT_TRUE(CheckBufferContents(map.data(), kFileSize, 0));
+}
+
+TEST_F(MemoryMappedFileTest, MapWholeFileUsingRegion) {
+  const size_t kFileSize = 157 * 1024;
+  CreateTemporaryTestFile(kFileSize);
+  MemoryMappedFile map;
+
+  File file(temp_file_path(), File::FLAG_OPEN | File::FLAG_READ);
+  map.Initialize(std::move(file), MemoryMappedFile::Region::kWholeFile);
+  ASSERT_EQ(kFileSize, map.length());
+  ASSERT_TRUE(map.data() != nullptr);
+  EXPECT_TRUE(map.IsValid());
+  ASSERT_TRUE(CheckBufferContents(map.data(), kFileSize, 0));
+}
+
+TEST_F(MemoryMappedFileTest, MapPartialRegionAtBeginning) {
+  const size_t kFileSize = 157 * 1024;
+  const size_t kPartialSize = 4 * 1024 + 32;
+  CreateTemporaryTestFile(kFileSize);
+  MemoryMappedFile map;
+
+  File file(temp_file_path(), File::FLAG_OPEN | File::FLAG_READ);
+  MemoryMappedFile::Region region = {0, kPartialSize};
+  map.Initialize(std::move(file), region);
+  ASSERT_EQ(kPartialSize, map.length());
+  ASSERT_TRUE(map.data() != nullptr);
+  EXPECT_TRUE(map.IsValid());
+  ASSERT_TRUE(CheckBufferContents(map.data(), kPartialSize, 0));
+}
+
+TEST_F(MemoryMappedFileTest, MapPartialRegionAtEnd) {
+  const size_t kFileSize = 157 * 1024;
+  const size_t kPartialSize = 5 * 1024 - 32;
+  const size_t kOffset = kFileSize - kPartialSize;
+  CreateTemporaryTestFile(kFileSize);
+  MemoryMappedFile map;
+
+  File file(temp_file_path(), File::FLAG_OPEN | File::FLAG_READ);
+  MemoryMappedFile::Region region = {kOffset, kPartialSize};
+  map.Initialize(std::move(file), region);
+  ASSERT_EQ(kPartialSize, map.length());
+  ASSERT_TRUE(map.data() != nullptr);
+  EXPECT_TRUE(map.IsValid());
+  ASSERT_TRUE(CheckBufferContents(map.data(), kPartialSize, kOffset));
+}
+
+TEST_F(MemoryMappedFileTest, MapSmallPartialRegionInTheMiddle) {
+  const size_t kFileSize = 157 * 1024;
+  const size_t kOffset = 1024 * 5 + 32;
+  const size_t kPartialSize = 8;
+
+  CreateTemporaryTestFile(kFileSize);
+  MemoryMappedFile map;
+
+  File file(temp_file_path(), File::FLAG_OPEN | File::FLAG_READ);
+  MemoryMappedFile::Region region = {kOffset, kPartialSize};
+  map.Initialize(std::move(file), region);
+  ASSERT_EQ(kPartialSize, map.length());
+  ASSERT_TRUE(map.data() != nullptr);
+  EXPECT_TRUE(map.IsValid());
+  ASSERT_TRUE(CheckBufferContents(map.data(), kPartialSize, kOffset));
+}
+
+TEST_F(MemoryMappedFileTest, MapLargePartialRegionInTheMiddle) {
+  const size_t kFileSize = 157 * 1024;
+  const size_t kOffset = 1024 * 5 + 32;
+  const size_t kPartialSize = 16 * 1024 - 32;
+
+  CreateTemporaryTestFile(kFileSize);
+  MemoryMappedFile map;
+
+  File file(temp_file_path(), File::FLAG_OPEN | File::FLAG_READ);
+  MemoryMappedFile::Region region = {kOffset, kPartialSize};
+  map.Initialize(std::move(file), region);
+  ASSERT_EQ(kPartialSize, map.length());
+  ASSERT_TRUE(map.data() != nullptr);
+  EXPECT_TRUE(map.IsValid());
+  ASSERT_TRUE(CheckBufferContents(map.data(), kPartialSize, kOffset));
+}
+
+TEST_F(MemoryMappedFileTest, WriteableFile) {
+  const size_t kFileSize = 127;
+  CreateTemporaryTestFile(kFileSize);
+
+  {
+    MemoryMappedFile map;
+    map.Initialize(temp_file_path(), MemoryMappedFile::READ_WRITE);
+    ASSERT_EQ(kFileSize, map.length());
+    ASSERT_TRUE(map.data() != nullptr);
+    EXPECT_TRUE(map.IsValid());
+    ASSERT_TRUE(CheckBufferContents(map.data(), kFileSize, 0));
+
+    uint8_t* bytes = map.data();
+    bytes[0] = 'B';
+    bytes[1] = 'a';
+    bytes[2] = 'r';
+    bytes[kFileSize - 1] = '!';
+    EXPECT_FALSE(CheckBufferContents(map.data(), kFileSize, 0));
+    EXPECT_TRUE(CheckBufferContents(map.data() + 3, kFileSize - 4, 3));
+  }
+
+  int64_t file_size;
+  ASSERT_TRUE(GetFileSize(temp_file_path(), &file_size));
+  EXPECT_EQ(static_cast<int64_t>(kFileSize), file_size);
+
+  std::string contents;
+  ASSERT_TRUE(ReadFileToString(temp_file_path(), &contents));
+  EXPECT_EQ("Bar", contents.substr(0, 3));
+  EXPECT_EQ("!", contents.substr(kFileSize - 1, 1));
+}
+
+TEST_F(MemoryMappedFileTest, ExtendableFile) {
+  const size_t kFileSize = 127;
+  const size_t kFileExtend = 100;
+  CreateTemporaryTestFile(kFileSize);
+
+  {
+    File file(temp_file_path(),
+              File::FLAG_OPEN | File::FLAG_READ | File::FLAG_WRITE);
+    MemoryMappedFile::Region region = {0, kFileSize + kFileExtend};
+    MemoryMappedFile map;
+    map.Initialize(std::move(file), region,
+                   MemoryMappedFile::READ_WRITE_EXTEND);
+    EXPECT_EQ(kFileSize + kFileExtend, map.length());
+    ASSERT_TRUE(map.data() != nullptr);
+    EXPECT_TRUE(map.IsValid());
+    ASSERT_TRUE(CheckBufferContents(map.data(), kFileSize, 0));
+
+    uint8_t* bytes = map.data();
+    EXPECT_EQ(0, bytes[kFileSize + 0]);
+    EXPECT_EQ(0, bytes[kFileSize + 1]);
+    EXPECT_EQ(0, bytes[kFileSize + 2]);
+    bytes[kFileSize + 0] = 'B';
+    bytes[kFileSize + 1] = 'A';
+    bytes[kFileSize + 2] = 'Z';
+    EXPECT_TRUE(CheckBufferContents(map.data(), kFileSize, 0));
+  }
+
+  int64_t file_size;
+  ASSERT_TRUE(GetFileSize(temp_file_path(), &file_size));
+  EXPECT_LE(static_cast<int64_t>(kFileSize + 3), file_size);
+  EXPECT_GE(static_cast<int64_t>(kFileSize + kFileExtend), file_size);
+
+  std::string contents;
+  ASSERT_TRUE(ReadFileToString(temp_file_path(), &contents));
+  EXPECT_EQ("BAZ", contents.substr(kFileSize, 3));
+}
+
+}  // namespace
+
+}  // namespace base
diff --git a/base/hash_unittest.cc b/base/hash_unittest.cc
new file mode 100644
index 0000000..fc8a751
--- /dev/null
+++ b/base/hash_unittest.cc
@@ -0,0 +1,82 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/hash.h"
+
+#include <string>
+#include <vector>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+TEST(HashTest, String) {
+  std::string str;
+  // Empty string (should hash to 0).
+  str = "";
+  EXPECT_EQ(0u, Hash(str));
+
+  // Simple test.
+  str = "hello world";
+  EXPECT_EQ(2794219650u, Hash(str));
+
+  // Change one bit.
+  str = "helmo world";
+  EXPECT_EQ(1006697176u, Hash(str));
+
+  // Insert a null byte.
+  str = "hello  world";
+  str[5] = '\0';
+  EXPECT_EQ(2319902537u, Hash(str));
+
+  // Test that the bytes after the null contribute to the hash.
+  str = "hello  worle";
+  str[5] = '\0';
+  EXPECT_EQ(553904462u, Hash(str));
+
+  // Extremely long string.
+  // Also tests strings with high bit set, and null byte.
+  std::vector<char> long_string_buffer;
+  for (int i = 0; i < 4096; ++i)
+    long_string_buffer.push_back((i % 256) - 128);
+  str.assign(&long_string_buffer.front(), long_string_buffer.size());
+  EXPECT_EQ(2797962408u, Hash(str));
+
+  // All possible lengths (mod 4). Tests separate code paths. Also test with
+  // final byte high bit set (regression test for http://crbug.com/90659).
+  // Note that the 1 and 3 cases have a weird bug where the final byte is
+  // treated as a signed char. It was decided on the above bug discussion to
+  // enshrine that behaviour as "correct" to avoid invalidating existing hashes.
+
+  // Length mod 4 == 0.
+  str = "hello w\xab";
+  EXPECT_EQ(615571198u, Hash(str));
+  // Length mod 4 == 1.
+  str = "hello wo\xab";
+  EXPECT_EQ(623474296u, Hash(str));
+  // Length mod 4 == 2.
+  str = "hello wor\xab";
+  EXPECT_EQ(4278562408u, Hash(str));
+  // Length mod 4 == 3.
+  str = "hello worl\xab";
+  EXPECT_EQ(3224633008u, Hash(str));
+}
+
+TEST(HashTest, CString) {
+  const char* str;
+  // Empty string (should hash to 0).
+  str = "";
+  EXPECT_EQ(0u, Hash(str, strlen(str)));
+
+  // Simple test.
+  str = "hello world";
+  EXPECT_EQ(2794219650u, Hash(str, strlen(str)));
+
+  // Ensure that it stops reading after the given length, and does not expect a
+  // null byte.
+  str = "hello world; don't read this part";
+  EXPECT_EQ(2794219650u, Hash(str, strlen("hello world")));
+}
+
+}  // namespace base
diff --git a/base/i18n/base_i18n_switches.cc b/base/i18n/base_i18n_switches.cc
new file mode 100644
index 0000000..103d665
--- /dev/null
+++ b/base/i18n/base_i18n_switches.cc
@@ -0,0 +1,21 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/i18n/base_i18n_switches.h"
+
+namespace switches {
+
+// Force the UI to a specific direction. Valid values are "ltr" (left-to-right)
+// and "rtl" (right-to-left).
+const char kForceUIDirection[]   = "force-ui-direction";
+
+// Force the text rendering to a specific direction. Valid values are "ltr"
+// (left-to-right) and "rtl" (right-to-left). Only tested meaningfully with
+// RTL.
+const char kForceTextDirection[] = "force-text-direction";
+
+const char kForceDirectionLTR[]  = "ltr";
+const char kForceDirectionRTL[]  = "rtl";
+
+}  // namespace switches
diff --git a/base/i18n/base_i18n_switches.h b/base/i18n/base_i18n_switches.h
new file mode 100644
index 0000000..d1ba690
--- /dev/null
+++ b/base/i18n/base_i18n_switches.h
@@ -0,0 +1,21 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_I18N_BASE_I18N_SWITCHES_H_
+#define BASE_I18N_BASE_I18N_SWITCHES_H_
+
+#include "base/i18n/base_i18n_export.h"
+
+namespace switches {
+
+BASE_I18N_EXPORT extern const char kForceUIDirection[];
+BASE_I18N_EXPORT extern const char kForceTextDirection[];
+
+// kForce*Direction choices for the switches above.
+BASE_I18N_EXPORT extern const char kForceDirectionLTR[];
+BASE_I18N_EXPORT extern const char kForceDirectionRTL[];
+
+}  // namespace switches
+
+#endif  // BASE_I18N_BASE_I18N_SWITCHES_H_
diff --git a/base/i18n/bidi_line_iterator.cc b/base/i18n/bidi_line_iterator.cc
new file mode 100644
index 0000000..3f7f868
--- /dev/null
+++ b/base/i18n/bidi_line_iterator.cc
@@ -0,0 +1,119 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/i18n/bidi_line_iterator.h"
+
+#include "base/logging.h"
+
+namespace base {
+namespace i18n {
+
+namespace {
+
+UBiDiLevel GetParagraphLevelForDirection(TextDirection direction) {
+  switch (direction) {
+    case UNKNOWN_DIRECTION:
+      return UBIDI_DEFAULT_LTR;
+      break;
+    case RIGHT_TO_LEFT:
+      return 1;  // Highest RTL level.
+      break;
+    case LEFT_TO_RIGHT:
+      return 0;  // Highest LTR level.
+      break;
+    default:
+      NOTREACHED();
+      return 0;
+  }
+}
+
+// Overrides the default bidi class for a given character, for the custom
+// "AS_URL" behavior. Returns U_BIDI_CLASS_DEFAULT to defer to the default ICU
+// behavior.
+//
+// Matches the C callback interface of ICU's UBiDiClassCallback type (which is
+// why there is an unused argument).
+UCharDirection GetURLBiDiClassCallback(const void* /*unused*/, UChar32 c) {
+  // Note: Use a switch statement instead of strchr() to avoid iterating over a
+  // string for each character (the switch allows for much better compiler
+  // optimization).
+  switch (c) {
+    // The set of characters that delimit URL components (separating the scheme,
+    // username, password, domain labels, host, path segments, query
+    // names/values and fragment).
+    case '#':
+    case '&':
+    case '.':
+    case '/':
+    case ':':
+    case '=':
+    case '?':
+    case '@':
+      // Treat all of these characters as strong LTR, which effectively
+      // surrounds all of the text components of a URL (e.g., the domain labels
+      // and path segments) in a left-to-right embedding. This ensures that the
+      // URL components read from left to right, regardless of any RTL
+      // characters. (Within each component, RTL sequences are rendered from
+      // right to left as expected.)
+      return U_LEFT_TO_RIGHT;
+    default:
+      return U_BIDI_CLASS_DEFAULT;
+  }
+}
+
+}  // namespace
+
+BiDiLineIterator::BiDiLineIterator() : bidi_(nullptr) {}
+
+BiDiLineIterator::~BiDiLineIterator() {
+  if (bidi_) {
+    ubidi_close(bidi_);
+    bidi_ = nullptr;
+  }
+}
+
+bool BiDiLineIterator::Open(const string16& text,
+                            TextDirection direction,
+                            CustomBehavior behavior) {
+  DCHECK(!bidi_);
+  UErrorCode error = U_ZERO_ERROR;
+  bidi_ = ubidi_openSized(static_cast<int>(text.length()), 0, &error);
+  if (U_FAILURE(error))
+    return false;
+
+  if (behavior == CustomBehavior::AS_URL) {
+    ubidi_setClassCallback(bidi_, GetURLBiDiClassCallback, nullptr, nullptr,
+                           nullptr, &error);
+    if (U_FAILURE(error))
+      return false;
+  }
+
+  ubidi_setPara(bidi_, text.data(), static_cast<int>(text.length()),
+                GetParagraphLevelForDirection(direction), nullptr, &error);
+  return (U_SUCCESS(error));
+}
+
+int BiDiLineIterator::CountRuns() const {
+  DCHECK(bidi_ != nullptr);
+  UErrorCode error = U_ZERO_ERROR;
+  const int runs = ubidi_countRuns(bidi_, &error);
+  return U_SUCCESS(error) ? runs : 0;
+}
+
+UBiDiDirection BiDiLineIterator::GetVisualRun(int index,
+                                              int* start,
+                                              int* length) const {
+  DCHECK(bidi_ != nullptr);
+  return ubidi_getVisualRun(bidi_, index, start, length);
+}
+
+void BiDiLineIterator::GetLogicalRun(int start,
+                                     int* end,
+                                     UBiDiLevel* level) const {
+  DCHECK(bidi_ != nullptr);
+  ubidi_getLogicalRun(bidi_, start, end, level);
+}
+
+}  // namespace i18n
+}  // namespace base
diff --git a/base/i18n/bidi_line_iterator.h b/base/i18n/bidi_line_iterator.h
new file mode 100644
index 0000000..d840f61
--- /dev/null
+++ b/base/i18n/bidi_line_iterator.h
@@ -0,0 +1,60 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_I18N_BIDI_LINE_ITERATOR_H_
+#define BASE_I18N_BIDI_LINE_ITERATOR_H_
+
+#include "base/i18n/base_i18n_export.h"
+#include "base/i18n/rtl.h"
+#include "base/macros.h"
+#include "base/strings/string16.h"
+#include "third_party/icu/source/common/unicode/ubidi.h"
+#include "third_party/icu/source/common/unicode/uchar.h"
+
+namespace base {
+namespace i18n {
+
+// A simple wrapper class for the bidirectional iterator of ICU.
+// This class uses the bidirectional iterator of ICU to split a line of
+// bidirectional texts into visual runs in its display order.
+class BASE_I18N_EXPORT BiDiLineIterator {
+ public:
+  // Specifies some alternative iteration behavior.
+  enum class CustomBehavior {
+    // No special behavior.
+    NONE,
+    // Treat URL delimiter characters as strong LTR. This is a special treatment
+    // for URLs that purposefully violates the URL Standard, as an experiment.
+    // It should only be used behind a flag.
+    AS_URL
+  };
+
+  BiDiLineIterator();
+  ~BiDiLineIterator();
+
+  // Initializes the bidirectional iterator with the specified text.  Returns
+  // whether initialization succeeded.
+  bool Open(const string16& text,
+            TextDirection direction,
+            CustomBehavior behavior);
+
+  // Returns the number of visual runs in the text, or zero on error.
+  int CountRuns() const;
+
+  // Gets the logical offset, length, and direction of the specified visual run.
+  UBiDiDirection GetVisualRun(int index, int* start, int* length) const;
+
+  // Given a start position, figure out where the run ends (and the BiDiLevel).
+  void GetLogicalRun(int start, int* end, UBiDiLevel* level) const;
+
+ private:
+  UBiDi* bidi_;
+
+  DISALLOW_COPY_AND_ASSIGN(BiDiLineIterator);
+};
+
+}  // namespace i18n
+}  // namespace base
+
+#endif  // BASE_I18N_BIDI_LINE_ITERATOR_H_
diff --git a/base/i18n/bidi_line_iterator_unittest.cc b/base/i18n/bidi_line_iterator_unittest.cc
new file mode 100644
index 0000000..d531313
--- /dev/null
+++ b/base/i18n/bidi_line_iterator_unittest.cc
@@ -0,0 +1,209 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/i18n/bidi_line_iterator.h"
+
+#include "base/macros.h"
+#include "base/strings/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace i18n {
+namespace {
+
+class BiDiLineIteratorTest : public testing::TestWithParam<TextDirection> {
+ public:
+  BiDiLineIteratorTest() = default;
+
+  BiDiLineIterator* iterator() { return &iterator_; }
+
+ private:
+  BiDiLineIterator iterator_;
+
+  DISALLOW_COPY_AND_ASSIGN(BiDiLineIteratorTest);
+};
+
+TEST_P(BiDiLineIteratorTest, OnlyLTR) {
+  iterator()->Open(UTF8ToUTF16("abc 😁 测试"), GetParam(),
+                   BiDiLineIterator::CustomBehavior::NONE);
+  ASSERT_EQ(1, iterator()->CountRuns());
+
+  int start, length;
+  EXPECT_EQ(UBIDI_LTR, iterator()->GetVisualRun(0, &start, &length));
+  EXPECT_EQ(0, start);
+  EXPECT_EQ(9, length);
+
+  int end;
+  UBiDiLevel level;
+  iterator()->GetLogicalRun(0, &end, &level);
+  EXPECT_EQ(9, end);
+  if (GetParam() == TextDirection::RIGHT_TO_LEFT)
+    EXPECT_EQ(2, level);
+  else
+    EXPECT_EQ(0, level);
+}
+
+TEST_P(BiDiLineIteratorTest, OnlyRTL) {
+  iterator()->Open(UTF8ToUTF16("מה השעה"), GetParam(),
+                   BiDiLineIterator::CustomBehavior::NONE);
+  ASSERT_EQ(1, iterator()->CountRuns());
+
+  int start, length;
+  EXPECT_EQ(UBIDI_RTL, iterator()->GetVisualRun(0, &start, &length));
+  EXPECT_EQ(0, start);
+  EXPECT_EQ(7, length);
+
+  int end;
+  UBiDiLevel level;
+  iterator()->GetLogicalRun(0, &end, &level);
+  EXPECT_EQ(7, end);
+  EXPECT_EQ(1, level);
+}
+
+TEST_P(BiDiLineIteratorTest, Mixed) {
+  iterator()->Open(UTF8ToUTF16("אני משתמש ב- Chrome כדפדפן האינטרנט שלי"),
+                   GetParam(), BiDiLineIterator::CustomBehavior::NONE);
+  ASSERT_EQ(3, iterator()->CountRuns());
+
+  // We'll get completely different results depending on the top-level paragraph
+  // direction.
+  if (GetParam() == TextDirection::RIGHT_TO_LEFT) {
+    // If para direction is RTL, expect the LTR substring "Chrome" to be nested
+    // within the surrounding RTL text.
+    int start, length;
+    EXPECT_EQ(UBIDI_RTL, iterator()->GetVisualRun(0, &start, &length));
+    EXPECT_EQ(19, start);
+    EXPECT_EQ(20, length);
+    EXPECT_EQ(UBIDI_LTR, iterator()->GetVisualRun(1, &start, &length));
+    EXPECT_EQ(13, start);
+    EXPECT_EQ(6, length);
+    EXPECT_EQ(UBIDI_RTL, iterator()->GetVisualRun(2, &start, &length));
+    EXPECT_EQ(0, start);
+    EXPECT_EQ(13, length);
+
+    int end;
+    UBiDiLevel level;
+    iterator()->GetLogicalRun(0, &end, &level);
+    EXPECT_EQ(13, end);
+    EXPECT_EQ(1, level);
+    iterator()->GetLogicalRun(13, &end, &level);
+    EXPECT_EQ(19, end);
+    EXPECT_EQ(2, level);
+    iterator()->GetLogicalRun(19, &end, &level);
+    EXPECT_EQ(39, end);
+    EXPECT_EQ(1, level);
+  } else {
+    // If the para direction is LTR, expect the LTR substring "- Chrome " to be
+    // at the top level, with two nested RTL runs on either side.
+    int start, length;
+    EXPECT_EQ(UBIDI_RTL, iterator()->GetVisualRun(0, &start, &length));
+    EXPECT_EQ(0, start);
+    EXPECT_EQ(11, length);
+    EXPECT_EQ(UBIDI_LTR, iterator()->GetVisualRun(1, &start, &length));
+    EXPECT_EQ(11, start);
+    EXPECT_EQ(9, length);
+    EXPECT_EQ(UBIDI_RTL, iterator()->GetVisualRun(2, &start, &length));
+    EXPECT_EQ(20, start);
+    EXPECT_EQ(19, length);
+
+    int end;
+    UBiDiLevel level;
+    iterator()->GetLogicalRun(0, &end, &level);
+    EXPECT_EQ(11, end);
+    EXPECT_EQ(1, level);
+    iterator()->GetLogicalRun(11, &end, &level);
+    EXPECT_EQ(20, end);
+    EXPECT_EQ(0, level);
+    iterator()->GetLogicalRun(20, &end, &level);
+    EXPECT_EQ(39, end);
+    EXPECT_EQ(1, level);
+  }
+}
+
+TEST_P(BiDiLineIteratorTest, RTLPunctuationNoCustomBehavior) {
+  // This string features Hebrew characters interleaved with ASCII punctuation.
+  iterator()->Open(UTF8ToUTF16("א!ב\"ג#ד$ה%ו&ז'ח(ט)י*ך+כ,ל-ם.מ/"
+                               "ן:נ;ס<ע=ף>פ?ץ@צ[ק\\ר]ש^ת_א`ב{ג|ד}ה~ו"),
+                   GetParam(), BiDiLineIterator::CustomBehavior::NONE);
+
+  // Expect a single RTL run.
+  ASSERT_EQ(1, iterator()->CountRuns());
+
+  int start, length;
+  EXPECT_EQ(UBIDI_RTL, iterator()->GetVisualRun(0, &start, &length));
+  EXPECT_EQ(0, start);
+  EXPECT_EQ(65, length);
+
+  int end;
+  UBiDiLevel level;
+  iterator()->GetLogicalRun(0, &end, &level);
+  EXPECT_EQ(65, end);
+  EXPECT_EQ(1, level);
+}
+
+TEST_P(BiDiLineIteratorTest, RTLPunctuationAsURL) {
+  // This string features Hebrew characters interleaved with ASCII punctuation.
+  iterator()->Open(UTF8ToUTF16("א!ב\"ג#ד$ה%ו&ז'ח(ט)י*ך+כ,ל-ם.מ/"
+                               "ן:נ;ס<ע=ף>פ?ץ@צ[ק\\ר]ש^ת_א`ב{ג|ד}ה~ו"),
+                   GetParam(), BiDiLineIterator::CustomBehavior::AS_URL);
+
+  const int kStringSize = 65;
+
+  // Expect a primary RTL run, broken up by each of the 8 punctuation marks that
+  // are considered strong LTR (17 runs total).
+  struct {
+    int start;
+    UBiDiDirection dir;
+  } expected_runs[] = {
+      {0, UBIDI_RTL},  {5, UBIDI_LTR},   // '#'
+      {6, UBIDI_RTL},  {11, UBIDI_LTR},  // '&'
+      {12, UBIDI_RTL}, {27, UBIDI_LTR},  // '.'
+      {28, UBIDI_RTL}, {29, UBIDI_LTR},  // '/'
+      {30, UBIDI_RTL}, {31, UBIDI_LTR},  // ':'
+      {32, UBIDI_RTL}, {37, UBIDI_LTR},  // '='
+      {38, UBIDI_RTL}, {41, UBIDI_LTR},  // '?'
+      {42, UBIDI_RTL}, {43, UBIDI_LTR},  // '@'
+      {44, UBIDI_RTL},
+  };
+
+  ASSERT_EQ(arraysize(expected_runs),
+            static_cast<size_t>(iterator()->CountRuns()));
+
+  for (size_t i = 0; i < arraysize(expected_runs); ++i) {
+    const auto& expected_run = expected_runs[i];
+    int expected_run_end = i >= arraysize(expected_runs) - 1
+                               ? kStringSize
+                               : expected_runs[i + 1].start;
+
+    size_t visual_index = GetParam() == TextDirection::RIGHT_TO_LEFT
+                              ? arraysize(expected_runs) - 1 - i
+                              : i;
+    int start, length;
+    EXPECT_EQ(expected_run.dir,
+              iterator()->GetVisualRun(visual_index, &start, &length))
+        << "(i = " << i << ")";
+    EXPECT_EQ(expected_run.start, start) << "(i = " << i << ")";
+    EXPECT_EQ(expected_run_end - expected_run.start, length)
+        << "(i = " << i << ")";
+
+    int expected_level =
+        expected_run.dir == UBIDI_RTL
+            ? 1
+            : (GetParam() == TextDirection::RIGHT_TO_LEFT ? 2 : 0);
+    int end;
+    UBiDiLevel level;
+    iterator()->GetLogicalRun(expected_run.start, &end, &level);
+    EXPECT_EQ(expected_run_end, end) << "(i = " << i << ")";
+    EXPECT_EQ(expected_level, level) << "(i = " << i << ")";
+  }
+}
+
+INSTANTIATE_TEST_CASE_P(,
+                        BiDiLineIteratorTest,
+                        ::testing::Values(TextDirection::LEFT_TO_RIGHT,
+                                          TextDirection::RIGHT_TO_LEFT));
+
+}  // namespace
+}  // namespace i18n
+}  // namespace base
diff --git a/base/i18n/break_iterator.cc b/base/i18n/break_iterator.cc
new file mode 100644
index 0000000..251cd00
--- /dev/null
+++ b/base/i18n/break_iterator.cc
@@ -0,0 +1,191 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/i18n/break_iterator.h"
+
+#include <stdint.h>
+
+#include "base/logging.h"
+#include "third_party/icu/source/common/unicode/ubrk.h"
+#include "third_party/icu/source/common/unicode/uchar.h"
+#include "third_party/icu/source/common/unicode/ustring.h"
+
+namespace base {
+namespace i18n {
+
+const size_t npos = static_cast<size_t>(-1);
+
+BreakIterator::BreakIterator(const StringPiece16& str, BreakType break_type)
+    : iter_(nullptr),
+      string_(str),
+      break_type_(break_type),
+      prev_(npos),
+      pos_(0) {}
+
+BreakIterator::BreakIterator(const StringPiece16& str, const string16& rules)
+    : iter_(nullptr),
+      string_(str),
+      rules_(rules),
+      break_type_(RULE_BASED),
+      prev_(npos),
+      pos_(0) {}
+
+BreakIterator::~BreakIterator() {
+  if (iter_)
+    ubrk_close(static_cast<UBreakIterator*>(iter_));
+}
+
+bool BreakIterator::Init() {
+  UErrorCode status = U_ZERO_ERROR;
+  UParseError parse_error;
+  UBreakIteratorType break_type;
+  switch (break_type_) {
+    case BREAK_CHARACTER:
+      break_type = UBRK_CHARACTER;
+      break;
+    case BREAK_WORD:
+      break_type = UBRK_WORD;
+      break;
+    case BREAK_LINE:
+    case BREAK_NEWLINE:
+    case RULE_BASED: // (Keep compiler happy, break_type not used in this case)
+      break_type = UBRK_LINE;
+      break;
+    default:
+      NOTREACHED() << "invalid break_type_";
+      return false;
+  }
+  if (break_type_ == RULE_BASED) {
+    iter_ = ubrk_openRules(rules_.c_str(),
+                           static_cast<int32_t>(rules_.length()),
+                           string_.data(),
+                           static_cast<int32_t>(string_.size()),
+                           &parse_error,
+                           &status);
+    if (U_FAILURE(status)) {
+      NOTREACHED() << "ubrk_openRules failed to parse rule string at line "
+          << parse_error.line << ", offset " << parse_error.offset;
+    }
+  } else {
+    iter_ = ubrk_open(break_type, nullptr, string_.data(),
+                      static_cast<int32_t>(string_.size()), &status);
+    if (U_FAILURE(status)) {
+      NOTREACHED() << "ubrk_open failed for type " << break_type
+          << " with error " << status;
+    }
+  }
+
+  if (U_FAILURE(status)) {
+    return false;
+  }
+
+  // Move the iterator to the beginning of the string.
+  ubrk_first(static_cast<UBreakIterator*>(iter_));
+  return true;
+}
+
+bool BreakIterator::Advance() {
+  int32_t pos;
+  int32_t status;
+  prev_ = pos_;
+  switch (break_type_) {
+    case BREAK_CHARACTER:
+    case BREAK_WORD:
+    case BREAK_LINE:
+    case RULE_BASED:
+      pos = ubrk_next(static_cast<UBreakIterator*>(iter_));
+      if (pos == UBRK_DONE) {
+        pos_ = npos;
+        return false;
+      }
+      pos_ = static_cast<size_t>(pos);
+      return true;
+    case BREAK_NEWLINE:
+      do {
+        pos = ubrk_next(static_cast<UBreakIterator*>(iter_));
+        if (pos == UBRK_DONE)
+          break;
+        pos_ = static_cast<size_t>(pos);
+        status = ubrk_getRuleStatus(static_cast<UBreakIterator*>(iter_));
+      } while (status >= UBRK_LINE_SOFT && status < UBRK_LINE_SOFT_LIMIT);
+      if (pos == UBRK_DONE && prev_ == pos_) {
+        pos_ = npos;
+        return false;
+      }
+      return true;
+    default:
+      NOTREACHED() << "invalid break_type_";
+      return false;
+  }
+}
+
+bool BreakIterator::SetText(const base::char16* text, const size_t length) {
+  UErrorCode status = U_ZERO_ERROR;
+  ubrk_setText(static_cast<UBreakIterator*>(iter_),
+               text, length, &status);
+  pos_ = 0;  // implicit when ubrk_setText is done
+  prev_ = npos;
+  if (U_FAILURE(status)) {
+    NOTREACHED() << "ubrk_setText failed";
+    return false;
+  }
+  string_ = StringPiece16(text, length);
+  return true;
+}
+
+bool BreakIterator::IsWord() const {
+  return GetWordBreakStatus() == IS_WORD_BREAK;
+}
+
+BreakIterator::WordBreakStatus BreakIterator::GetWordBreakStatus() const {
+  int32_t status = ubrk_getRuleStatus(static_cast<UBreakIterator*>(iter_));
+  if (break_type_ != BREAK_WORD && break_type_ != RULE_BASED)
+    return IS_LINE_OR_CHAR_BREAK;
+  // In ICU 60, trying to advance past the end of the text does not change
+  // |status| so that |pos_| has to be checked as well as |status|.
+  // See http://bugs.icu-project.org/trac/ticket/13447 .
+  return (status == UBRK_WORD_NONE || pos_ == npos) ? IS_SKIPPABLE_WORD
+                                                    : IS_WORD_BREAK;
+}
+
+bool BreakIterator::IsEndOfWord(size_t position) const {
+  if (break_type_ != BREAK_WORD && break_type_ != RULE_BASED)
+    return false;
+
+  UBreakIterator* iter = static_cast<UBreakIterator*>(iter_);
+  UBool boundary = ubrk_isBoundary(iter, static_cast<int32_t>(position));
+  int32_t status = ubrk_getRuleStatus(iter);
+  return (!!boundary && status != UBRK_WORD_NONE);
+}
+
+bool BreakIterator::IsStartOfWord(size_t position) const {
+  if (break_type_ != BREAK_WORD && break_type_ != RULE_BASED)
+    return false;
+
+  UBreakIterator* iter = static_cast<UBreakIterator*>(iter_);
+  UBool boundary = ubrk_isBoundary(iter, static_cast<int32_t>(position));
+  ubrk_next(iter);
+  int32_t next_status = ubrk_getRuleStatus(iter);
+  return (!!boundary && next_status != UBRK_WORD_NONE);
+}
+
+bool BreakIterator::IsGraphemeBoundary(size_t position) const {
+  if (break_type_ != BREAK_CHARACTER)
+    return false;
+
+  UBreakIterator* iter = static_cast<UBreakIterator*>(iter_);
+  return !!ubrk_isBoundary(iter, static_cast<int32_t>(position));
+}
+
+string16 BreakIterator::GetString() const {
+  return GetStringPiece().as_string();
+}
+
+StringPiece16 BreakIterator::GetStringPiece() const {
+  DCHECK(prev_ != npos && pos_ != npos);
+  return string_.substr(prev_, pos_ - prev_);
+}
+
+}  // namespace i18n
+}  // namespace base
diff --git a/base/i18n/break_iterator.h b/base/i18n/break_iterator.h
new file mode 100644
index 0000000..dc30b64
--- /dev/null
+++ b/base/i18n/break_iterator.h
@@ -0,0 +1,182 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_I18N_BREAK_ITERATOR_H_
+#define BASE_I18N_BREAK_ITERATOR_H_
+
+#include <stddef.h>
+
+#include "base/i18n/base_i18n_export.h"
+#include "base/macros.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_piece.h"
+
+// The BreakIterator class iterates through the words, word breaks, and
+// line breaks in a UTF-16 string.
+//
+// It provides several modes, BREAK_WORD, BREAK_LINE, and BREAK_NEWLINE,
+// which modify how characters are aggregated into the returned string.
+//
+// Under BREAK_WORD mode, once a word is encountered any non-word
+// characters are not included in the returned string (e.g. in the
+// UTF-16 equivalent of the string " foo bar! ", the word breaks are at
+// the periods in ". .foo. .bar.!. .").
+// Note that Chinese/Japanese/Thai do not use spaces between words so that
+// boundaries can fall in the middle of a continuous run of non-space /
+// non-punctuation characters.
+//
+// Under BREAK_LINE mode, once a line breaking opportunity is encountered,
+// any non-word  characters are included in the returned string, breaking
+// only when a space-equivalent character or a line breaking opportunity
+// is encountered (e.g. in the UTF16-equivalent of the string " foo bar! ",
+// the breaks are at the periods in ". .foo .bar! .").
+//
+// Note that lines can be broken at any character/syllable/grapheme cluster
+// boundary in Chinese/Japanese/Korean and at word boundaries in Thai
+// (Thai does not use spaces between words). Therefore, this is NOT the same
+// as breaking only at space-equivalent characters where its former
+// name (BREAK_SPACE) implied.
+//
+// Under BREAK_NEWLINE mode, all characters are included in the returned
+// string, breaking only when a newline-equivalent character is encountered
+// (eg. in the UTF-16 equivalent of the string "foo\nbar!\n\n", the line
+// breaks are at the periods in ".foo\n.bar\n.\n.").
+//
+// To extract the words from a string, move a BREAK_WORD BreakIterator
+// through the string and test whether IsWord() is true. E.g.,
+//   BreakIterator iter(str, BreakIterator::BREAK_WORD);
+//   if (!iter.Init())
+//     return false;
+//   while (iter.Advance()) {
+//     if (iter.IsWord()) {
+//       // Region [iter.prev(), iter.pos()) contains a word.
+//       VLOG(1) << "word: " << iter.GetString();
+//     }
+//   }
+
+namespace base {
+namespace i18n {
+
+class BASE_I18N_EXPORT BreakIterator {
+ public:
+  enum BreakType {
+    BREAK_WORD,
+    BREAK_LINE,
+    // TODO(jshin): Remove this after reviewing call sites.
+    // If call sites really need break only on space-like characters
+    // implement it separately.
+    BREAK_SPACE = BREAK_LINE,
+    BREAK_NEWLINE,
+    BREAK_CHARACTER,
+    // But don't remove this one!
+    RULE_BASED,
+  };
+
+  enum WordBreakStatus {
+    // The end of text that the iterator recognizes as word characters.
+    // Non-word characters are things like punctuation and spaces.
+    IS_WORD_BREAK,
+    // Characters that the iterator can skip past, such as punctuation,
+    // whitespace, and, if using RULE_BASED mode, characters from another
+    // character set.
+    IS_SKIPPABLE_WORD,
+    // Only used if not in BREAK_WORD or RULE_BASED mode. This is returned for
+    // newlines, line breaks, and character breaks.
+    IS_LINE_OR_CHAR_BREAK
+  };
+
+  // Requires |str| to live as long as the BreakIterator does.
+  BreakIterator(const StringPiece16& str, BreakType break_type);
+  // Make a rule-based iterator. BreakType == RULE_BASED is implied.
+  // TODO(andrewhayden): This signature could easily be misinterpreted as
+  // "(const string16& str, const string16& locale)". We should do something
+  // better.
+  BreakIterator(const StringPiece16& str, const string16& rules);
+  ~BreakIterator();
+
+  // Init() must be called before any of the iterators are valid.
+  // Returns false if ICU failed to initialize.
+  bool Init();
+
+  // Advance to the next break.  Returns false if we've run past the end of
+  // the string.  (Note that the very last "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();
+
+  // Updates the text used by the iterator, resetting the iterator as if
+  // if Init() had been called again. Any old state is lost. Returns true
+  // unless there is an error setting the text.
+  bool SetText(const base::char16* text, const size_t length);
+
+  // Under BREAK_WORD mode, 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.)  Under BREAK_LINE and BREAK_NEWLINE modes,
+  // this distinction doesn't apply and it always returns false.
+  bool IsWord() const;
+
+  // Under BREAK_WORD mode:
+  //  - Returns IS_SKIPPABLE_WORD if non-word characters, such as punctuation or
+  //    spaces, are found.
+  //  - Returns IS_WORD_BREAK if the break we just hit is the end of a sequence
+  //    of word characters.
+  // Under RULE_BASED mode:
+  //  - Returns IS_SKIPPABLE_WORD if characters outside the rules' character set
+  //    or non-word characters, such as punctuation or spaces, are found.
+  //  - Returns IS_WORD_BREAK if the break we just hit is the end of a sequence
+  //    of word characters that are in the rules' character set.
+  // Not under BREAK_WORD or RULE_BASED mode:
+  //  - Returns IS_LINE_OR_CHAR_BREAK.
+  BreakIterator::WordBreakStatus GetWordBreakStatus() const;
+
+  // Under BREAK_WORD mode, returns true if |position| is at the end of word or
+  // at the start of word. It always returns false under BREAK_LINE and
+  // BREAK_NEWLINE modes.
+  bool IsEndOfWord(size_t position) const;
+  bool IsStartOfWord(size_t position) const;
+
+  // Under BREAK_CHARACTER mode, returns whether |position| is a Unicode
+  // grapheme boundary.
+  bool IsGraphemeBoundary(size_t position) const;
+
+  // Returns the string between prev() and pos().
+  // Advance() must have been called successfully at least once for pos() to
+  // have advanced to somewhere useful.
+  string16 GetString() const;
+
+  StringPiece16 GetStringPiece() const;
+
+  // Returns the value of pos() returned before Advance() was last called.
+  size_t prev() const { return prev_; }
+
+  // Returns the current break position within the string,
+  // or BreakIterator::npos when done.
+  size_t pos() const { return pos_; }
+
+ private:
+  // ICU iterator, avoiding ICU ubrk.h dependence.
+  // This is actually an ICU UBreakiterator* type, which turns out to be
+  // a typedef for a void* in the ICU headers. Using void* directly prevents
+  // callers from needing access to the ICU public headers directory.
+  void* iter_;
+
+  // The string we're iterating over. Can be changed with SetText(...)
+  StringPiece16 string_;
+
+  // Rules for our iterator. Mutually exclusive with break_type_.
+  const string16 rules_;
+
+  // The breaking style (word/space/newline). Mutually exclusive with rules_
+  BreakType break_type_;
+
+  // Previous and current iterator positions.
+  size_t prev_, pos_;
+
+  DISALLOW_COPY_AND_ASSIGN(BreakIterator);
+};
+
+}  // namespace i18n
+}  // namespace base
+
+#endif  // BASE_I18N_BREAK_ITERATOR_H_
diff --git a/base/i18n/break_iterator_unittest.cc b/base/i18n/break_iterator_unittest.cc
new file mode 100644
index 0000000..ed5de44
--- /dev/null
+++ b/base/i18n/break_iterator_unittest.cc
@@ -0,0 +1,584 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/i18n/break_iterator.h"
+
+#include <stddef.h>
+
+#include "base/macros.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace i18n {
+
+TEST(BreakIteratorTest, BreakWordEmpty) {
+  string16 empty;
+  BreakIterator iter(empty, BreakIterator::BREAK_WORD);
+  ASSERT_TRUE(iter.Init());
+  EXPECT_FALSE(iter.Advance());
+  EXPECT_FALSE(iter.IsWord());
+  EXPECT_FALSE(iter.Advance());  // Test unexpected advance after end.
+  EXPECT_FALSE(iter.IsWord());
+}
+
+TEST(BreakIteratorTest, BreakWord) {
+  string16 space(UTF8ToUTF16(" "));
+  string16 str(UTF8ToUTF16(" foo bar! \npouet boom"));
+  BreakIterator iter(str, BreakIterator::BREAK_WORD);
+  ASSERT_TRUE(iter.Init());
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_FALSE(iter.IsWord());
+  EXPECT_EQ(space, iter.GetString());
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_TRUE(iter.IsWord());
+  EXPECT_EQ(UTF8ToUTF16("foo"), iter.GetString());
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_FALSE(iter.IsWord());
+  EXPECT_EQ(space, iter.GetString());
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_TRUE(iter.IsWord());
+  EXPECT_EQ(UTF8ToUTF16("bar"), iter.GetString());
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_FALSE(iter.IsWord());
+  EXPECT_EQ(UTF8ToUTF16("!"), iter.GetString());
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_FALSE(iter.IsWord());
+  EXPECT_EQ(space, iter.GetString());
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_FALSE(iter.IsWord());
+  EXPECT_EQ(UTF8ToUTF16("\n"), iter.GetString());
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_TRUE(iter.IsWord());
+  EXPECT_EQ(UTF8ToUTF16("pouet"), iter.GetString());
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_FALSE(iter.IsWord());
+  EXPECT_EQ(space, iter.GetString());
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_TRUE(iter.IsWord());
+  EXPECT_EQ(UTF8ToUTF16("boom"), iter.GetString());
+  EXPECT_FALSE(iter.Advance());
+  EXPECT_FALSE(iter.IsWord());
+  EXPECT_FALSE(iter.Advance());  // Test unexpected advance after end.
+  EXPECT_FALSE(iter.IsWord());
+}
+
+TEST(BreakIteratorTest, BreakWordWide16) {
+  // Two greek words separated by space.
+  const string16 str(WideToUTF16(
+      L"\x03a0\x03b1\x03b3\x03ba\x03cc\x03c3\x03bc\x03b9"
+      L"\x03bf\x03c2\x0020\x0399\x03c3\x03c4\x03cc\x03c2"));
+  const string16 word1(str.substr(0, 10));
+  const string16 word2(str.substr(11, 5));
+  BreakIterator iter(str, BreakIterator::BREAK_WORD);
+  ASSERT_TRUE(iter.Init());
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_TRUE(iter.IsWord());
+  EXPECT_EQ(word1, iter.GetString());
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_FALSE(iter.IsWord());
+  EXPECT_EQ(UTF8ToUTF16(" "), iter.GetString());
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_TRUE(iter.IsWord());
+  EXPECT_EQ(word2, iter.GetString());
+  EXPECT_FALSE(iter.Advance());
+  EXPECT_FALSE(iter.IsWord());
+  EXPECT_FALSE(iter.Advance());  // Test unexpected advance after end.
+  EXPECT_FALSE(iter.IsWord());
+}
+
+TEST(BreakIteratorTest, BreakWordWide32) {
+  // U+1D49C MATHEMATICAL SCRIPT CAPITAL A
+  const char very_wide_char[] = "\xF0\x9D\x92\x9C";
+  const string16 str(
+      UTF8ToUTF16(base::StringPrintf("%s a", very_wide_char)));
+  const string16 very_wide_word(str.substr(0, 2));
+
+  BreakIterator iter(str, BreakIterator::BREAK_WORD);
+  ASSERT_TRUE(iter.Init());
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_TRUE(iter.IsWord());
+  EXPECT_EQ(very_wide_word, iter.GetString());
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_FALSE(iter.IsWord());
+  EXPECT_EQ(UTF8ToUTF16(" "), iter.GetString());
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_TRUE(iter.IsWord());
+  EXPECT_EQ(UTF8ToUTF16("a"), iter.GetString());
+  EXPECT_FALSE(iter.Advance());
+  EXPECT_FALSE(iter.IsWord());
+  EXPECT_FALSE(iter.Advance());  // Test unexpected advance after end.
+  EXPECT_FALSE(iter.IsWord());
+}
+
+TEST(BreakIteratorTest, BreakWordThai) {
+  // Terms in Thai, without spaces in between.
+  const char term1[] = "พิมพ์";
+  const char term2[] = "น้อย";
+  const char term3[] = "ลง";
+  const string16 str(UTF8ToUTF16(base::JoinString({term1, term2, term3}, "")));
+
+  BreakIterator iter(str, BreakIterator::BREAK_WORD);
+  ASSERT_TRUE(iter.Init());
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_TRUE(iter.IsWord());
+  EXPECT_EQ(UTF8ToUTF16(term1), iter.GetString());
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_TRUE(iter.IsWord());
+  EXPECT_EQ(UTF8ToUTF16(term2), iter.GetString());
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_TRUE(iter.IsWord());
+  EXPECT_EQ(UTF8ToUTF16(term3), iter.GetString());
+  EXPECT_FALSE(iter.Advance());
+  EXPECT_FALSE(iter.IsWord());
+}
+
+// In some languages, the words are not broken by spaces. ICU provides a huge
+// dictionary to detect word boundaries in Thai, Chinese, Japanese, Burmese,
+// and Khmer. Due to the size of such a table, the part for Chinese and
+// Japanese is not shipped on mobile.
+#if !(defined(OS_IOS) || defined(OS_ANDROID))
+
+TEST(BreakIteratorTest, BreakWordChinese) {
+  // Terms in Traditional Chinese, without spaces in between.
+  const char term1[] = "瀏覽";
+  const char term2[] = "速度";
+  const char term3[] = "飛快";
+  const string16 str(UTF8ToUTF16(base::JoinString({term1, term2, term3}, "")));
+
+  BreakIterator iter(str, BreakIterator::BREAK_WORD);
+  ASSERT_TRUE(iter.Init());
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_TRUE(iter.IsWord());
+  EXPECT_EQ(UTF8ToUTF16(term1), iter.GetString());
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_TRUE(iter.IsWord());
+  EXPECT_EQ(UTF8ToUTF16(term2), iter.GetString());
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_TRUE(iter.IsWord());
+  EXPECT_EQ(UTF8ToUTF16(term3), iter.GetString());
+  EXPECT_FALSE(iter.Advance());
+  EXPECT_FALSE(iter.IsWord());
+}
+
+TEST(BreakIteratorTest, BreakWordJapanese) {
+  // Terms in Japanese, without spaces in between.
+  const char term1[] = "モバイル";
+  const char term2[] = "でも";
+  const string16 str(UTF8ToUTF16(base::JoinString({term1, term2}, "")));
+
+  BreakIterator iter(str, BreakIterator::BREAK_WORD);
+  ASSERT_TRUE(iter.Init());
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_TRUE(iter.IsWord());
+  EXPECT_EQ(UTF8ToUTF16(term1), iter.GetString());
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_TRUE(iter.IsWord());
+  EXPECT_EQ(UTF8ToUTF16(term2), iter.GetString());
+  EXPECT_FALSE(iter.Advance());
+  EXPECT_FALSE(iter.IsWord());
+}
+
+TEST(BreakIteratorTest, BreakWordChineseEnglish) {
+  // Terms in Simplified Chinese mixed with English and wide punctuations.
+  string16 space(UTF8ToUTF16(" "));
+  const char token1[] = "下载";
+  const char token2[] = "Chrome";
+  const char token3[] = "(";
+  const char token4[] = "Mac";
+  const char token5[] = "版";
+  const char token6[] = ")";
+  const string16 str(UTF8ToUTF16(base::JoinString(
+      {token1, " ", token2, token3, token4, " ", token5, token6}, "")));
+
+  BreakIterator iter(str, BreakIterator::BREAK_WORD);
+  ASSERT_TRUE(iter.Init());
+
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_TRUE(iter.IsWord());
+  EXPECT_EQ(UTF8ToUTF16(token1), iter.GetString());
+
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_FALSE(iter.IsWord());
+  EXPECT_EQ(space, iter.GetString());
+
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_TRUE(iter.IsWord());
+  EXPECT_EQ(UTF8ToUTF16(token2), iter.GetString());
+
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_FALSE(iter.IsWord());
+  EXPECT_EQ(UTF8ToUTF16(token3), iter.GetString());
+
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_TRUE(iter.IsWord());
+  EXPECT_EQ(UTF8ToUTF16(token4), iter.GetString());
+
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_FALSE(iter.IsWord());
+  EXPECT_EQ(space, iter.GetString());
+
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_TRUE(iter.IsWord());
+  EXPECT_EQ(UTF8ToUTF16(token5), iter.GetString());
+
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_FALSE(iter.IsWord());
+  EXPECT_EQ(UTF8ToUTF16(token6), iter.GetString());
+
+  EXPECT_FALSE(iter.Advance());
+  EXPECT_FALSE(iter.IsWord());
+}
+
+#endif  // !(defined(OS_IOS) || defined(OS_ANDROID))
+
+TEST(BreakIteratorTest, BreakSpaceEmpty) {
+  string16 empty;
+  BreakIterator iter(empty, BreakIterator::BREAK_SPACE);
+  ASSERT_TRUE(iter.Init());
+  EXPECT_FALSE(iter.Advance());
+  EXPECT_FALSE(iter.IsWord());
+  EXPECT_FALSE(iter.Advance());  // Test unexpected advance after end.
+  EXPECT_FALSE(iter.IsWord());
+}
+
+TEST(BreakIteratorTest, BreakSpace) {
+  string16 str(UTF8ToUTF16(" foo bar! \npouet boom"));
+  BreakIterator iter(str, BreakIterator::BREAK_SPACE);
+  ASSERT_TRUE(iter.Init());
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_FALSE(iter.IsWord());
+  EXPECT_EQ(UTF8ToUTF16(" "), iter.GetString());
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_FALSE(iter.IsWord());
+  EXPECT_EQ(UTF8ToUTF16("foo "), iter.GetString());
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_FALSE(iter.IsWord());
+  EXPECT_EQ(UTF8ToUTF16("bar! \n"), iter.GetString());
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_FALSE(iter.IsWord());
+  EXPECT_EQ(UTF8ToUTF16("pouet "), iter.GetString());
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_FALSE(iter.IsWord());
+  EXPECT_EQ(UTF8ToUTF16("boom"), iter.GetString());
+  EXPECT_FALSE(iter.Advance());
+  EXPECT_FALSE(iter.IsWord());
+  EXPECT_FALSE(iter.Advance());  // Test unexpected advance after end.
+  EXPECT_FALSE(iter.IsWord());
+}
+
+TEST(BreakIteratorTest, BreakSpaceSP) {
+  string16 str(UTF8ToUTF16(" foo bar! \npouet boom "));
+  BreakIterator iter(str, BreakIterator::BREAK_SPACE);
+  ASSERT_TRUE(iter.Init());
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_FALSE(iter.IsWord());
+  EXPECT_EQ(UTF8ToUTF16(" "), iter.GetString());
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_FALSE(iter.IsWord());
+  EXPECT_EQ(UTF8ToUTF16("foo "), iter.GetString());
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_FALSE(iter.IsWord());
+  EXPECT_EQ(UTF8ToUTF16("bar! \n"), iter.GetString());
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_FALSE(iter.IsWord());
+  EXPECT_EQ(UTF8ToUTF16("pouet "), iter.GetString());
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_FALSE(iter.IsWord());
+  EXPECT_EQ(UTF8ToUTF16("boom "), iter.GetString());
+  EXPECT_FALSE(iter.Advance());
+  EXPECT_FALSE(iter.IsWord());
+  EXPECT_FALSE(iter.Advance());  // Test unexpected advance after end.
+  EXPECT_FALSE(iter.IsWord());
+}
+
+TEST(BreakIteratorTest, BreakSpacekWide16) {
+  // Two Greek words.
+  const string16 str(WideToUTF16(
+      L"\x03a0\x03b1\x03b3\x03ba\x03cc\x03c3\x03bc\x03b9"
+      L"\x03bf\x03c2\x0020\x0399\x03c3\x03c4\x03cc\x03c2"));
+  const string16 word1(str.substr(0, 11));
+  const string16 word2(str.substr(11, 5));
+  BreakIterator iter(str, BreakIterator::BREAK_SPACE);
+  ASSERT_TRUE(iter.Init());
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_FALSE(iter.IsWord());
+  EXPECT_EQ(word1, iter.GetString());
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_FALSE(iter.IsWord());
+  EXPECT_EQ(word2, iter.GetString());
+  EXPECT_FALSE(iter.Advance());
+  EXPECT_FALSE(iter.IsWord());
+  EXPECT_FALSE(iter.Advance());  // Test unexpected advance after end.
+  EXPECT_FALSE(iter.IsWord());
+}
+
+TEST(BreakIteratorTest, BreakSpaceWide32) {
+  // U+1D49C MATHEMATICAL SCRIPT CAPITAL A
+  const char very_wide_char[] = "\xF0\x9D\x92\x9C";
+  const string16 str(
+      UTF8ToUTF16(base::StringPrintf("%s a", very_wide_char)));
+  const string16 very_wide_word(str.substr(0, 3));
+
+  BreakIterator iter(str, BreakIterator::BREAK_SPACE);
+  ASSERT_TRUE(iter.Init());
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_FALSE(iter.IsWord());
+  EXPECT_EQ(very_wide_word, iter.GetString());
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_FALSE(iter.IsWord());
+  EXPECT_EQ(UTF8ToUTF16("a"), iter.GetString());
+  EXPECT_FALSE(iter.Advance());
+  EXPECT_FALSE(iter.IsWord());
+  EXPECT_FALSE(iter.Advance());  // Test unexpected advance after end.
+  EXPECT_FALSE(iter.IsWord());
+}
+
+TEST(BreakIteratorTest, BreakLineEmpty) {
+  string16 empty;
+  BreakIterator iter(empty, BreakIterator::BREAK_NEWLINE);
+  ASSERT_TRUE(iter.Init());
+  EXPECT_FALSE(iter.Advance());
+  EXPECT_FALSE(iter.IsWord());
+  EXPECT_FALSE(iter.Advance());   // Test unexpected advance after end.
+  EXPECT_FALSE(iter.IsWord());
+}
+
+TEST(BreakIteratorTest, BreakLine) {
+  string16 nl(UTF8ToUTF16("\n"));
+  string16 str(UTF8ToUTF16("\nfoo bar!\n\npouet boom"));
+  BreakIterator iter(str, BreakIterator::BREAK_NEWLINE);
+  ASSERT_TRUE(iter.Init());
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_FALSE(iter.IsWord());
+  EXPECT_EQ(nl, iter.GetString());
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_FALSE(iter.IsWord());
+  EXPECT_EQ(UTF8ToUTF16("foo bar!\n"), iter.GetString());
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_FALSE(iter.IsWord());
+  EXPECT_EQ(nl, iter.GetString());
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_FALSE(iter.IsWord());
+  EXPECT_EQ(UTF8ToUTF16("pouet boom"), iter.GetString());
+  EXPECT_FALSE(iter.Advance());
+  EXPECT_FALSE(iter.IsWord());
+  EXPECT_FALSE(iter.Advance());   // Test unexpected advance after end.
+  EXPECT_FALSE(iter.IsWord());
+}
+
+TEST(BreakIteratorTest, BreakLineNL) {
+  string16 nl(UTF8ToUTF16("\n"));
+  string16 str(UTF8ToUTF16("\nfoo bar!\n\npouet boom\n"));
+  BreakIterator iter(str, BreakIterator::BREAK_NEWLINE);
+  ASSERT_TRUE(iter.Init());
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_FALSE(iter.IsWord());
+  EXPECT_EQ(nl, iter.GetString());
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_FALSE(iter.IsWord());
+  EXPECT_EQ(UTF8ToUTF16("foo bar!\n"), iter.GetString());
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_FALSE(iter.IsWord());
+  EXPECT_EQ(nl, iter.GetString());
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_FALSE(iter.IsWord());
+  EXPECT_EQ(UTF8ToUTF16("pouet boom\n"), iter.GetString());
+  EXPECT_FALSE(iter.Advance());
+  EXPECT_FALSE(iter.IsWord());
+  EXPECT_FALSE(iter.Advance());   // Test unexpected advance after end.
+  EXPECT_FALSE(iter.IsWord());
+}
+
+TEST(BreakIteratorTest, BreakLineWide16) {
+  // Two Greek words separated by newline.
+  const string16 str(WideToUTF16(
+      L"\x03a0\x03b1\x03b3\x03ba\x03cc\x03c3\x03bc\x03b9"
+      L"\x03bf\x03c2\x000a\x0399\x03c3\x03c4\x03cc\x03c2"));
+  const string16 line1(str.substr(0, 11));
+  const string16 line2(str.substr(11, 5));
+  BreakIterator iter(str, BreakIterator::BREAK_NEWLINE);
+  ASSERT_TRUE(iter.Init());
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_FALSE(iter.IsWord());
+  EXPECT_EQ(line1, iter.GetString());
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_FALSE(iter.IsWord());
+  EXPECT_EQ(line2, iter.GetString());
+  EXPECT_FALSE(iter.Advance());
+  EXPECT_FALSE(iter.IsWord());
+  EXPECT_FALSE(iter.Advance());   // Test unexpected advance after end.
+  EXPECT_FALSE(iter.IsWord());
+}
+
+TEST(BreakIteratorTest, BreakLineWide32) {
+  // U+1D49C MATHEMATICAL SCRIPT CAPITAL A
+  const char very_wide_char[] = "\xF0\x9D\x92\x9C";
+  const string16 str(
+      UTF8ToUTF16(base::StringPrintf("%s\na", very_wide_char)));
+  const string16 very_wide_line(str.substr(0, 3));
+  BreakIterator iter(str, BreakIterator::BREAK_NEWLINE);
+  ASSERT_TRUE(iter.Init());
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_FALSE(iter.IsWord());
+  EXPECT_EQ(very_wide_line, iter.GetString());
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_FALSE(iter.IsWord());
+  EXPECT_EQ(UTF8ToUTF16("a"), iter.GetString());
+  EXPECT_FALSE(iter.Advance());
+  EXPECT_FALSE(iter.IsWord());
+  EXPECT_FALSE(iter.Advance());   // Test unexpected advance after end.
+  EXPECT_FALSE(iter.IsWord());
+}
+
+TEST(BreakIteratorTest, BreakCharacter) {
+  static const wchar_t* kCharacters[] = {
+    // An English word consisting of four ASCII characters.
+    L"w", L"o", L"r", L"d", L" ",
+    // A Hindi word (which means "Hindi") consisting of three Devanagari
+    // characters.
+    L"\x0939\x093F", L"\x0928\x094D", L"\x0926\x0940", L" ",
+    // A Thai word (which means "feel") consisting of three Thai characters.
+    L"\x0E23\x0E39\x0E49", L"\x0E2A\x0E36", L"\x0E01", L" ",
+  };
+  std::vector<string16> characters;
+  string16 text;
+  for (size_t i = 0; i < arraysize(kCharacters); ++i) {
+    characters.push_back(WideToUTF16(kCharacters[i]));
+    text.append(characters.back());
+  }
+  BreakIterator iter(text, BreakIterator::BREAK_CHARACTER);
+  ASSERT_TRUE(iter.Init());
+  for (size_t i = 0; i < arraysize(kCharacters); ++i) {
+    EXPECT_TRUE(iter.Advance());
+    EXPECT_EQ(characters[i], iter.GetString());
+  }
+}
+
+// Test for https://code.google.com/p/chromium/issues/detail?id=411213
+// We should be able to get valid substrings with GetString() function
+// after setting new content by calling SetText().
+TEST(BreakIteratorTest, GetStringAfterSetText) {
+  const string16 initial_string(ASCIIToUTF16("str"));
+  BreakIterator iter(initial_string, BreakIterator::BREAK_WORD);
+  ASSERT_TRUE(iter.Init());
+
+  const string16 long_string(ASCIIToUTF16("another,string"));
+  EXPECT_TRUE(iter.SetText(long_string.c_str(), long_string.size()));
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_TRUE(iter.Advance());  // Advance to ',' in |long_string|
+
+  // Check that the current position is out of bounds of the |initial_string|.
+  EXPECT_LT(initial_string.size(), iter.pos());
+
+  // Check that we can get a valid substring of |long_string|.
+  EXPECT_EQ(ASCIIToUTF16(","), iter.GetString());
+}
+
+TEST(BreakIteratorTest, GetStringPiece) {
+  const string16 initial_string(ASCIIToUTF16("some string"));
+  BreakIterator iter(initial_string, BreakIterator::BREAK_WORD);
+  ASSERT_TRUE(iter.Init());
+
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_EQ(iter.GetString(), iter.GetStringPiece().as_string());
+  EXPECT_EQ(StringPiece16(ASCIIToUTF16("some")), iter.GetStringPiece());
+
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_EQ(iter.GetString(), iter.GetStringPiece().as_string());
+  EXPECT_EQ(StringPiece16(ASCIIToUTF16("string")), iter.GetStringPiece());
+}
+
+// Make sure that when not in RULE_BASED or BREAK_WORD mode we're getting
+// IS_LINE_OR_CHAR_BREAK.
+TEST(BreakIteratorTest, GetWordBreakStatusBreakLine) {
+  // A string containing the English word "foo", followed by two Khmer
+  // characters, the English word "Can", and then two Russian characters and
+  // punctuation.
+  base::string16 text(
+      base::WideToUTF16(L"foo \x1791\x17C1 \nCan \x041C\x0438..."));
+  BreakIterator iter(text, BreakIterator::BREAK_LINE);
+  ASSERT_TRUE(iter.Init());
+
+  EXPECT_TRUE(iter.Advance());
+  // Finds "foo" and the space.
+  EXPECT_EQ(base::UTF8ToUTF16("foo "), iter.GetString());
+  EXPECT_EQ(iter.GetWordBreakStatus(), BreakIterator::IS_LINE_OR_CHAR_BREAK);
+  EXPECT_TRUE(iter.Advance());
+  // Finds the Khmer characters, the next space, and the newline.
+  EXPECT_EQ(base::WideToUTF16(L"\x1791\x17C1 \n"), iter.GetString());
+  EXPECT_EQ(iter.GetWordBreakStatus(), BreakIterator::IS_LINE_OR_CHAR_BREAK);
+  EXPECT_TRUE(iter.Advance());
+  // Finds "Can" and the space.
+  EXPECT_EQ(base::UTF8ToUTF16("Can "), iter.GetString());
+  EXPECT_EQ(iter.GetWordBreakStatus(), BreakIterator::IS_LINE_OR_CHAR_BREAK);
+  EXPECT_TRUE(iter.Advance());
+  // Finds the Russian characters and periods.
+  EXPECT_EQ(base::WideToUTF16(L"\x041C\x0438..."), iter.GetString());
+  EXPECT_EQ(iter.GetWordBreakStatus(), BreakIterator::IS_LINE_OR_CHAR_BREAK);
+  EXPECT_FALSE(iter.Advance());
+}
+
+// Make sure that in BREAK_WORD mode we're getting IS_WORD_BREAK and
+// IS_SKIPPABLE_WORD when we should be. IS_WORD_BREAK should be returned when we
+// finish going over non-punctuation characters while IS_SKIPPABLE_WORD should
+// be returned on punctuation and spaces.
+TEST(BreakIteratorTest, GetWordBreakStatusBreakWord) {
+  // A string containing the English word "foo", followed by two Khmer
+  // characters, the English word "Can", and then two Russian characters and
+  // punctuation.
+  base::string16 text(
+      base::WideToUTF16(L"foo \x1791\x17C1 \nCan \x041C\x0438..."));
+  BreakIterator iter(text, BreakIterator::BREAK_WORD);
+  ASSERT_TRUE(iter.Init());
+
+  EXPECT_TRUE(iter.Advance());
+  // Finds "foo".
+  EXPECT_EQ(base::UTF8ToUTF16("foo"), iter.GetString());
+  EXPECT_EQ(iter.GetWordBreakStatus(), BreakIterator::IS_WORD_BREAK);
+  EXPECT_TRUE(iter.Advance());
+  // Finds the space, and the Khmer characters.
+  EXPECT_EQ(base::UTF8ToUTF16(" "), iter.GetString());
+  EXPECT_EQ(iter.GetWordBreakStatus(), BreakIterator::IS_SKIPPABLE_WORD);
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_EQ(base::WideToUTF16(L"\x1791\x17C1"), iter.GetString());
+  EXPECT_EQ(iter.GetWordBreakStatus(), BreakIterator::IS_WORD_BREAK);
+  EXPECT_TRUE(iter.Advance());
+  // Finds the space and the newline.
+  EXPECT_EQ(base::UTF8ToUTF16(" "), iter.GetString());
+  EXPECT_EQ(iter.GetWordBreakStatus(), BreakIterator::IS_SKIPPABLE_WORD);
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_EQ(base::UTF8ToUTF16("\n"), iter.GetString());
+  EXPECT_EQ(iter.GetWordBreakStatus(), BreakIterator::IS_SKIPPABLE_WORD);
+  EXPECT_TRUE(iter.Advance());
+  // Finds "Can".
+  EXPECT_EQ(base::UTF8ToUTF16("Can"), iter.GetString());
+  EXPECT_EQ(iter.GetWordBreakStatus(), BreakIterator::IS_WORD_BREAK);
+  EXPECT_TRUE(iter.Advance());
+  // Finds the space and the Russian characters.
+  EXPECT_EQ(base::UTF8ToUTF16(" "), iter.GetString());
+  EXPECT_EQ(iter.GetWordBreakStatus(), BreakIterator::IS_SKIPPABLE_WORD);
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_EQ(base::WideToUTF16(L"\x041C\x0438"), iter.GetString());
+  EXPECT_EQ(iter.GetWordBreakStatus(), BreakIterator::IS_WORD_BREAK);
+  EXPECT_TRUE(iter.Advance());
+  // Finds the trailing periods.
+  EXPECT_EQ(base::UTF8ToUTF16("."), iter.GetString());
+  EXPECT_EQ(iter.GetWordBreakStatus(), BreakIterator::IS_SKIPPABLE_WORD);
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_EQ(base::UTF8ToUTF16("."), iter.GetString());
+  EXPECT_EQ(iter.GetWordBreakStatus(), BreakIterator::IS_SKIPPABLE_WORD);
+  EXPECT_TRUE(iter.Advance());
+  EXPECT_EQ(base::UTF8ToUTF16("."), iter.GetString());
+  EXPECT_EQ(iter.GetWordBreakStatus(), BreakIterator::IS_SKIPPABLE_WORD);
+  EXPECT_FALSE(iter.Advance());
+}
+
+}  // namespace i18n
+}  // namespace base
diff --git a/base/i18n/build_utf8_validator_tables.cc b/base/i18n/build_utf8_validator_tables.cc
new file mode 100644
index 0000000..0cdcc35
--- /dev/null
+++ b/base/i18n/build_utf8_validator_tables.cc
@@ -0,0 +1,470 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Create a state machine for validating UTF-8. The algorithm in brief:
+// 1. Convert the complete unicode range of code points, except for the
+//    surrogate code points, to an ordered array of sequences of bytes in
+//    UTF-8.
+// 2. Convert individual bytes to ranges, starting from the right of each byte
+//    sequence. For each range, ensure the bytes on the left and the ranges
+//    on the right are the identical.
+// 3. Convert the resulting list of ranges into a state machine, collapsing
+//    identical states.
+// 4. Convert the state machine to an array of bytes.
+// 5. Output as a C++ file.
+//
+// To use:
+//  $ ninja -C out/Release build_utf8_validator_tables
+//  $ out/Release/build_utf8_validator_tables
+//                                   --output=base/i18n/utf8_validator_tables.cc
+//  $ git add base/i18n/utf8_validator_tables.cc
+//
+// Because the table is not expected to ever change, it is checked into the
+// repository rather than being regenerated at build time.
+//
+// This code uses type uint8_t throughout to represent bytes, to avoid
+// signed/unsigned char confusion.
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <algorithm>
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/strings/stringprintf.h"
+#include "third_party/icu/source/common/unicode/utf8.h"
+
+namespace {
+
+const char kHelpText[] =
+    "Usage: build_utf8_validator_tables [ --help ] [ --output=<file> ]\n";
+
+const char kProlog[] =
+    "// Copyright 2013 The Chromium Authors. All rights reserved.\n"
+    "// Use of this source code is governed by a BSD-style license that can "
+    "be\n"
+    "// found in the LICENSE file.\n"
+    "\n"
+    "// This file is auto-generated by build_utf8_validator_tables.\n"
+    "// DO NOT EDIT.\n"
+    "\n"
+    "#include \"base/i18n/utf8_validator_tables.h\"\n"
+    "\n"
+    "namespace base {\n"
+    "namespace internal {\n"
+    "\n"
+    "const uint8_t kUtf8ValidatorTables[] = {\n";
+
+const char kEpilog[] =
+    "};\n"
+    "\n"
+    "const size_t kUtf8ValidatorTablesSize = arraysize(kUtf8ValidatorTables);\n"
+    "\n"
+    "}  // namespace internal\n"
+    "}  // namespace base\n";
+
+// Ranges are inclusive at both ends--they represent [from, to]
+class Range {
+ public:
+  // Ranges always start with just one byte.
+  explicit Range(uint8_t value) : from_(value), to_(value) {}
+
+  // Range objects are copyable and assignable to be used in STL
+  // containers. Since they only contain non-pointer POD types, the default copy
+  // constructor, assignment operator and destructor will work.
+
+  // Add a byte to the range. We intentionally only support adding a byte at the
+  // end, since that is the only operation the code needs.
+  void AddByte(uint8_t to) {
+    CHECK(to == to_ + 1);
+    to_ = to;
+  }
+
+  uint8_t from() const { return from_; }
+  uint8_t to() const { return to_; }
+
+  bool operator<(const Range& rhs) const {
+    return (from() < rhs.from() || (from() == rhs.from() && to() < rhs.to()));
+  }
+
+  bool operator==(const Range& rhs) const {
+    return from() == rhs.from() && to() == rhs.to();
+  }
+
+ private:
+  uint8_t from_;
+  uint8_t to_;
+};
+
+// A vector of Ranges is like a simple regular expression--it corresponds to
+// a set of strings of the same length that have bytes in each position in
+// the appropriate range.
+typedef std::vector<Range> StringSet;
+
+// A UTF-8 "character" is represented by a sequence of bytes.
+typedef std::vector<uint8_t> Character;
+
+// In the second stage of the algorithm, we want to convert a large list of
+// Characters into a small list of StringSets.
+struct Pair {
+  Character character;
+  StringSet set;
+};
+
+typedef std::vector<Pair> PairVector;
+
+// A class to print a table of numbers in the same style as clang-format.
+class TablePrinter {
+ public:
+  explicit TablePrinter(FILE* stream)
+      : stream_(stream), values_on_this_line_(0), current_offset_(0) {}
+
+  void PrintValue(uint8_t value) {
+    if (values_on_this_line_ == 0) {
+      fputs("   ", stream_);
+    } else if (values_on_this_line_ == kMaxValuesPerLine) {
+      fprintf(stream_, "  // 0x%02x\n   ", current_offset_);
+      values_on_this_line_ = 0;
+    }
+    fprintf(stream_, " 0x%02x,", static_cast<int>(value));
+    ++values_on_this_line_;
+    ++current_offset_;
+  }
+
+  void NewLine() {
+    while (values_on_this_line_ < kMaxValuesPerLine) {
+      fputs("      ", stream_);
+      ++values_on_this_line_;
+    }
+    fprintf(stream_, "  // 0x%02x\n", current_offset_);
+    values_on_this_line_ = 0;
+  }
+
+ private:
+  // stdio stream. Not owned.
+  FILE* stream_;
+
+  // Number of values so far printed on this line.
+  int values_on_this_line_;
+
+  // Total values printed so far.
+  int current_offset_;
+
+  static const int kMaxValuesPerLine = 8;
+
+  DISALLOW_COPY_AND_ASSIGN(TablePrinter);
+};
+
+// Start by filling a PairVector with characters. The resulting vector goes from
+// "\x00" to "\xf4\x8f\xbf\xbf".
+PairVector InitializeCharacters() {
+  PairVector vector;
+  for (int i = 0; i <= 0x10FFFF; ++i) {
+    if (i >= 0xD800 && i < 0xE000) {
+      // Surrogate codepoints are not permitted. Non-character code points are
+      // explicitly permitted.
+      continue;
+    }
+    uint8_t bytes[4];
+    unsigned int offset = 0;
+    UBool is_error = false;
+    U8_APPEND(bytes, offset, arraysize(bytes), i, is_error);
+    DCHECK(!is_error);
+    DCHECK_GT(offset, 0u);
+    DCHECK_LE(offset, arraysize(bytes));
+    Pair pair = {Character(bytes, bytes + offset), StringSet()};
+    vector.push_back(pair);
+  }
+  return vector;
+}
+
+// Construct a new Pair from |character| and the concatenation of |new_range|
+// and |existing_set|, and append it to |pairs|.
+void ConstructPairAndAppend(const Character& character,
+                            const Range& new_range,
+                            const StringSet& existing_set,
+                            PairVector* pairs) {
+  Pair new_pair = {character, StringSet(1, new_range)};
+  new_pair.set.insert(
+      new_pair.set.end(), existing_set.begin(), existing_set.end());
+  pairs->push_back(new_pair);
+}
+
+// Each pass over the PairVector strips one byte off the right-hand-side of the
+// characters and adds a range to the set on the right. For example, the first
+// pass converts the range from "\xe0\xa0\x80" to "\xe0\xa0\xbf" to ("\xe0\xa0",
+// [\x80-\xbf]), then the second pass converts the range from ("\xe0\xa0",
+// [\x80-\xbf]) to ("\xe0\xbf", [\x80-\xbf]) to ("\xe0",
+// [\xa0-\xbf][\x80-\xbf]).
+void MoveRightMostCharToSet(PairVector* pairs) {
+  PairVector new_pairs;
+  PairVector::const_iterator it = pairs->begin();
+  while (it != pairs->end() && it->character.empty()) {
+    new_pairs.push_back(*it);
+    ++it;
+  }
+  CHECK(it != pairs->end());
+  Character unconverted_bytes(it->character.begin(), it->character.end() - 1);
+  Range new_range(it->character.back());
+  StringSet converted = it->set;
+  ++it;
+  while (it != pairs->end()) {
+    const Pair& current_pair = *it++;
+    if (current_pair.character.size() == unconverted_bytes.size() + 1 &&
+        std::equal(unconverted_bytes.begin(),
+                   unconverted_bytes.end(),
+                   current_pair.character.begin()) &&
+        converted == current_pair.set) {
+      // The particular set of UTF-8 codepoints we are validating guarantees
+      // that each byte range will be contiguous. This would not necessarily be
+      // true for an arbitrary set of UTF-8 codepoints.
+      DCHECK_EQ(new_range.to() + 1, current_pair.character.back());
+      new_range.AddByte(current_pair.character.back());
+      continue;
+    }
+    ConstructPairAndAppend(unconverted_bytes, new_range, converted, &new_pairs);
+    unconverted_bytes = Character(current_pair.character.begin(),
+                                  current_pair.character.end() - 1);
+    new_range = Range(current_pair.character.back());
+    converted = current_pair.set;
+  }
+  ConstructPairAndAppend(unconverted_bytes, new_range, converted, &new_pairs);
+  new_pairs.swap(*pairs);
+}
+
+void MoveAllCharsToSets(PairVector* pairs) {
+  // Since each pass of the function moves one character, and UTF-8 sequences
+  // are at most 4 characters long, this simply runs the algorithm four times.
+  for (int i = 0; i < 4; ++i) {
+    MoveRightMostCharToSet(pairs);
+  }
+#if DCHECK_IS_ON()
+  for (PairVector::const_iterator it = pairs->begin(); it != pairs->end();
+       ++it) {
+    DCHECK(it->character.empty());
+  }
+#endif
+}
+
+// Logs the generated string sets in regular-expression style, ie. [\x00-\x7f],
+// [\xc2-\xdf][\x80-\xbf], etc. This can be a useful sanity-check that the
+// algorithm is working. Use the command-line option
+// --vmodule=build_utf8_validator_tables=1 to see this output.
+void LogStringSets(const PairVector& pairs) {
+  for (PairVector::const_iterator pair_it = pairs.begin();
+       pair_it != pairs.end();
+       ++pair_it) {
+    std::string set_as_string;
+    for (StringSet::const_iterator set_it = pair_it->set.begin();
+         set_it != pair_it->set.end();
+         ++set_it) {
+      set_as_string += base::StringPrintf("[\\x%02x-\\x%02x]",
+                                          static_cast<int>(set_it->from()),
+                                          static_cast<int>(set_it->to()));
+    }
+    VLOG(1) << set_as_string;
+  }
+}
+
+// A single state in the state machine is represented by a sorted vector of
+// start bytes and target states. All input bytes in the range between the start
+// byte and the next entry in the vector (or 0xFF) result in a transition to the
+// target state.
+struct StateRange {
+  uint8_t from;
+  uint8_t target_state;
+};
+
+typedef std::vector<StateRange> State;
+
+// Generates a state where all bytes go to state 1 (invalid). This is also used
+// as an initialiser for other states (since bytes from outside the desired
+// range are invalid).
+State GenerateInvalidState() {
+  const StateRange range = {0, 1};
+  return State(1, range);
+}
+
+// A map from a state (ie. a set of strings which will match from this state) to
+// a number (which is an index into the array of states).
+typedef std::map<StringSet, uint8_t> StateMap;
+
+// Create a new state corresponding to |set|, add it |states| and |state_map|
+// and return the index it was given in |states|.
+uint8_t MakeState(const StringSet& set,
+                  std::vector<State>* states,
+                  StateMap* state_map) {
+  DCHECK(!set.empty());
+  const Range& range = set.front();
+  const StringSet rest(set.begin() + 1, set.end());
+  const StateMap::const_iterator where = state_map->find(rest);
+  const uint8_t target_state = where == state_map->end()
+                                   ? MakeState(rest, states, state_map)
+                                   : where->second;
+  DCHECK_LT(0, range.from());
+  DCHECK_LT(range.to(), 0xFF);
+  const StateRange new_state_initializer[] = {
+      {0, 1},
+      {range.from(), target_state},
+      {static_cast<uint8_t>(range.to() + 1), 1}};
+  states->push_back(
+      State(new_state_initializer,
+            new_state_initializer + arraysize(new_state_initializer)));
+  const uint8_t new_state_number =
+      base::checked_cast<uint8_t>(states->size() - 1);
+  CHECK(state_map->insert(std::make_pair(set, new_state_number)).second);
+  return new_state_number;
+}
+
+std::vector<State> GenerateStates(const PairVector& pairs) {
+  // States 0 and 1 are the initial/valid state and invalid state, respectively.
+  std::vector<State> states(2, GenerateInvalidState());
+  StateMap state_map;
+  state_map.insert(std::make_pair(StringSet(), 0));
+  for (PairVector::const_iterator it = pairs.begin(); it != pairs.end(); ++it) {
+    DCHECK(it->character.empty());
+    DCHECK(!it->set.empty());
+    const Range& range = it->set.front();
+    const StringSet rest(it->set.begin() + 1, it->set.end());
+    const StateMap::const_iterator where = state_map.find(rest);
+    const uint8_t target_state = where == state_map.end()
+                                     ? MakeState(rest, &states, &state_map)
+                                     : where->second;
+    if (states[0].back().from == range.from()) {
+      DCHECK_EQ(1, states[0].back().target_state);
+      states[0].back().target_state = target_state;
+      DCHECK_LT(range.to(), 0xFF);
+      const StateRange new_range = {static_cast<uint8_t>(range.to() + 1), 1};
+      states[0].push_back(new_range);
+    } else {
+      DCHECK_LT(range.to(), 0xFF);
+      const StateRange new_range_initializer[] = {
+          {range.from(), target_state},
+          {static_cast<uint8_t>(range.to() + 1), 1}};
+      states[0]
+          .insert(states[0].end(),
+                  new_range_initializer,
+                  new_range_initializer + arraysize(new_range_initializer));
+    }
+  }
+  return states;
+}
+
+// Output the generated states as a C++ table. Two tricks are used to compact
+// the table: each state in the table starts with a shift value which indicates
+// how many bits we can discard from the right-hand-side of the byte before
+// doing the table lookup. Secondly, only the state-transitions for bytes
+// with the top-bit set are included in the table; bytes without the top-bit set
+// are just ASCII and are handled directly by the code.
+void PrintStates(const std::vector<State>& states, FILE* stream) {
+  // First calculate the start-offset of each state. This allows the state
+  // machine to jump directly to the correct offset, avoiding an extra
+  // indirection. State 0 starts at offset 0.
+  std::vector<uint8_t> state_offset(1, 0);
+  std::vector<uint8_t> shifts;
+  uint8_t pos = 0;
+
+  for (std::vector<State>::const_iterator state_it = states.begin();
+       state_it != states.end();
+       ++state_it) {
+    // We want to set |shift| to the (0-based) index of the least-significant
+    // set bit in any of the ranges for this state, since this tells us how many
+    // bits we can discard and still determine what range a byte lies in. Sadly
+    // it appears that ffs() is not portable, so we do it clumsily.
+    uint8_t shift = 7;
+    for (State::const_iterator range_it = state_it->begin();
+         range_it != state_it->end();
+         ++range_it) {
+      while (shift > 0 && range_it->from % (1 << shift) != 0) {
+        --shift;
+      }
+    }
+    shifts.push_back(shift);
+    pos += 1 + (1 << (7 - shift));
+    state_offset.push_back(pos);
+  }
+
+  DCHECK_EQ(129, state_offset[1]);
+
+  fputs(kProlog, stream);
+  TablePrinter table_printer(stream);
+
+  for (uint8_t state_index = 0; state_index < states.size(); ++state_index) {
+    const uint8_t shift = shifts[state_index];
+    uint8_t next_range = 0;
+    uint8_t target_state = 1;
+    fprintf(stream,
+            "    // State %d, offset 0x%02x\n",
+            static_cast<int>(state_index),
+            static_cast<int>(state_offset[state_index]));
+    table_printer.PrintValue(shift);
+    for (int i = 0; i < 0x100; i += (1 << shift)) {
+      if (next_range < states[state_index].size() &&
+          states[state_index][next_range].from == i) {
+        target_state = states[state_index][next_range].target_state;
+        ++next_range;
+      }
+      if (i >= 0x80) {
+        table_printer.PrintValue(state_offset[target_state]);
+      }
+    }
+    table_printer.NewLine();
+  }
+
+  fputs(kEpilog, stream);
+}
+
+}  // namespace
+
+int main(int argc, char* argv[]) {
+  base::CommandLine::Init(argc, argv);
+  logging::LoggingSettings settings;
+  settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
+  logging::InitLogging(settings);
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch("help")) {
+    fwrite(kHelpText, 1, arraysize(kHelpText), stdout);
+    exit(EXIT_SUCCESS);
+  }
+  base::FilePath filename =
+      base::CommandLine::ForCurrentProcess()->GetSwitchValuePath("output");
+
+  FILE* output = stdout;
+  if (!filename.empty()) {
+    output = base::OpenFile(filename, "wb");
+    if (!output)
+      PLOG(FATAL) << "Couldn't open '" << filename.AsUTF8Unsafe()
+                  << "' for writing";
+  }
+
+  // Step 1: Enumerate the characters
+  PairVector pairs = InitializeCharacters();
+  // Step 2: Convert to sets.
+  MoveAllCharsToSets(&pairs);
+  if (VLOG_IS_ON(1)) {
+    LogStringSets(pairs);
+  }
+  // Step 3: Generate states.
+  std::vector<State> states = GenerateStates(pairs);
+  // Step 4/5: Print output
+  PrintStates(states, output);
+
+  if (!filename.empty()) {
+    if (!base::CloseFile(output))
+      PLOG(FATAL) << "Couldn't finish writing '" << filename.AsUTF8Unsafe()
+                  << "'";
+  }
+
+  return EXIT_SUCCESS;
+}
diff --git a/base/i18n/case_conversion.cc b/base/i18n/case_conversion.cc
new file mode 100644
index 0000000..a4a104c
--- /dev/null
+++ b/base/i18n/case_conversion.cc
@@ -0,0 +1,90 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/i18n/case_conversion.h"
+
+#include <stdint.h>
+
+#include "base/numerics/safe_conversions.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_util.h"
+#include "third_party/icu/source/common/unicode/uchar.h"
+#include "third_party/icu/source/common/unicode/unistr.h"
+#include "third_party/icu/source/common/unicode/ustring.h"
+
+namespace base {
+namespace i18n {
+
+namespace {
+
+// Provides a uniform interface for upper/lower/folding which take take
+// slightly varying parameters.
+typedef int32_t (*CaseMapperFunction)(UChar* dest, int32_t dest_capacity,
+                                      const UChar* src, int32_t src_length,
+                                      UErrorCode* error);
+
+int32_t ToUpperMapper(UChar* dest, int32_t dest_capacity,
+                      const UChar* src, int32_t src_length,
+                      UErrorCode* error) {
+  // Use default locale.
+  return u_strToUpper(dest, dest_capacity, src, src_length, nullptr, error);
+}
+
+int32_t ToLowerMapper(UChar* dest, int32_t dest_capacity,
+                      const UChar* src, int32_t src_length,
+                      UErrorCode* error) {
+  // Use default locale.
+  return u_strToLower(dest, dest_capacity, src, src_length, nullptr, error);
+}
+
+int32_t FoldCaseMapper(UChar* dest, int32_t dest_capacity,
+                       const UChar* src, int32_t src_length,
+                       UErrorCode* error) {
+  return u_strFoldCase(dest, dest_capacity, src, src_length,
+                       U_FOLD_CASE_DEFAULT, error);
+}
+
+// Provides similar functionality as UnicodeString::caseMap but on string16.
+string16 CaseMap(StringPiece16 string, CaseMapperFunction case_mapper) {
+  string16 dest;
+  if (string.empty())
+    return dest;
+
+  // Provide an initial guess that the string length won't change. The typical
+  // strings we use will very rarely change length in this process, so don't
+  // optimize for that case.
+  dest.resize(string.size());
+
+  UErrorCode error;
+  do {
+    error = U_ZERO_ERROR;
+
+    // ICU won't terminate the string if there's not enough room for the null
+    // terminator, but will otherwise. So we don't need to save room for that.
+    // Don't use WriteInto, which assumes null terminators.
+    int32_t new_length = case_mapper(
+        &dest[0], saturated_cast<int32_t>(dest.size()),
+        string.data(), saturated_cast<int32_t>(string.size()),
+        &error);
+    dest.resize(new_length);
+  } while (error == U_BUFFER_OVERFLOW_ERROR);
+  return dest;
+}
+
+}  // namespace
+
+string16 ToLower(StringPiece16 string) {
+  return CaseMap(string, &ToLowerMapper);
+}
+
+string16 ToUpper(StringPiece16 string) {
+  return CaseMap(string, &ToUpperMapper);
+}
+
+string16 FoldCase(StringPiece16 string) {
+  return CaseMap(string, &FoldCaseMapper);
+}
+
+}  // namespace i18n
+}  // namespace base
diff --git a/base/i18n/case_conversion.h b/base/i18n/case_conversion.h
new file mode 100644
index 0000000..0631a80
--- /dev/null
+++ b/base/i18n/case_conversion.h
@@ -0,0 +1,48 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_I18N_CASE_CONVERSION_H_
+#define BASE_I18N_CASE_CONVERSION_H_
+
+#include "base/i18n/base_i18n_export.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_piece.h"
+
+namespace base {
+namespace i18n {
+
+// UNICODE CASE-HANDLING ADVICE
+//
+// In English it's always safe to convert to upper-case or lower-case text
+// and get a good answer. But some languages have rules specific to those
+// locales. One example is the Turkish I:
+//   http://www.i18nguy.com/unicode/turkish-i18n.html
+//
+// ToLower/ToUpper use the current ICU locale which will take into account
+// the user language preference. Use this when dealing with user typing.
+//
+// FoldCase canonicalizes to a standardized form independent of the current
+// locale. Use this when comparing general Unicode strings that don't
+// necessarily belong in the user's current locale (like commands, protocol
+// names, other strings from the web) for case-insensitive equality.
+//
+// Note that case conversions will change the length of the string in some
+// not-uncommon cases. Never assume that the output is the same length as
+// the input.
+
+// Returns the lower case equivalent of string. Uses ICU's current locale.
+BASE_I18N_EXPORT string16 ToLower(StringPiece16 string);
+
+// Returns the upper case equivalent of string. Uses ICU's current locale.
+BASE_I18N_EXPORT string16 ToUpper(StringPiece16 string);
+
+// Convert the given string to a canonical case, independent of the current
+// locale. For ASCII the canonical form is lower case.
+// See http://unicode.org/faq/casemap_charprop.html#2
+BASE_I18N_EXPORT string16 FoldCase(StringPiece16 string);
+
+}  // namespace i18n
+}  // namespace base
+
+#endif  // BASE_I18N_CASE_CONVERSION_H_
diff --git a/base/i18n/case_conversion_unittest.cc b/base/i18n/case_conversion_unittest.cc
new file mode 100644
index 0000000..ee795bc
--- /dev/null
+++ b/base/i18n/case_conversion_unittest.cc
@@ -0,0 +1,119 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/i18n/case_conversion.h"
+#include "base/i18n/rtl.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/icu_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/icu/source/i18n/unicode/usearch.h"
+
+namespace base {
+namespace i18n {
+
+namespace {
+
+const wchar_t kNonASCIIMixed[] =
+    L"\xC4\xD6\xE4\xF6\x20\xCF\xEF\x20\xF7\x25"
+    L"\xA4\x23\x2A\x5E\x60\x40\xA3\x24\x2030\x201A\x7E\x20\x1F07\x1F0F"
+    L"\x20\x1E00\x1E01";
+const wchar_t kNonASCIILower[] =
+    L"\xE4\xF6\xE4\xF6\x20\xEF\xEF"
+    L"\x20\xF7\x25\xA4\x23\x2A\x5E\x60\x40\xA3\x24\x2030\x201A\x7E\x20\x1F07"
+    L"\x1F07\x20\x1E01\x1E01";
+const wchar_t kNonASCIIUpper[] =
+    L"\xC4\xD6\xC4\xD6\x20\xCF\xCF"
+    L"\x20\xF7\x25\xA4\x23\x2A\x5E\x60\x40\xA3\x24\x2030\x201A\x7E\x20\x1F0F"
+    L"\x1F0F\x20\x1E00\x1E00";
+
+}  // namespace
+
+// Test upper and lower case string conversion.
+TEST(CaseConversionTest, UpperLower) {
+  const string16 mixed(ASCIIToUTF16("Text with UPPer & lowER casE."));
+  const string16 expected_lower(ASCIIToUTF16("text with upper & lower case."));
+  const string16 expected_upper(ASCIIToUTF16("TEXT WITH UPPER & LOWER CASE."));
+
+  string16 result = ToLower(mixed);
+  EXPECT_EQ(expected_lower, result);
+
+  result = ToUpper(mixed);
+  EXPECT_EQ(expected_upper, result);
+}
+
+TEST(CaseConversionTest, NonASCII) {
+  const string16 mixed(WideToUTF16(kNonASCIIMixed));
+  const string16 expected_lower(WideToUTF16(kNonASCIILower));
+  const string16 expected_upper(WideToUTF16(kNonASCIIUpper));
+
+  string16 result = ToLower(mixed);
+  EXPECT_EQ(expected_lower, result);
+
+  result = ToUpper(mixed);
+  EXPECT_EQ(expected_upper, result);
+}
+
+TEST(CaseConversionTest, TurkishLocaleConversion) {
+  const string16 mixed(WideToUTF16(L"\x49\x131"));
+  const string16 expected_lower(WideToUTF16(L"\x69\x131"));
+  const string16 expected_upper(WideToUTF16(L"\x49\x49"));
+
+  test::ScopedRestoreICUDefaultLocale restore_locale;
+  i18n::SetICUDefaultLocale("en_US");
+
+  string16 result = ToLower(mixed);
+  EXPECT_EQ(expected_lower, result);
+
+  result = ToUpper(mixed);
+  EXPECT_EQ(expected_upper, result);
+
+  i18n::SetICUDefaultLocale("tr");
+
+  const string16 expected_lower_turkish(WideToUTF16(L"\x131\x131"));
+  const string16 expected_upper_turkish(WideToUTF16(L"\x49\x49"));
+
+  result = ToLower(mixed);
+  EXPECT_EQ(expected_lower_turkish, result);
+
+  result = ToUpper(mixed);
+  EXPECT_EQ(expected_upper_turkish, result);
+}
+
+TEST(CaseConversionTest, FoldCase) {
+  // Simple ASCII, should lower-case.
+  EXPECT_EQ(ASCIIToUTF16("hello, world"),
+            FoldCase(ASCIIToUTF16("Hello, World")));
+
+  // Non-ASCII cases from above. They should all fold to the same result.
+  EXPECT_EQ(FoldCase(WideToUTF16(kNonASCIIMixed)),
+            FoldCase(WideToUTF16(kNonASCIILower)));
+  EXPECT_EQ(FoldCase(WideToUTF16(kNonASCIIMixed)),
+            FoldCase(WideToUTF16(kNonASCIIUpper)));
+
+  // Turkish cases from above. This is the lower-case expected result from the
+  // US locale. It should be the same even when the current locale is Turkish.
+  const string16 turkish(WideToUTF16(L"\x49\x131"));
+  const string16 turkish_expected(WideToUTF16(L"\x69\x131"));
+
+  test::ScopedRestoreICUDefaultLocale restore_locale;
+  i18n::SetICUDefaultLocale("en_US");
+  EXPECT_EQ(turkish_expected, FoldCase(turkish));
+
+  i18n::SetICUDefaultLocale("tr");
+  EXPECT_EQ(turkish_expected, FoldCase(turkish));
+
+  // Test a case that gets bigger when processed.
+  // U+130 = LATIN CAPITAL LETTER I WITH DOT ABOVE gets folded to a lower case
+  // "i" followed by U+307 COMBINING DOT ABOVE.
+  EXPECT_EQ(WideToUTF16(L"i\u0307j"), FoldCase(WideToUTF16(L"\u0130j")));
+
+  // U+00DF (SHARP S) and U+1E9E (CAPIRAL SHARP S) are both folded to "ss".
+  EXPECT_EQ(ASCIIToUTF16("ssss"), FoldCase(WideToUTF16(L"\u00DF\u1E9E")));
+}
+
+}  // namespace i18n
+}  // namespace base
+
+
+
diff --git a/base/i18n/char_iterator.cc b/base/i18n/char_iterator.cc
new file mode 100644
index 0000000..d80b8b6
--- /dev/null
+++ b/base/i18n/char_iterator.cc
@@ -0,0 +1,80 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/i18n/char_iterator.h"
+
+#include "third_party/icu/source/common/unicode/utf8.h"
+#include "third_party/icu/source/common/unicode/utf16.h"
+
+namespace base {
+namespace i18n {
+
+UTF8CharIterator::UTF8CharIterator(const std::string* str)
+    : str_(reinterpret_cast<const uint8_t*>(str->data())),
+      len_(str->size()),
+      array_pos_(0),
+      next_pos_(0),
+      char_pos_(0),
+      char_(0) {
+  if (len_)
+    U8_NEXT(str_, next_pos_, len_, char_);
+}
+
+UTF8CharIterator::~UTF8CharIterator() = default;
+
+bool UTF8CharIterator::Advance() {
+  if (array_pos_ >= len_)
+    return false;
+
+  array_pos_ = next_pos_;
+  char_pos_++;
+  if (next_pos_ < len_)
+    U8_NEXT(str_, next_pos_, len_, char_);
+
+  return true;
+}
+
+UTF16CharIterator::UTF16CharIterator(const string16* str)
+    : str_(reinterpret_cast<const char16*>(str->data())),
+      len_(str->size()),
+      array_pos_(0),
+      next_pos_(0),
+      char_pos_(0),
+      char_(0) {
+  if (len_)
+    ReadChar();
+}
+
+UTF16CharIterator::UTF16CharIterator(const char16* str, size_t str_len)
+    : str_(str),
+      len_(str_len),
+      array_pos_(0),
+      next_pos_(0),
+      char_pos_(0),
+      char_(0) {
+  if (len_)
+    ReadChar();
+}
+
+UTF16CharIterator::~UTF16CharIterator() = default;
+
+bool UTF16CharIterator::Advance() {
+  if (array_pos_ >= len_)
+    return false;
+
+  array_pos_ = next_pos_;
+  char_pos_++;
+  if (next_pos_ < len_)
+    ReadChar();
+
+  return true;
+}
+
+void UTF16CharIterator::ReadChar() {
+  // This is actually a huge macro, so is worth having in a separate function.
+  U16_NEXT(str_, next_pos_, len_, char_);
+}
+
+}  // namespace i18n
+}  // namespace base
diff --git a/base/i18n/char_iterator.h b/base/i18n/char_iterator.h
new file mode 100644
index 0000000..24024d4
--- /dev/null
+++ b/base/i18n/char_iterator.h
@@ -0,0 +1,134 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_I18N_CHAR_ITERATOR_H_
+#define BASE_I18N_CHAR_ITERATOR_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <string>
+
+#include "base/i18n/base_i18n_export.h"
+#include "base/macros.h"
+#include "base/strings/string16.h"
+#include "build/build_config.h"
+
+// The CharIterator classes iterate through the characters in UTF8 and
+// UTF16 strings.  Example usage:
+//
+//   UTF8CharIterator iter(&str);
+//   while (!iter.end()) {
+//     VLOG(1) << iter.get();
+//     iter.Advance();
+//   }
+
+#if defined(OS_WIN)
+typedef unsigned char uint8_t;
+#endif
+
+namespace base {
+namespace i18n {
+
+class BASE_I18N_EXPORT UTF8CharIterator {
+ public:
+  // Requires |str| to live as long as the UTF8CharIterator does.
+  explicit UTF8CharIterator(const std::string* str);
+  ~UTF8CharIterator();
+
+  // Return the starting array index of the current character within the
+  // string.
+  int32_t array_pos() const { return array_pos_; }
+
+  // Return the logical index of the current character, independent of the
+  // number of bytes each character takes.
+  int32_t char_pos() const { return char_pos_; }
+
+  // Return the current char.
+  int32_t get() const { return char_; }
+
+  // Returns true if we're at the end of the string.
+  bool end() const { return array_pos_ == len_; }
+
+  // Advance to the next actual character.  Returns false if we're at the
+  // end of the string.
+  bool Advance();
+
+ private:
+  // The string we're iterating over.
+  const uint8_t* str_;
+
+  // The length of the encoded string.
+  int32_t len_;
+
+  // Array index.
+  int32_t array_pos_;
+
+  // The next array index.
+  int32_t next_pos_;
+
+  // Character index.
+  int32_t char_pos_;
+
+  // The current character.
+  int32_t char_;
+
+  DISALLOW_COPY_AND_ASSIGN(UTF8CharIterator);
+};
+
+class BASE_I18N_EXPORT UTF16CharIterator {
+ public:
+  // Requires |str| to live as long as the UTF16CharIterator does.
+  explicit UTF16CharIterator(const string16* str);
+  UTF16CharIterator(const char16* str, size_t str_len);
+  ~UTF16CharIterator();
+
+  // Return the starting array index of the current character within the
+  // string.
+  int32_t array_pos() const { return array_pos_; }
+
+  // Return the logical index of the current character, independent of the
+  // number of codewords each character takes.
+  int32_t char_pos() const { return char_pos_; }
+
+  // Return the current char.
+  int32_t get() const { return char_; }
+
+  // Returns true if we're at the end of the string.
+  bool end() const { return array_pos_ == len_; }
+
+  // Advance to the next actual character.  Returns false if we're at the
+  // end of the string.
+  bool Advance();
+
+ private:
+  // Fills in the current character we found and advances to the next
+  // character, updating all flags as necessary.
+  void ReadChar();
+
+  // The string we're iterating over.
+  const char16* str_;
+
+  // The length of the encoded string.
+  int32_t len_;
+
+  // Array index.
+  int32_t array_pos_;
+
+  // The next array index.
+  int32_t next_pos_;
+
+  // Character index.
+  int32_t char_pos_;
+
+  // The current character.
+  int32_t char_;
+
+  DISALLOW_COPY_AND_ASSIGN(UTF16CharIterator);
+};
+
+}  // namespace i18n
+}  // namespace base
+
+#endif  // BASE_I18N_CHAR_ITERATOR_H_
diff --git a/base/i18n/char_iterator_unittest.cc b/base/i18n/char_iterator_unittest.cc
new file mode 100644
index 0000000..0cf8e6c
--- /dev/null
+++ b/base/i18n/char_iterator_unittest.cc
@@ -0,0 +1,101 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/i18n/char_iterator.h"
+
+#include "base/strings/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace i18n {
+
+TEST(CharIteratorsTest, TestUTF8) {
+  std::string empty;
+  UTF8CharIterator empty_iter(&empty);
+  ASSERT_TRUE(empty_iter.end());
+  ASSERT_EQ(0, empty_iter.array_pos());
+  ASSERT_EQ(0, empty_iter.char_pos());
+  ASSERT_FALSE(empty_iter.Advance());
+
+  std::string str("s\303\273r");  // [u with circumflex]
+  UTF8CharIterator iter(&str);
+  ASSERT_FALSE(iter.end());
+  ASSERT_EQ(0, iter.array_pos());
+  ASSERT_EQ(0, iter.char_pos());
+  ASSERT_EQ('s', iter.get());
+  ASSERT_TRUE(iter.Advance());
+
+  ASSERT_FALSE(iter.end());
+  ASSERT_EQ(1, iter.array_pos());
+  ASSERT_EQ(1, iter.char_pos());
+  ASSERT_EQ(251, iter.get());
+  ASSERT_TRUE(iter.Advance());
+
+  ASSERT_FALSE(iter.end());
+  ASSERT_EQ(3, iter.array_pos());
+  ASSERT_EQ(2, iter.char_pos());
+  ASSERT_EQ('r', iter.get());
+  ASSERT_TRUE(iter.Advance());
+
+  ASSERT_TRUE(iter.end());
+  ASSERT_EQ(4, iter.array_pos());
+  ASSERT_EQ(3, iter.char_pos());
+
+  // Don't care what it returns, but this shouldn't crash
+  iter.get();
+
+  ASSERT_FALSE(iter.Advance());
+}
+
+TEST(CharIteratorsTest, TestUTF16) {
+  string16 empty = UTF8ToUTF16("");
+  UTF16CharIterator empty_iter(&empty);
+  ASSERT_TRUE(empty_iter.end());
+  ASSERT_EQ(0, empty_iter.array_pos());
+  ASSERT_EQ(0, empty_iter.char_pos());
+  ASSERT_FALSE(empty_iter.Advance());
+
+  // This test string contains 4 characters:
+  //   x
+  //   u with circumflex - 2 bytes in UTF8, 1 codeword in UTF16
+  //   math double-struck A - 4 bytes in UTF8, 2 codewords in UTF16
+  //   z
+  string16 str = UTF8ToUTF16("x\303\273\360\235\224\270z");
+  UTF16CharIterator iter(&str);
+  ASSERT_FALSE(iter.end());
+  ASSERT_EQ(0, iter.array_pos());
+  ASSERT_EQ(0, iter.char_pos());
+  ASSERT_EQ('x', iter.get());
+  ASSERT_TRUE(iter.Advance());
+
+  ASSERT_FALSE(iter.end());
+  ASSERT_EQ(1, iter.array_pos());
+  ASSERT_EQ(1, iter.char_pos());
+  ASSERT_EQ(251, iter.get());
+  ASSERT_TRUE(iter.Advance());
+
+  ASSERT_FALSE(iter.end());
+  ASSERT_EQ(2, iter.array_pos());
+  ASSERT_EQ(2, iter.char_pos());
+  ASSERT_EQ(120120, iter.get());
+  ASSERT_TRUE(iter.Advance());
+
+  ASSERT_FALSE(iter.end());
+  ASSERT_EQ(4, iter.array_pos());
+  ASSERT_EQ(3, iter.char_pos());
+  ASSERT_EQ('z', iter.get());
+  ASSERT_TRUE(iter.Advance());
+
+  ASSERT_TRUE(iter.end());
+  ASSERT_EQ(5, iter.array_pos());
+  ASSERT_EQ(4, iter.char_pos());
+
+  // Don't care what it returns, but this shouldn't crash
+  iter.get();
+
+  ASSERT_FALSE(iter.Advance());
+}
+
+}  // namespace i18n
+}  // namespace base
diff --git a/base/i18n/character_encoding.cc b/base/i18n/character_encoding.cc
new file mode 100644
index 0000000..a1068c3
--- /dev/null
+++ b/base/i18n/character_encoding.cc
@@ -0,0 +1,42 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/i18n/character_encoding.h"
+
+#include "base/macros.h"
+#include "third_party/icu/source/common/unicode/ucnv.h"
+
+namespace base {
+namespace {
+
+// An array of all supported canonical encoding names.
+const char* const kCanonicalEncodingNames[] = {
+    "Big5",         "EUC-JP",       "EUC-KR",       "gb18030",
+    "GBK",          "IBM866",       "ISO-2022-JP",  "ISO-8859-10",
+    "ISO-8859-13",  "ISO-8859-14",  "ISO-8859-15",  "ISO-8859-16",
+    "ISO-8859-2",   "ISO-8859-3",   "ISO-8859-4",   "ISO-8859-5",
+    "ISO-8859-6",   "ISO-8859-7",   "ISO-8859-8",   "ISO-8859-8-I",
+    "KOI8-R",       "KOI8-U",       "macintosh",    "Shift_JIS",
+    "UTF-16LE",     "UTF-8",        "windows-1250", "windows-1251",
+    "windows-1252", "windows-1253", "windows-1254", "windows-1255",
+    "windows-1256", "windows-1257", "windows-1258", "windows-874"};
+
+}  // namespace
+
+std::string GetCanonicalEncodingNameByAliasName(const std::string& alias_name) {
+  for (auto* encoding_name : kCanonicalEncodingNames) {
+    if (alias_name == encoding_name)
+      return alias_name;
+  }
+  static const char* kStandards[3] = {"HTML", "MIME", "IANA"};
+  for (auto* standard : kStandards) {
+    UErrorCode error_code = U_ZERO_ERROR;
+    const char* canonical_name =
+        ucnv_getStandardName(alias_name.c_str(), standard, &error_code);
+    if (U_SUCCESS(error_code) && canonical_name)
+      return canonical_name;
+  }
+  return std::string();
+}
+}  // namespace base
diff --git a/base/i18n/character_encoding.h b/base/i18n/character_encoding.h
new file mode 100644
index 0000000..974cb5a
--- /dev/null
+++ b/base/i18n/character_encoding.h
@@ -0,0 +1,20 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_I18N_CHARACTER_ENCODING_H_
+#define BASE_I18N_CHARACTER_ENCODING_H_
+
+#include <string>
+
+#include "base/i18n/base_i18n_export.h"
+
+namespace base {
+
+// Return canonical encoding name according to the encoding alias name.
+BASE_I18N_EXPORT std::string GetCanonicalEncodingNameByAliasName(
+    const std::string& alias_name);
+
+}  // namespace base
+
+#endif  // BASE_I18N_CHARACTER_ENCODING_H_
diff --git a/base/i18n/character_encoding_unittest.cc b/base/i18n/character_encoding_unittest.cc
new file mode 100644
index 0000000..3c11ba3
--- /dev/null
+++ b/base/i18n/character_encoding_unittest.cc
@@ -0,0 +1,23 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/i18n/character_encoding.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+TEST(CharacterEncodingTest, GetCanonicalEncodingNameByAliasName) {
+  EXPECT_EQ("Big5", GetCanonicalEncodingNameByAliasName("Big5"));
+  EXPECT_EQ("windows-874", GetCanonicalEncodingNameByAliasName("windows-874"));
+  EXPECT_EQ("ISO-8859-8", GetCanonicalEncodingNameByAliasName("ISO-8859-8"));
+
+  // Non-canonical alias names should be converted to a canonical one.
+  EXPECT_EQ("UTF-8", GetCanonicalEncodingNameByAliasName("utf8"));
+  EXPECT_EQ("gb18030", GetCanonicalEncodingNameByAliasName("GB18030"));
+  EXPECT_EQ("windows-874", GetCanonicalEncodingNameByAliasName("tis-620"));
+  EXPECT_EQ("EUC-KR", GetCanonicalEncodingNameByAliasName("ks_c_5601-1987"));
+}
+
+}  // namespace base
diff --git a/base/i18n/encoding_detection.cc b/base/i18n/encoding_detection.cc
new file mode 100644
index 0000000..fef34e4
--- /dev/null
+++ b/base/i18n/encoding_detection.cc
@@ -0,0 +1,40 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/i18n/encoding_detection.h"
+
+#include "build/build_config.h"
+#include "third_party/ced/src/compact_enc_det/compact_enc_det.h"
+
+// third_party/ced/src/util/encodings/encodings.h, which is included
+// by the include above, undefs UNICODE because that is a macro used
+// internally in ced. If we later in the same translation unit do
+// anything related to Windows or Windows headers those will then use
+// the ASCII versions which we do not want. To avoid that happening in
+// jumbo builds, we redefine UNICODE again here.
+#if defined(OS_WIN)
+#define UNICODE 1
+#endif  // OS_WIN
+
+namespace base {
+
+bool DetectEncoding(const std::string& text, std::string* encoding) {
+  int consumed_bytes;
+  bool is_reliable;
+  Encoding enc = CompactEncDet::DetectEncoding(
+      text.c_str(), text.length(), nullptr, nullptr, nullptr,
+      UNKNOWN_ENCODING,
+      UNKNOWN_LANGUAGE,
+      CompactEncDet::QUERY_CORPUS,  // plain text
+      false,  // Include 7-bit encodings
+      &consumed_bytes,
+      &is_reliable);
+
+  if (enc == UNKNOWN_ENCODING)
+    return false;
+
+  *encoding = MimeEncodingName(enc);
+  return true;
+}
+}  // namespace base
diff --git a/base/i18n/encoding_detection.h b/base/i18n/encoding_detection.h
new file mode 100644
index 0000000..c8e660c
--- /dev/null
+++ b/base/i18n/encoding_detection.h
@@ -0,0 +1,21 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_I18N_ENCODING_DETECTION_H_
+#define BASE_I18N_ENCODING_DETECTION_H_
+
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/i18n/base_i18n_export.h"
+
+namespace base {
+
+// Detect encoding of |text| and put the name of encoding in |encoding|.
+// Returns true on success.
+BASE_I18N_EXPORT bool DetectEncoding(const std::string& text,
+                                     std::string* encoding) WARN_UNUSED_RESULT;
+}  // namespace base
+
+#endif  // BASE_I18N_ENCODING_DETECTION_H_
diff --git a/base/i18n/file_util_icu.cc b/base/i18n/file_util_icu.cc
new file mode 100644
index 0000000..c91aea1
--- /dev/null
+++ b/base/i18n/file_util_icu.cc
@@ -0,0 +1,179 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// File utilities that use the ICU library go in this file.
+
+#include "base/i18n/file_util_icu.h"
+
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/files/file_path.h"
+#include "base/i18n/icu_string_conversions.h"
+#include "base/i18n/string_compare.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/singleton.h"
+#include "base/strings/string_util.h"
+#include "base/strings/sys_string_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
+#include "third_party/icu/source/common/unicode/uniset.h"
+#include "third_party/icu/source/i18n/unicode/coll.h"
+
+namespace base {
+namespace i18n {
+
+namespace {
+
+class IllegalCharacters {
+ public:
+  static IllegalCharacters* GetInstance() {
+    return Singleton<IllegalCharacters>::get();
+  }
+
+  bool DisallowedEverywhere(UChar32 ucs4) {
+    return !!illegal_anywhere_->contains(ucs4);
+  }
+
+  bool DisallowedLeadingOrTrailing(UChar32 ucs4) {
+    return !!illegal_at_ends_->contains(ucs4);
+  }
+
+  bool IsAllowedName(const string16& s) {
+    return s.empty() || (!!illegal_anywhere_->containsNone(
+                             icu::UnicodeString(s.c_str(), s.size())) &&
+                         !illegal_at_ends_->contains(*s.begin()) &&
+                         !illegal_at_ends_->contains(*s.rbegin()));
+  }
+
+ private:
+  friend class Singleton<IllegalCharacters>;
+  friend struct DefaultSingletonTraits<IllegalCharacters>;
+
+  IllegalCharacters();
+  ~IllegalCharacters() = default;
+
+  // set of characters considered invalid anywhere inside a filename.
+  std::unique_ptr<icu::UnicodeSet> illegal_anywhere_;
+
+  // set of characters considered invalid at either end of a filename.
+  std::unique_ptr<icu::UnicodeSet> illegal_at_ends_;
+
+  DISALLOW_COPY_AND_ASSIGN(IllegalCharacters);
+};
+
+IllegalCharacters::IllegalCharacters() {
+  UErrorCode everywhere_status = U_ZERO_ERROR;
+  UErrorCode ends_status = U_ZERO_ERROR;
+  // Control characters, formatting characters, non-characters, path separators,
+  // 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
+  // Note that code points in the "Other, Format" (Cf) category are ignored on
+  // HFS+ despite the ZERO_WIDTH_JOINER and ZERO_WIDTH_NON-JOINER being
+  // legitimate in Arabic and some S/SE Asian scripts. In addition tilde (~) is
+  // also excluded due to the possibility of interacting poorly with short
+  // filenames on VFAT. (Related to CVE-2014-9390)
+  illegal_anywhere_.reset(new icu::UnicodeSet(
+      UNICODE_STRING_SIMPLE("[[\"~*/:<>?\\\\|][:Cc:][:Cf:]]"),
+      everywhere_status));
+  illegal_at_ends_.reset(new icu::UnicodeSet(
+      UNICODE_STRING_SIMPLE("[[:WSpace:][.]]"), ends_status));
+  DCHECK(U_SUCCESS(everywhere_status));
+  DCHECK(U_SUCCESS(ends_status));
+
+  // Add non-characters. If this becomes a performance bottleneck by
+  // any chance, do not add these to |set| and change IsFilenameLegal()
+  // to check |ucs4 & 0xFFFEu == 0xFFFEu|, in addiition to calling
+  // IsAllowedName().
+  illegal_anywhere_->add(0xFDD0, 0xFDEF);
+  for (int i = 0; i <= 0x10; ++i) {
+    int plane_base = 0x10000 * i;
+    illegal_anywhere_->add(plane_base + 0xFFFE, plane_base + 0xFFFF);
+  }
+  illegal_anywhere_->freeze();
+  illegal_at_ends_->freeze();
+}
+
+}  // namespace
+
+bool IsFilenameLegal(const string16& file_name) {
+  return IllegalCharacters::GetInstance()->IsAllowedName(file_name);
+}
+
+void ReplaceIllegalCharactersInPath(FilePath::StringType* file_name,
+                                    char replace_char) {
+  IllegalCharacters* illegal = IllegalCharacters::GetInstance();
+
+  DCHECK(!(illegal->DisallowedEverywhere(replace_char)));
+  DCHECK(!(illegal->DisallowedLeadingOrTrailing(replace_char)));
+
+  int cursor = 0;  // The ICU macros expect an int.
+  while (cursor < static_cast<int>(file_name->size())) {
+    int char_begin = cursor;
+    uint32_t code_point;
+#if defined(OS_WIN)
+    // Windows uses UTF-16 encoding for filenames.
+    U16_NEXT(file_name->data(), cursor, static_cast<int>(file_name->length()),
+             code_point);
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+    // Mac and Chrome OS use UTF-8 encoding for filenames.
+    // Linux doesn't actually define file system encoding. Try to parse as
+    // UTF-8.
+    U8_NEXT(file_name->data(), cursor, static_cast<int>(file_name->length()),
+            code_point);
+#else
+#error Unsupported platform
+#endif
+
+    if (illegal->DisallowedEverywhere(code_point) ||
+        ((char_begin == 0 || cursor == static_cast<int>(file_name->length())) &&
+         illegal->DisallowedLeadingOrTrailing(code_point))) {
+      file_name->replace(char_begin, cursor - char_begin, 1, replace_char);
+      // We just made the potentially multi-byte/word char into one that only
+      // takes one byte/word, so need to adjust the cursor to point to the next
+      // character again.
+      cursor = char_begin + 1;
+    }
+  }
+}
+
+bool LocaleAwareCompareFilenames(const FilePath& a, const FilePath& b) {
+  UErrorCode error_code = U_ZERO_ERROR;
+  // Use the default collator. The default locale should have been properly
+  // set by the time this constructor is called.
+  std::unique_ptr<icu::Collator> collator(
+      icu::Collator::createInstance(error_code));
+  DCHECK(U_SUCCESS(error_code));
+  // Make it case-sensitive.
+  collator->setStrength(icu::Collator::TERTIARY);
+
+#if defined(OS_WIN)
+  return CompareString16WithCollator(*collator, WideToUTF16(a.value()),
+                                     WideToUTF16(b.value())) == UCOL_LESS;
+
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+  // On linux, the file system encoding is not defined. We assume
+  // SysNativeMBToWide takes care of it.
+  return CompareString16WithCollator(
+             *collator, WideToUTF16(SysNativeMBToWide(a.value())),
+             WideToUTF16(SysNativeMBToWide(b.value()))) == UCOL_LESS;
+#endif
+}
+
+void NormalizeFileNameEncoding(FilePath* file_name) {
+#if defined(OS_CHROMEOS)
+  std::string normalized_str;
+  if (ConvertToUtf8AndNormalize(file_name->BaseName().value(), kCodepageUTF8,
+                                &normalized_str) &&
+      !normalized_str.empty()) {
+    *file_name = file_name->DirName().Append(FilePath(normalized_str));
+  }
+#endif
+}
+
+}  // namespace i18n
+}  // namespace base
diff --git a/base/i18n/file_util_icu.h b/base/i18n/file_util_icu.h
new file mode 100644
index 0000000..f8bd9f4
--- /dev/null
+++ b/base/i18n/file_util_icu.h
@@ -0,0 +1,58 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_I18N_FILE_UTIL_ICU_H_
+#define BASE_I18N_FILE_UTIL_ICU_H_
+
+// File utilities that use the ICU library go in this file.
+
+#include "base/files/file_path.h"
+#include "base/i18n/base_i18n_export.h"
+#include "base/strings/string16.h"
+
+namespace base {
+namespace i18n {
+
+// Returns true if file_name does not have any illegal character. The input
+// param has the same restriction as that for ReplaceIllegalCharacters.
+BASE_I18N_EXPORT bool IsFilenameLegal(const string16& file_name);
+
+// 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 (since slashes are considered illegal). Any leading or
+// trailing whitespace or periods in |file_name| is also replaced with the
+// |replace_char|.
+//
+// Example:
+//   "bad:file*name?.txt" will be turned into "bad_file_name_.txt" when
+//   |replace_char| is '_'.
+//
+// Warning: Do not use this function as the sole means of sanitizing a filename.
+//   While the resulting filename itself would be legal, it doesn't necessarily
+//   mean that the file will behave safely. On Windows, certain reserved names
+//   refer to devices rather than files (E.g. LPT1), and some filenames could be
+//   interpreted as shell namespace extensions (E.g. Foo.{<GUID>}).
+//
+// On Windows, Chrome OS and Mac, the file system encoding is already known and
+// parsed as UTF-8 and UTF-16 accordingly.
+// On Linux, the file name will be parsed as UTF8.
+// TODO(asanka): Move full filename sanitization logic here.
+BASE_I18N_EXPORT void ReplaceIllegalCharactersInPath(
+    FilePath::StringType* file_name,
+    char replace_char);
+
+// Compares two filenames using the current locale information. This can be
+// used to sort directory listings. It behaves like "operator<" for use in
+// std::sort.
+BASE_I18N_EXPORT bool LocaleAwareCompareFilenames(const FilePath& a,
+                                                  const FilePath& b);
+
+// Calculates the canonical file-system representation of |file_name| base name.
+// Modifies |file_name| in place. No-op if not on ChromeOS.
+BASE_I18N_EXPORT void NormalizeFileNameEncoding(FilePath* file_name);
+
+}  // namespace i18n
+}  // namespace base
+
+#endif  // BASE_I18N_FILE_UTIL_ICU_H_
diff --git a/base/i18n/file_util_icu_unittest.cc b/base/i18n/file_util_icu_unittest.cc
new file mode 100644
index 0000000..062d29b
--- /dev/null
+++ b/base/i18n/file_util_icu_unittest.cc
@@ -0,0 +1,140 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/i18n/file_util_icu.h"
+
+#include <stddef.h>
+
+#include "base/files/file_util.h"
+#include "base/macros.h"
+#include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/platform_test.h"
+
+namespace base {
+namespace i18n {
+
+// file_util winds up using autoreleased objects on the Mac, so this needs
+// to be a PlatformTest
+class FileUtilICUTest : public PlatformTest {
+};
+
+#if defined(OS_POSIX) && !defined(OS_MACOSX)
+
+// On linux, file path is parsed and filtered as UTF-8.
+static const struct GoodBadPairLinux {
+  const char* bad_name;
+  const char* good_name;
+} kLinuxIllegalCharacterCases[] = {
+  {"bad*\\/file:name?.jpg", "bad---file-name-.jpg"},
+  {"**********::::.txt", "--------------.txt"},
+  {"\xe9\xf0zzzz.\xff", "\xe9\xf0zzzz.\xff"},
+  {" _ ", "-_-"},
+  {".", "-"},
+  {" .( ). ", "-.( ).-"},
+  {"     ", "-   -"},
+};
+
+TEST_F(FileUtilICUTest, ReplaceIllegalCharactersInPathLinuxTest) {
+  for (size_t i = 0; i < arraysize(kLinuxIllegalCharacterCases); ++i) {
+    std::string bad_name(kLinuxIllegalCharacterCases[i].bad_name);
+    ReplaceIllegalCharactersInPath(&bad_name, '-');
+    EXPECT_EQ(kLinuxIllegalCharacterCases[i].good_name, bad_name);
+  }
+}
+
+#endif
+
+// For Mac & Windows, which both do Unicode validation on filenames. These
+// characters are given as wide strings since its more convenient to specify
+// unicode characters. For Mac they should be converted to UTF-8.
+static const struct goodbad_pair {
+  const wchar_t* bad_name;
+  const wchar_t* good_name;
+} kIllegalCharacterCases[] = {
+    {L"bad*file:name?.jpg", L"bad-file-name-.jpg"},
+    {L"**********::::.txt", L"--------------.txt"},
+    // 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"bad*file\\?name.jpg", L"bad-file--name.jpg"},
+    {L"\t  bad*file\\name/.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-\u0644.mp3"},
+    {L"\U00010330\U00010331.mp3", L"\U00010330\U00010331.mp3"},
+    // Unassigned codepoints are ok.
+    {L"\u0378\U00040001.mp3", L"\u0378\U00040001.mp3"},
+    // Non-characters are not allowed.
+    {L"bad\uFFFFfile\U0010FFFEname.jpg", L"bad-file-name.jpg"},
+    {L"bad\uFDD0file\uFDEFname.jpg", L"bad-file-name.jpg"},
+    // CVE-2014-9390
+    {L"(\u200C.\u200D.\u200E.\u200F.\u202A.\u202B.\u202C.\u202D.\u202E.\u206A."
+     L"\u206B.\u206C.\u206D.\u206F.\uFEFF)",
+     L"(-.-.-.-.-.-.-.-.-.-.-.-.-.-.-)"},
+    {L"config~1", L"config-1"},
+    {L" _ ", L"-_-"},
+    {L" ", L"-"},
+    {L"\u2008.(\u2007).\u3000", L"-.(\u2007).-"},
+    {L"     ", L"-   -"},
+    {L".    ", L"-   -"}
+};
+
+#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_POSIX)
+
+TEST_F(FileUtilICUTest, ReplaceIllegalCharactersInPathTest) {
+  for (size_t i = 0; i < arraysize(kIllegalCharacterCases); ++i) {
+#if defined(OS_WIN)
+    std::wstring bad_name(kIllegalCharacterCases[i].bad_name);
+    ReplaceIllegalCharactersInPath(&bad_name, '-');
+    EXPECT_EQ(kIllegalCharacterCases[i].good_name, bad_name);
+#else
+    std::string bad_name(WideToUTF8(kIllegalCharacterCases[i].bad_name));
+    ReplaceIllegalCharactersInPath(&bad_name, '-');
+    EXPECT_EQ(WideToUTF8(kIllegalCharacterCases[i].good_name), bad_name);
+#endif
+  }
+}
+
+#endif
+
+TEST_F(FileUtilICUTest, IsFilenameLegalTest) {
+  EXPECT_TRUE(IsFilenameLegal(string16()));
+
+  for (const auto& test_case : kIllegalCharacterCases) {
+    string16 bad_name = WideToUTF16(test_case.bad_name);
+    string16 good_name = WideToUTF16(test_case.good_name);
+
+    EXPECT_TRUE(IsFilenameLegal(good_name)) << good_name;
+    if (good_name != bad_name)
+      EXPECT_FALSE(IsFilenameLegal(bad_name)) << bad_name;
+  }
+}
+
+#if defined(OS_CHROMEOS)
+static const struct normalize_name_encoding_test_cases {
+  const char* original_path;
+  const char* normalized_path;
+} kNormalizeFileNameEncodingTestCases[] = {
+  { "foo_na\xcc\x88me.foo", "foo_n\xc3\xa4me.foo"},
+  { "foo_dir_na\xcc\x88me/foo_na\xcc\x88me.foo",
+    "foo_dir_na\xcc\x88me/foo_n\xc3\xa4me.foo"},
+  { "", ""},
+  { "foo_dir_na\xcc\x88me/", "foo_dir_n\xc3\xa4me"}
+};
+
+TEST_F(FileUtilICUTest, NormalizeFileNameEncoding) {
+  for (size_t i = 0; i < arraysize(kNormalizeFileNameEncodingTestCases); i++) {
+    FilePath path(kNormalizeFileNameEncodingTestCases[i].original_path);
+    NormalizeFileNameEncoding(&path);
+    EXPECT_EQ(FilePath(kNormalizeFileNameEncodingTestCases[i].normalized_path),
+              path);
+  }
+}
+
+#endif
+
+}  // namespace i18n
+}  // namespace base
diff --git a/base/i18n/i18n_constants.cc b/base/i18n/i18n_constants.cc
new file mode 100644
index 0000000..7d2f5fc
--- /dev/null
+++ b/base/i18n/i18n_constants.cc
@@ -0,0 +1,13 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/i18n/i18n_constants.h"
+
+namespace base {
+
+const char kCodepageLatin1[] = "ISO-8859-1";
+const char kCodepageUTF8[] = "UTF-8";
+
+}  // namespace base
+
diff --git a/base/i18n/i18n_constants.h b/base/i18n/i18n_constants.h
new file mode 100644
index 0000000..c1bd87d
--- /dev/null
+++ b/base/i18n/i18n_constants.h
@@ -0,0 +1,21 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_I18N_I18N_CONSTANTS_H_
+#define BASE_I18N_I18N_CONSTANTS_H_
+
+#include "base/i18n/base_i18n_export.h"
+
+namespace base {
+
+// Names of codepages (charsets) understood by icu.
+BASE_I18N_EXPORT extern const char kCodepageLatin1[];  // a.k.a. ISO 8859-1
+BASE_I18N_EXPORT extern const char kCodepageUTF8[];
+
+// The other possible options are UTF-16BE and UTF-16LE, but they are unused in
+// Chromium as of this writing.
+
+}  // namespace base
+
+#endif  // BASE_I18N_I18N_CONSTANTS_H_
diff --git a/base/i18n/icu_string_conversions.cc b/base/i18n/icu_string_conversions.cc
new file mode 100644
index 0000000..6ec9980
--- /dev/null
+++ b/base/i18n/icu_string_conversions.cc
@@ -0,0 +1,223 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/i18n/icu_string_conversions.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "third_party/icu/source/common/unicode/normalizer2.h"
+#include "third_party/icu/source/common/unicode/ucnv.h"
+#include "third_party/icu/source/common/unicode/ucnv_cb.h"
+#include "third_party/icu/source/common/unicode/ucnv_err.h"
+#include "third_party/icu/source/common/unicode/ustring.h"
+
+namespace base {
+
+namespace {
+// ToUnicodeCallbackSubstitute() is based on UCNV_TO_U_CALLBACK_SUBSTITUTE
+// in source/common/ucnv_err.c.
+
+// Copyright (c) 1995-2006 International Business Machines Corporation
+// and others
+//
+// All rights reserved.
+//
+
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, and/or
+// sell copies of the Software, and to permit persons to whom the Software
+// is furnished to do so, provided that the above copyright notice(s) and
+// this permission notice appear in all copies of the Software and that
+// both the above copyright notice(s) and this permission notice appear in
+// supporting documentation.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+// OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
+// INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
+// OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+// OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE
+// OR PERFORMANCE OF THIS SOFTWARE.
+//
+// Except as contained in this notice, the name of a copyright holder
+// shall not be used in advertising or otherwise to promote the sale, use
+// or other dealings in this Software without prior written authorization
+// of the copyright holder.
+
+//  ___________________________________________________________________________
+//
+// All trademarks and registered trademarks mentioned herein are the property
+// of their respective owners.
+
+void ToUnicodeCallbackSubstitute(const void* context,
+                                 UConverterToUnicodeArgs *to_args,
+                                 const char* code_units,
+                                 int32_t length,
+                                 UConverterCallbackReason reason,
+                                 UErrorCode * err) {
+  static const UChar kReplacementChar = 0xFFFD;
+  if (reason <= UCNV_IRREGULAR) {
+    if (context == nullptr ||
+        (*(reinterpret_cast<const char*>(context)) == 'i' &&
+         reason == UCNV_UNASSIGNED)) {
+      *err = U_ZERO_ERROR;
+      ucnv_cbToUWriteUChars(to_args, &kReplacementChar, 1, 0, err);
+      }
+      // else the caller must have set the error code accordingly.
+  }
+  // else ignore the reset, close and clone calls.
+}
+
+bool ConvertFromUTF16(UConverter* converter, const UChar* uchar_src,
+                      int uchar_len, OnStringConversionError::Type on_error,
+                      std::string* encoded) {
+  int encoded_max_length = UCNV_GET_MAX_BYTES_FOR_STRING(uchar_len,
+      ucnv_getMaxCharSize(converter));
+  encoded->resize(encoded_max_length);
+
+  UErrorCode status = U_ZERO_ERROR;
+
+  // Setup our error handler.
+  switch (on_error) {
+    case OnStringConversionError::FAIL:
+      ucnv_setFromUCallBack(converter, UCNV_FROM_U_CALLBACK_STOP, nullptr,
+                            nullptr, nullptr, &status);
+      break;
+    case OnStringConversionError::SKIP:
+    case OnStringConversionError::SUBSTITUTE:
+      ucnv_setFromUCallBack(converter, UCNV_FROM_U_CALLBACK_SKIP, nullptr,
+                            nullptr, nullptr, &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;
+}
+
+// Set up our error handler for ToUTF-16 converters
+void SetUpErrorHandlerForToUChars(OnStringConversionError::Type on_error,
+                                  UConverter* converter, UErrorCode* status) {
+  switch (on_error) {
+    case OnStringConversionError::FAIL:
+      ucnv_setToUCallBack(converter, UCNV_TO_U_CALLBACK_STOP, nullptr, nullptr,
+                          nullptr, status);
+      break;
+    case OnStringConversionError::SKIP:
+      ucnv_setToUCallBack(converter, UCNV_TO_U_CALLBACK_SKIP, nullptr, nullptr,
+                          nullptr, status);
+      break;
+    case OnStringConversionError::SUBSTITUTE:
+      ucnv_setToUCallBack(converter, ToUnicodeCallbackSubstitute, nullptr,
+                          nullptr, nullptr, status);
+      break;
+    default:
+      NOTREACHED();
+  }
+}
+
+}  // namespace
+
+// Codepage <-> Wide/UTF-16  ---------------------------------------------------
+
+bool UTF16ToCodepage(const string16& utf16,
+                     const char* codepage_name,
+                     OnStringConversionError::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;
+
+  return ConvertFromUTF16(converter, utf16.c_str(),
+                          static_cast<int>(utf16.length()), on_error, encoded);
+}
+
+bool CodepageToUTF16(const std::string& encoded,
+                     const char* codepage_name,
+                     OnStringConversionError::Type on_error,
+                     string16* utf16) {
+  utf16->clear();
+
+  UErrorCode status = U_ZERO_ERROR;
+  UConverter* converter = ucnv_open(codepage_name, &status);
+  if (!U_SUCCESS(status))
+    return false;
+
+  // Even in the worst case, the maximum length in 2-byte units of UTF-16
+  // output would be at most the same as the number of bytes in input. There
+  // is no single-byte encoding in which a character is mapped to a
+  // non-BMP character requiring two 2-byte units.
+  //
+  // Moreover, non-BMP characters in legacy multibyte encodings
+  // (e.g. EUC-JP, GB18030) take at least 2 bytes. The only exceptions are
+  // BOCU and SCSU, but we don't care about them.
+  size_t uchar_max_length = encoded.length() + 1;
+
+  SetUpErrorHandlerForToUChars(on_error, converter, &status);
+  std::unique_ptr<char16[]> buffer(new char16[uchar_max_length]);
+  int actual_size = ucnv_toUChars(converter, buffer.get(),
+      static_cast<int>(uchar_max_length), encoded.data(),
+      static_cast<int>(encoded.length()), &status);
+  ucnv_close(converter);
+  if (!U_SUCCESS(status)) {
+    utf16->clear();  // Make sure the output is empty on error.
+    return false;
+  }
+
+  utf16->assign(buffer.get(), actual_size);
+  return true;
+}
+
+bool ConvertToUtf8AndNormalize(const std::string& text,
+                               const std::string& charset,
+                               std::string* result) {
+  result->clear();
+  string16 utf16;
+  if (!CodepageToUTF16(
+      text, charset.c_str(), OnStringConversionError::FAIL, &utf16))
+    return false;
+
+  UErrorCode status = U_ZERO_ERROR;
+  const icu::Normalizer2* normalizer = icu::Normalizer2::getNFCInstance(status);
+  DCHECK(U_SUCCESS(status));
+  if (U_FAILURE(status))
+    return false;
+  int32_t utf16_length = static_cast<int32_t>(utf16.length());
+  icu::UnicodeString normalized(utf16.data(), utf16_length);
+  int32_t normalized_prefix_length =
+      normalizer->spanQuickCheckYes(normalized, status);
+  if (normalized_prefix_length < utf16_length) {
+    icu::UnicodeString un_normalized(normalized, normalized_prefix_length);
+    normalized.truncate(normalized_prefix_length);
+    normalizer->normalizeSecondAndAppend(normalized, un_normalized, status);
+  }
+  if (U_FAILURE(status))
+    return false;
+  normalized.toUTF8String(*result);
+  return true;
+}
+
+}  // namespace base
diff --git a/base/i18n/icu_string_conversions.h b/base/i18n/icu_string_conversions.h
new file mode 100644
index 0000000..cbdcb99
--- /dev/null
+++ b/base/i18n/icu_string_conversions.h
@@ -0,0 +1,57 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_I18N_ICU_STRING_CONVERSIONS_H_
+#define BASE_I18N_ICU_STRING_CONVERSIONS_H_
+
+#include <string>
+
+#include "base/i18n/base_i18n_export.h"
+#include "base/i18n/i18n_constants.h"
+#include "base/strings/string16.h"
+
+namespace base {
+
+// Defines the error handling modes of UTF16ToCodepage and CodepageToUTF16.
+class OnStringConversionError {
+ 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,
+
+    // When converting to Unicode, the offending byte sequences are substituted
+    // by Unicode replacement character (U+FFFD). When converting from Unicode,
+    // this is the same as SKIP.
+    SUBSTITUTE,
+  };
+
+ private:
+  OnStringConversionError() = delete;
+};
+
+// Converts between UTF-16 strings and the encoding specified.  If the
+// encoding doesn't exist or the encoding fails (when on_error is FAIL),
+// returns false.
+BASE_I18N_EXPORT bool UTF16ToCodepage(const string16& utf16,
+                                      const char* codepage_name,
+                                      OnStringConversionError::Type on_error,
+                                      std::string* encoded);
+BASE_I18N_EXPORT bool CodepageToUTF16(const std::string& encoded,
+                                      const char* codepage_name,
+                                      OnStringConversionError::Type on_error,
+                                      string16* utf16);
+
+// Converts from any codepage to UTF-8 and ensures the resulting UTF-8 is
+// normalized.
+BASE_I18N_EXPORT bool ConvertToUtf8AndNormalize(const std::string& text,
+                                                const std::string& charset,
+                                                std::string* result);
+
+}  // namespace base
+
+#endif  // BASE_I18N_ICU_STRING_CONVERSIONS_H_
diff --git a/base/i18n/icu_string_conversions_unittest.cc b/base/i18n/icu_string_conversions_unittest.cc
new file mode 100644
index 0000000..d155986
--- /dev/null
+++ b/base/i18n/icu_string_conversions_unittest.cc
@@ -0,0 +1,235 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <math.h>
+#include <stdarg.h>
+#include <stddef.h>
+
+#include <limits>
+#include <sstream>
+
+#include "base/format_macros.h"
+#include "base/i18n/icu_string_conversions.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+namespace {
+
+// Given a null-terminated string of wchar_t with each wchar_t representing
+// a UTF-16 code unit, returns a string16 made up of wchar_t's in the input.
+// Each wchar_t should be <= 0xFFFF and a non-BMP character (> U+FFFF)
+// should be represented as a surrogate pair (two UTF-16 units)
+// *even* where wchar_t is 32-bit (Linux and Mac).
+//
+// This is to help write tests for functions with string16 params until
+// the C++ 0x UTF-16 literal is well-supported by compilers.
+string16 BuildString16(const wchar_t* s) {
+#if defined(WCHAR_T_IS_UTF16)
+  return string16(s);
+#elif defined(WCHAR_T_IS_UTF32)
+  string16 u16;
+  while (*s != 0) {
+    DCHECK_LE(static_cast<unsigned int>(*s), 0xFFFFu);
+    u16.push_back(*s++);
+  }
+  return u16;
+#endif
+}
+
+}  // namespace
+
+// kConverterCodepageCases is not comprehensive. There are a number of cases
+// to add if we really want to have a comprehensive coverage of various
+// codepages and their 'idiosyncrasies'. Currently, the only implementation
+// for CodepageTo* and *ToCodepage uses ICU, which has a very extensive
+// set of tests for the charset conversion. So, we can get away with a
+// relatively small number of cases listed below.
+//
+// Note about |u16_wide| in the following struct.
+// On Windows, the field is always identical to |wide|. On Mac and Linux,
+// it's identical as long as there's no character outside the
+// BMP (<= U+FFFF). When there is, it is different from |wide| and
+// is not a real wide string (UTF-32 string) in that each wchar_t in
+// the string is a UTF-16 code unit zero-extended to be 32-bit
+// even when the code unit belongs to a surrogate pair.
+// For instance, a Unicode string (U+0041 U+010000) is represented as
+// L"\x0041\xD800\xDC00" instead of L"\x0041\x10000".
+// To avoid the clutter, |u16_wide| will be set to NULL
+// if it's identical to |wide| on *all* platforms.
+
+static const struct {
+  const char* codepage_name;
+  const char* encoded;
+  OnStringConversionError::Type on_error;
+  bool success;
+  const wchar_t* wide;
+  const wchar_t* u16_wide;
+} kConvertCodepageCases[] = {
+    // Test a case where the input cannot be decoded, using SKIP, FAIL
+    // and SUBSTITUTE error handling rules. "A7 41" is valid, but "A6" isn't.
+    {"big5", "\xA7\x41\xA6", OnStringConversionError::FAIL, false, L"",
+     nullptr},
+    {"big5", "\xA7\x41\xA6", OnStringConversionError::SKIP, true, L"\x4F60",
+     nullptr},
+    {"big5", "\xA7\x41\xA6", OnStringConversionError::SUBSTITUTE, true,
+     L"\x4F60\xFFFD", nullptr},
+    // 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",
+     OnStringConversionError::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",
+     nullptr},
+    // Chinese Simplified (GB2312)
+    {"gb2312", "\xC4\xE3\xBA\xC3", OnStringConversionError::FAIL, true,
+     L"\x4F60\x597D", nullptr},
+    // Chinese (GB18030) : 4 byte sequences mapped to BMP characters
+    {"gb18030", "\x81\x30\x84\x36\xA1\xA7", OnStringConversionError::FAIL, true,
+     L"\x00A5\x00A8", nullptr},
+    // Chinese (GB18030) : A 4 byte sequence mapped to plane 2 (U+20000)
+    {"gb18030", "\x95\x32\x82\x36\xD2\xBB", OnStringConversionError::FAIL, true,
+#if defined(WCHAR_T_IS_UTF16)
+     L"\xD840\xDC00\x4E00",
+#elif defined(WCHAR_T_IS_UTF32)
+     L"\x20000\x4E00",
+#endif
+     L"\xD840\xDC00\x4E00"},
+    {"big5", "\xA7\x41\xA6\x6E", OnStringConversionError::FAIL, true,
+     L"\x4F60\x597D", nullptr},
+    // Greek (ISO-8859)
+    {"iso-8859-7",
+     "\xE3\xE5\xE9\xDC"
+     " "
+     "\xF3\xEF\xF5",
+     OnStringConversionError::FAIL, true,
+     L"\x03B3\x03B5\x03B9\x03AC"
+     L" "
+     L"\x03C3\x03BF\x03C5",
+     nullptr},
+    // Hebrew (Windows)
+    {"windows-1255", "\xF9\xD1\xC8\xEC\xE5\xC9\xED",
+     OnStringConversionError::FAIL, true,
+     L"\x05E9\x05C1\x05B8\x05DC\x05D5\x05B9\x05DD", nullptr},
+    // Korean (EUC)
+    {"euc-kr", "\xBE\xC8\xB3\xE7\xC7\xCF\xBC\xBC\xBF\xE4",
+     OnStringConversionError::FAIL, true, L"\xC548\xB155\xD558\xC138\xC694",
+     nullptr},
+    // Japanese (EUC)
+    {"euc-jp", "\xA4\xB3\xA4\xF3\xA4\xCB\xA4\xC1\xA4\xCF\xB0\xEC\x8E\xA6",
+     OnStringConversionError::FAIL, true,
+     L"\x3053\x3093\x306B\x3061\x306F\x4E00\xFF66", nullptr},
+    // Japanese (ISO-2022)
+    {"iso-2022-jp",
+     "\x1B$B"
+     "\x24\x33\x24\x73\x24\x4B\x24\x41\x24\x4F\x30\x6C"
+     "\x1B(B"
+     "ab"
+     "\x1B(J"
+     "\x5C\x7E#$"
+     "\x1B(B",
+     OnStringConversionError::FAIL, true,
+     L"\x3053\x3093\x306B\x3061\x306F\x4E00"
+     L"ab\x00A5\x203E#$",
+     nullptr},
+    // Japanese (Shift-JIS)
+    {"sjis", "\x82\xB1\x82\xF1\x82\xC9\x82\xBF\x82\xCD\x88\xEA\xA6",
+     OnStringConversionError::FAIL, true,
+     L"\x3053\x3093\x306B\x3061\x306F\x4E00\xFF66", nullptr},
+    // Russian (KOI8)
+    {"koi8-r", "\xDA\xC4\xD2\xC1\xD7\xD3\xD4\xD7\xD5\xCA\xD4\xC5",
+     OnStringConversionError::FAIL, true,
+     L"\x0437\x0434\x0440\x0430\x0432\x0441\x0442\x0432"
+     L"\x0443\x0439\x0442\x0435",
+     nullptr},
+    // Thai (windows-874)
+    {"windows-874",
+     "\xCA\xC7\xD1\xCA\xB4\xD5"
+     "\xA4\xC3\xD1\xBA",
+     OnStringConversionError::FAIL, true,
+     L"\x0E2A\x0E27\x0E31\x0E2A\x0E14\x0E35"
+     L"\x0E04\x0E23\x0e31\x0E1A",
+     nullptr},
+};
+
+TEST(ICUStringConversionsTest, ConvertBetweenCodepageAndUTF16) {
+  for (size_t i = 0; i < arraysize(kConvertCodepageCases); ++i) {
+    SCOPED_TRACE(base::StringPrintf(
+                     "Test[%" PRIuS "]: <encoded: %s> <codepage: %s>", i,
+                     kConvertCodepageCases[i].encoded,
+                     kConvertCodepageCases[i].codepage_name));
+
+    string16 utf16;
+    bool success = CodepageToUTF16(kConvertCodepageCases[i].encoded,
+                                   kConvertCodepageCases[i].codepage_name,
+                                   kConvertCodepageCases[i].on_error,
+                                   &utf16);
+    string16 utf16_expected;
+    if (kConvertCodepageCases[i].u16_wide == nullptr)
+      utf16_expected = BuildString16(kConvertCodepageCases[i].wide);
+    else
+      utf16_expected = BuildString16(kConvertCodepageCases[i].u16_wide);
+    EXPECT_EQ(kConvertCodepageCases[i].success, success);
+    EXPECT_EQ(utf16_expected, utf16);
+
+    // When decoding was successful and nothing was skipped, we also check the
+    // reverse conversion. See also the corresponding comment in
+    // ConvertBetweenCodepageAndWide.
+    if (success &&
+        kConvertCodepageCases[i].on_error == OnStringConversionError::FAIL) {
+      std::string encoded;
+      success = UTF16ToCodepage(utf16, kConvertCodepageCases[i].codepage_name,
+                                kConvertCodepageCases[i].on_error, &encoded);
+      EXPECT_EQ(kConvertCodepageCases[i].success, success);
+      EXPECT_EQ(kConvertCodepageCases[i].encoded, encoded);
+    }
+  }
+}
+
+static const struct {
+  const char* encoded;
+  const char* codepage_name;
+  bool expected_success;
+  const char* expected_value;
+} kConvertAndNormalizeCases[] = {
+  {"foo-\xe4.html", "iso-8859-1", true, "foo-\xc3\xa4.html"},
+  {"foo-\xe4.html", "iso-8859-7", true, "foo-\xce\xb4.html"},
+  {"foo-\xe4.html", "foo-bar", false, ""},
+  // HTML Encoding spec treats US-ASCII as synonymous with windows-1252
+  {"foo-\xff.html", "ascii", true, "foo-\xc3\xbf.html"},
+  {"foo.html", "ascii", true, "foo.html"},
+  {"foo-a\xcc\x88.html", "utf-8", true, "foo-\xc3\xa4.html"},
+  {"\x95\x32\x82\x36\xD2\xBB", "gb18030", true, "\xF0\xA0\x80\x80\xE4\xB8\x80"},
+  {"\xA7\x41\xA6\x6E", "big5", true, "\xE4\xBD\xA0\xE5\xA5\xBD"},
+  // Windows-1258 does have a combining character at xD2 (which is U+0309).
+  // The sequence of (U+00E2, U+0309) is also encoded as U+1EA9.
+  {"foo\xE2\xD2", "windows-1258", true, "foo\xE1\xBA\xA9"},
+  {"", "iso-8859-1", true, ""},
+};
+TEST(ICUStringConversionsTest, ConvertToUtf8AndNormalize) {
+  std::string result;
+  for (size_t i = 0; i < arraysize(kConvertAndNormalizeCases); ++i) {
+    SCOPED_TRACE(base::StringPrintf(
+                     "Test[%" PRIuS "]: <encoded: %s> <codepage: %s>", i,
+                     kConvertAndNormalizeCases[i].encoded,
+                     kConvertAndNormalizeCases[i].codepage_name));
+
+    bool success = ConvertToUtf8AndNormalize(
+        kConvertAndNormalizeCases[i].encoded,
+        kConvertAndNormalizeCases[i].codepage_name, &result);
+    EXPECT_EQ(kConvertAndNormalizeCases[i].expected_success, success);
+    EXPECT_EQ(kConvertAndNormalizeCases[i].expected_value, result);
+  }
+}
+
+}  // namespace base
diff --git a/base/i18n/icu_util.cc b/base/i18n/icu_util.cc
new file mode 100644
index 0000000..4d588c6
--- /dev/null
+++ b/base/i18n/icu_util.cc
@@ -0,0 +1,333 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/i18n/icu_util.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+
+#include <string>
+
+#include "base/debug/alias.h"
+#include "base/files/file_path.h"
+#include "base/files/memory_mapped_file.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "base/strings/string_util.h"
+#include "base/strings/sys_string_conversions.h"
+#include "build/build_config.h"
+#include "third_party/icu/source/common/unicode/putil.h"
+#include "third_party/icu/source/common/unicode/udata.h"
+#if (defined(OS_LINUX) && !defined(OS_CHROMEOS)) || defined(OS_ANDROID)
+#include "third_party/icu/source/i18n/unicode/timezone.h"
+#endif
+
+#if defined(OS_ANDROID)
+#include "base/android/apk_assets.h"
+#include "base/android/timezone_utils.h"
+#endif
+
+#if defined(OS_IOS)
+#include "base/ios/ios_util.h"
+#endif
+
+#if defined(OS_MACOSX)
+#include "base/mac/foundation_util.h"
+#endif
+
+#if defined(OS_FUCHSIA)
+#include "base/base_paths_fuchsia.h"
+#endif
+
+namespace base {
+namespace i18n {
+
+#if ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_SHARED
+#define ICU_UTIL_DATA_SYMBOL "icudt" U_ICU_VERSION_SHORT "_dat"
+#if defined(OS_WIN)
+#define ICU_UTIL_DATA_SHARED_MODULE_NAME "icudt.dll"
+#endif
+#endif
+
+namespace {
+#if !defined(OS_NACL)
+#if DCHECK_IS_ON()
+// 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.
+bool g_check_called_once = true;
+bool g_called_once = false;
+#endif  // DCHECK_IS_ON()
+
+#if ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE
+
+// To debug http://crbug.com/445616.
+int g_debug_icu_last_error;
+int g_debug_icu_load;
+int g_debug_icu_pf_error_details;
+int g_debug_icu_pf_last_error;
+#if defined(OS_WIN)
+wchar_t g_debug_icu_pf_filename[_MAX_PATH];
+#endif  // OS_WIN
+// Use an unversioned file name to simplify a icu version update down the road.
+// No need to change the filename in multiple places (gyp files, windows
+// build pkg configurations, etc). 'l' stands for Little Endian.
+// This variable is exported through the header file.
+const char kIcuDataFileName[] = "icudtl.dat";
+#if defined(OS_ANDROID)
+const char kAndroidAssetsIcuDataFileName[] = "assets/icudtl.dat";
+#endif
+
+// File handle intentionally never closed. Not using File here because its
+// Windows implementation guards against two instances owning the same
+// PlatformFile (which we allow since we know it is never freed).
+PlatformFile g_icudtl_pf = kInvalidPlatformFile;
+MemoryMappedFile* g_icudtl_mapped_file = nullptr;
+MemoryMappedFile::Region g_icudtl_region;
+
+void LazyInitIcuDataFile() {
+  if (g_icudtl_pf != kInvalidPlatformFile) {
+    return;
+  }
+#if defined(OS_ANDROID)
+  int fd = base::android::OpenApkAsset(kAndroidAssetsIcuDataFileName,
+                                       &g_icudtl_region);
+  g_icudtl_pf = fd;
+  if (fd != -1) {
+    return;
+  }
+// For unit tests, data file is located on disk, so try there as a fallback.
+#endif  // defined(OS_ANDROID)
+#if !defined(OS_MACOSX)
+  FilePath data_path;
+  if (!PathService::Get(DIR_ASSETS, &data_path)) {
+    LOG(ERROR) << "Can't find " << kIcuDataFileName;
+    return;
+  }
+#if defined(OS_WIN)
+  // TODO(brucedawson): http://crbug.com/445616
+  wchar_t tmp_buffer[_MAX_PATH] = {0};
+  wcscpy_s(tmp_buffer, data_path.value().c_str());
+  debug::Alias(tmp_buffer);
+#endif
+  data_path = data_path.AppendASCII(kIcuDataFileName);
+
+#if defined(OS_WIN)
+  // TODO(brucedawson): http://crbug.com/445616
+  wchar_t tmp_buffer2[_MAX_PATH] = {0};
+  wcscpy_s(tmp_buffer2, data_path.value().c_str());
+  debug::Alias(tmp_buffer2);
+#endif
+
+#else  // !defined(OS_MACOSX)
+  // Assume it is in the framework bundle's Resources directory.
+  ScopedCFTypeRef<CFStringRef> data_file_name(
+      SysUTF8ToCFStringRef(kIcuDataFileName));
+  FilePath data_path = mac::PathForFrameworkBundleResource(data_file_name);
+#if defined(OS_IOS)
+  FilePath override_data_path = base::ios::FilePathOfEmbeddedICU();
+  if (!override_data_path.empty()) {
+    data_path = override_data_path;
+  }
+#endif  // !defined(OS_IOS)
+  if (data_path.empty()) {
+    LOG(ERROR) << kIcuDataFileName << " not found in bundle";
+    return;
+  }
+#endif  // !defined(OS_MACOSX)
+  File file(data_path, File::FLAG_OPEN | File::FLAG_READ);
+  if (file.IsValid()) {
+    // TODO(brucedawson): http://crbug.com/445616.
+    g_debug_icu_pf_last_error = 0;
+    g_debug_icu_pf_error_details = 0;
+#if defined(OS_WIN)
+    g_debug_icu_pf_filename[0] = 0;
+#endif  // OS_WIN
+
+    g_icudtl_pf = file.TakePlatformFile();
+    g_icudtl_region = MemoryMappedFile::Region::kWholeFile;
+  }
+#if defined(OS_WIN)
+  else {
+    // TODO(brucedawson): http://crbug.com/445616.
+    g_debug_icu_pf_last_error = ::GetLastError();
+    g_debug_icu_pf_error_details = file.error_details();
+    wcscpy_s(g_debug_icu_pf_filename, data_path.value().c_str());
+  }
+#endif  // OS_WIN
+}
+
+bool InitializeICUWithFileDescriptorInternal(
+    PlatformFile data_fd,
+    const MemoryMappedFile::Region& data_region) {
+  // This can be called multiple times in tests.
+  if (g_icudtl_mapped_file) {
+    g_debug_icu_load = 0;  // To debug http://crbug.com/445616.
+    return true;
+  }
+  if (data_fd == kInvalidPlatformFile) {
+    g_debug_icu_load = 1;  // To debug http://crbug.com/445616.
+    LOG(ERROR) << "Invalid file descriptor to ICU data received.";
+    return false;
+  }
+
+  std::unique_ptr<MemoryMappedFile> icudtl_mapped_file(new MemoryMappedFile());
+  if (!icudtl_mapped_file->Initialize(File(data_fd), data_region)) {
+    g_debug_icu_load = 2;  // To debug http://crbug.com/445616.
+    LOG(ERROR) << "Couldn't mmap icu data file";
+    return false;
+  }
+  g_icudtl_mapped_file = icudtl_mapped_file.release();
+
+  UErrorCode err = U_ZERO_ERROR;
+  udata_setCommonData(const_cast<uint8_t*>(g_icudtl_mapped_file->data()), &err);
+  if (err != U_ZERO_ERROR) {
+    g_debug_icu_load = 3;  // To debug http://crbug.com/445616.
+    g_debug_icu_last_error = err;
+  }
+#if defined(OS_ANDROID)
+  else {
+    // On Android, we can't leave it up to ICU to set the default timezone
+    // because ICU's timezone detection does not work in many timezones (e.g.
+    // Australia/Sydney, Asia/Seoul, Europe/Paris ). Use JNI to detect the host
+    // timezone and set the ICU default timezone accordingly in advance of
+    // actual use. See crbug.com/722821 and
+    // https://ssl.icu-project.org/trac/ticket/13208 .
+    base::string16 timezone_id = base::android::GetDefaultTimeZoneId();
+    icu::TimeZone::adoptDefault(icu::TimeZone::createTimeZone(
+        icu::UnicodeString(FALSE, timezone_id.data(), timezone_id.length())));
+  }
+#endif
+  // Never try to load ICU data from files.
+  udata_setFileAccess(UDATA_ONLY_PACKAGES, &err);
+  return err == U_ZERO_ERROR;
+}
+#endif  // ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE
+#endif  // !defined(OS_NACL)
+
+}  // namespace
+
+#if !defined(OS_NACL)
+#if ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE
+#if defined(OS_ANDROID)
+bool InitializeICUWithFileDescriptor(
+    PlatformFile data_fd,
+    const MemoryMappedFile::Region& data_region) {
+#if DCHECK_IS_ON()
+  DCHECK(!g_check_called_once || !g_called_once);
+  g_called_once = true;
+#endif
+  return InitializeICUWithFileDescriptorInternal(data_fd, data_region);
+}
+
+PlatformFile GetIcuDataFileHandle(MemoryMappedFile::Region* out_region) {
+  CHECK_NE(g_icudtl_pf, kInvalidPlatformFile);
+  *out_region = g_icudtl_region;
+  return g_icudtl_pf;
+}
+#endif
+
+const uint8_t* GetRawIcuMemory() {
+  CHECK(g_icudtl_mapped_file);
+  return g_icudtl_mapped_file->data();
+}
+
+bool InitializeICUFromRawMemory(const uint8_t* raw_memory) {
+#if !defined(COMPONENT_BUILD)
+#if DCHECK_IS_ON()
+  DCHECK(!g_check_called_once || !g_called_once);
+  g_called_once = true;
+#endif
+
+  UErrorCode err = U_ZERO_ERROR;
+  udata_setCommonData(const_cast<uint8_t*>(raw_memory), &err);
+  // Never try to load ICU data from files.
+  udata_setFileAccess(UDATA_ONLY_PACKAGES, &err);
+  return err == U_ZERO_ERROR;
+#else
+  return true;
+#endif
+}
+
+#endif  // ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE
+
+bool InitializeICU() {
+#if DCHECK_IS_ON()
+  DCHECK(!g_check_called_once || !g_called_once);
+  g_called_once = true;
+#endif
+
+  bool result;
+#if (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_SHARED)
+  FilePath data_path;
+  PathService::Get(DIR_ASSETS, &data_path);
+  data_path = data_path.AppendASCII(ICU_UTIL_DATA_SHARED_MODULE_NAME);
+
+  HMODULE module = LoadLibrary(data_path.value().c_str());
+  if (!module) {
+    LOG(ERROR) << "Failed to load " << ICU_UTIL_DATA_SHARED_MODULE_NAME;
+    return false;
+  }
+
+  FARPROC addr = GetProcAddress(module, ICU_UTIL_DATA_SYMBOL);
+  if (!addr) {
+    LOG(ERROR) << ICU_UTIL_DATA_SYMBOL << ": not found in "
+               << ICU_UTIL_DATA_SHARED_MODULE_NAME;
+    return false;
+  }
+
+  UErrorCode err = U_ZERO_ERROR;
+  udata_setCommonData(reinterpret_cast<void*>(addr), &err);
+  // Never try to load ICU data from files.
+  udata_setFileAccess(UDATA_ONLY_PACKAGES, &err);
+  result = (err == U_ZERO_ERROR);
+#elif (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_STATIC)
+  // The ICU data is statically linked.
+  result = true;
+#elif (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE)
+  // If the ICU data directory is set, ICU won't actually load the data until
+  // it is needed.  This can fail if the process is sandboxed at that time.
+  // Instead, we map the file in and hand off the data so the sandbox won't
+  // cause any problems.
+  LazyInitIcuDataFile();
+  result =
+      InitializeICUWithFileDescriptorInternal(g_icudtl_pf, g_icudtl_region);
+#if defined(OS_WIN)
+  int debug_icu_load = g_debug_icu_load;
+  debug::Alias(&debug_icu_load);
+  int debug_icu_last_error = g_debug_icu_last_error;
+  debug::Alias(&debug_icu_last_error);
+  int debug_icu_pf_last_error = g_debug_icu_pf_last_error;
+  debug::Alias(&debug_icu_pf_last_error);
+  int debug_icu_pf_error_details = g_debug_icu_pf_error_details;
+  debug::Alias(&debug_icu_pf_error_details);
+  wchar_t debug_icu_pf_filename[_MAX_PATH] = {0};
+  wcscpy_s(debug_icu_pf_filename, g_debug_icu_pf_filename);
+  debug::Alias(&debug_icu_pf_filename);
+  CHECK(result);  // TODO(brucedawson): http://crbug.com/445616
+#endif
+#endif
+
+// To respond to the timezone change properly, the default timezone
+// cache in ICU has to be populated on starting up.
+// TODO(jungshik): Some callers do not care about tz at all. If necessary,
+// add a boolean argument to this function to init'd the default tz only
+// when requested.
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
+  if (result)
+    std::unique_ptr<icu::TimeZone> zone(icu::TimeZone::createDefault());
+#endif
+  return result;
+}
+#endif  // !defined(OS_NACL)
+
+void AllowMultipleInitializeCallsForTesting() {
+#if DCHECK_IS_ON() && !defined(OS_NACL)
+  g_check_called_once = false;
+#endif
+}
+
+}  // namespace i18n
+}  // namespace base
diff --git a/base/i18n/icu_util.h b/base/i18n/icu_util.h
new file mode 100644
index 0000000..5f9948f
--- /dev/null
+++ b/base/i18n/icu_util.h
@@ -0,0 +1,67 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_I18N_ICU_UTIL_H_
+#define BASE_I18N_ICU_UTIL_H_
+
+#include <stdint.h>
+
+#include "base/files/memory_mapped_file.h"
+#include "base/i18n/base_i18n_export.h"
+#include "build/build_config.h"
+
+#define ICU_UTIL_DATA_FILE   0
+#define ICU_UTIL_DATA_SHARED 1
+#define ICU_UTIL_DATA_STATIC 2
+
+namespace base {
+namespace i18n {
+
+#if !defined(OS_NACL)
+// Call this function to load ICU's data tables for the current process.  This
+// function should be called before ICU is used.
+BASE_I18N_EXPORT bool InitializeICU();
+
+#if ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE
+#if defined(OS_ANDROID)
+// Returns the PlatformFile and Region that was initialized by InitializeICU().
+// Use with InitializeICUWithFileDescriptor().
+BASE_I18N_EXPORT PlatformFile GetIcuDataFileHandle(
+    MemoryMappedFile::Region* out_region);
+
+// Android uses a file descriptor passed by browser process to initialize ICU
+// in render processes.
+BASE_I18N_EXPORT bool InitializeICUWithFileDescriptor(
+    PlatformFile data_fd,
+    const MemoryMappedFile::Region& data_region);
+#endif
+
+// Returns a void pointer to the memory mapped ICU data file.
+//
+// There are cases on Android where we would be unsafely reusing a file
+// descriptor within the same process when initializing two copies of ICU from
+// different binaries in the same address space. This returns an unowned
+// pointer to the memory mapped icu data file; consumers copies of base must
+// not outlive the copy of base that owns the memory mapped file.
+BASE_I18N_EXPORT const uint8_t* GetRawIcuMemory();
+
+// Initializes ICU memory
+//
+// This does nothing in component builds; this initialization should only be
+// done in cases where there could be two copies of base in a single process in
+// non-component builds. (The big example is standalone service libraries: the
+// Service Manager will have a copy of base linked in, and the majority of
+// service libraries will have base linked in but in non-component builds,
+// these will be separate copies of base.)
+BASE_I18N_EXPORT bool InitializeICUFromRawMemory(const uint8_t* raw_memory);
+#endif  // ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE
+#endif  // !defined(OS_NACL)
+
+// In a test binary, the call above might occur twice.
+BASE_I18N_EXPORT void AllowMultipleInitializeCallsForTesting();
+
+}  // namespace i18n
+}  // namespace base
+
+#endif  // BASE_I18N_ICU_UTIL_H_
diff --git a/base/i18n/message_formatter.cc b/base/i18n/message_formatter.cc
new file mode 100644
index 0000000..c69dd07
--- /dev/null
+++ b/base/i18n/message_formatter.cc
@@ -0,0 +1,142 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/i18n/message_formatter.h"
+
+#include "base/i18n/unicodestring.h"
+#include "base/logging.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/time/time.h"
+#include "third_party/icu/source/common/unicode/unistr.h"
+#include "third_party/icu/source/common/unicode/utypes.h"
+#include "third_party/icu/source/i18n/unicode/fmtable.h"
+#include "third_party/icu/source/i18n/unicode/msgfmt.h"
+
+using icu::UnicodeString;
+
+namespace base {
+namespace i18n {
+namespace {
+UnicodeString UnicodeStringFromStringPiece(StringPiece str) {
+  return UnicodeString::fromUTF8(
+      icu::StringPiece(str.data(), base::checked_cast<int32_t>(str.size())));
+}
+}  // anonymous namespace
+
+namespace internal {
+MessageArg::MessageArg() : formattable(nullptr) {}
+
+MessageArg::MessageArg(const char* s)
+    : formattable(new icu::Formattable(UnicodeStringFromStringPiece(s))) {}
+
+MessageArg::MessageArg(StringPiece s)
+    : formattable(new icu::Formattable(UnicodeStringFromStringPiece(s))) {}
+
+MessageArg::MessageArg(const std::string& s)
+    : formattable(new icu::Formattable(UnicodeString::fromUTF8(s))) {}
+
+MessageArg::MessageArg(const string16& s)
+    : formattable(new icu::Formattable(UnicodeString(s.data(), s.size()))) {}
+
+MessageArg::MessageArg(int i) : formattable(new icu::Formattable(i)) {}
+
+MessageArg::MessageArg(int64_t i) : formattable(new icu::Formattable(i)) {}
+
+MessageArg::MessageArg(double d) : formattable(new icu::Formattable(d)) {}
+
+MessageArg::MessageArg(const Time& t)
+    : formattable(new icu::Formattable(static_cast<UDate>(t.ToJsTime()))) {}
+
+MessageArg::~MessageArg() = default;
+
+// Tests if this argument has a value, and if so increments *count.
+bool MessageArg::has_value(int *count) const {
+  if (formattable == nullptr)
+    return false;
+
+  ++*count;
+  return true;
+}
+
+}  // namespace internal
+
+string16 MessageFormatter::FormatWithNumberedArgs(
+    StringPiece16 msg,
+    const internal::MessageArg& arg0,
+    const internal::MessageArg& arg1,
+    const internal::MessageArg& arg2,
+    const internal::MessageArg& arg3,
+    const internal::MessageArg& arg4,
+    const internal::MessageArg& arg5,
+    const internal::MessageArg& arg6) {
+  int32_t args_count = 0;
+  icu::Formattable args[] = {
+      arg0.has_value(&args_count) ? *arg0.formattable : icu::Formattable(),
+      arg1.has_value(&args_count) ? *arg1.formattable : icu::Formattable(),
+      arg2.has_value(&args_count) ? *arg2.formattable : icu::Formattable(),
+      arg3.has_value(&args_count) ? *arg3.formattable : icu::Formattable(),
+      arg4.has_value(&args_count) ? *arg4.formattable : icu::Formattable(),
+      arg5.has_value(&args_count) ? *arg5.formattable : icu::Formattable(),
+      arg6.has_value(&args_count) ? *arg6.formattable : icu::Formattable(),
+  };
+
+  UnicodeString msg_string(msg.data(), msg.size());
+  UErrorCode error = U_ZERO_ERROR;
+  icu::MessageFormat format(msg_string,  error);
+  icu::UnicodeString formatted;
+  icu::FieldPosition ignore(icu::FieldPosition::DONT_CARE);
+  format.format(args, args_count, formatted, ignore, error);
+  if (U_FAILURE(error)) {
+    LOG(ERROR) << "MessageFormat(" << msg.as_string() << ") failed with "
+               << u_errorName(error);
+    return string16();
+  }
+  return i18n::UnicodeStringToString16(formatted);
+}
+
+string16 MessageFormatter::FormatWithNamedArgs(
+    StringPiece16 msg,
+    StringPiece name0, const internal::MessageArg& arg0,
+    StringPiece name1, const internal::MessageArg& arg1,
+    StringPiece name2, const internal::MessageArg& arg2,
+    StringPiece name3, const internal::MessageArg& arg3,
+    StringPiece name4, const internal::MessageArg& arg4,
+    StringPiece name5, const internal::MessageArg& arg5,
+    StringPiece name6, const internal::MessageArg& arg6) {
+  icu::UnicodeString names[] = {
+      UnicodeStringFromStringPiece(name0),
+      UnicodeStringFromStringPiece(name1),
+      UnicodeStringFromStringPiece(name2),
+      UnicodeStringFromStringPiece(name3),
+      UnicodeStringFromStringPiece(name4),
+      UnicodeStringFromStringPiece(name5),
+      UnicodeStringFromStringPiece(name6),
+  };
+  int32_t args_count = 0;
+  icu::Formattable args[] = {
+      arg0.has_value(&args_count) ? *arg0.formattable : icu::Formattable(),
+      arg1.has_value(&args_count) ? *arg1.formattable : icu::Formattable(),
+      arg2.has_value(&args_count) ? *arg2.formattable : icu::Formattable(),
+      arg3.has_value(&args_count) ? *arg3.formattable : icu::Formattable(),
+      arg4.has_value(&args_count) ? *arg4.formattable : icu::Formattable(),
+      arg5.has_value(&args_count) ? *arg5.formattable : icu::Formattable(),
+      arg6.has_value(&args_count) ? *arg6.formattable : icu::Formattable(),
+  };
+
+  UnicodeString msg_string(msg.data(), msg.size());
+  UErrorCode error = U_ZERO_ERROR;
+  icu::MessageFormat format(msg_string, error);
+
+  icu::UnicodeString formatted;
+  format.format(names, args, args_count, formatted, error);
+  if (U_FAILURE(error)) {
+    LOG(ERROR) << "MessageFormat(" << msg.as_string() << ") failed with "
+               << u_errorName(error);
+    return string16();
+  }
+  return i18n::UnicodeStringToString16(formatted);
+}
+
+}  // namespace i18n
+}  // namespace base
diff --git a/base/i18n/message_formatter.h b/base/i18n/message_formatter.h
new file mode 100644
index 0000000..36a656d
--- /dev/null
+++ b/base/i18n/message_formatter.h
@@ -0,0 +1,128 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_I18N_MESSAGE_FORMATTER_H_
+#define BASE_I18N_MESSAGE_FORMATTER_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+
+#include "base/i18n/base_i18n_export.h"
+#include "base/macros.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_piece.h"
+#include "third_party/icu/source/common/unicode/uversion.h"
+
+U_NAMESPACE_BEGIN
+class Formattable;
+U_NAMESPACE_END
+
+namespace base {
+
+class Time;
+
+namespace i18n {
+
+class MessageFormatter;
+
+namespace internal {
+
+class BASE_I18N_EXPORT MessageArg {
+ public:
+  MessageArg(const char* s);
+  MessageArg(StringPiece s);
+  MessageArg(const std::string& s);
+  MessageArg(const string16& s);
+  MessageArg(int i);
+  MessageArg(int64_t i);
+  MessageArg(double d);
+  MessageArg(const Time& t);
+  ~MessageArg();
+
+ private:
+  friend class base::i18n::MessageFormatter;
+  MessageArg();
+  // Tests if this argument has a value, and if so increments *count.
+  bool has_value(int* count) const;
+  std::unique_ptr<icu::Formattable> formattable;
+  DISALLOW_COPY_AND_ASSIGN(MessageArg);
+};
+
+}  // namespace internal
+
+// Message Formatter with the ICU message format syntax support.
+// It can format strings (UTF-8 and UTF-16), numbers and base::Time with
+// plural, gender and other 'selectors' support. This is handy if you
+// have multiple parameters of differnt types and some of them require
+// plural or gender/selector support.
+//
+// To use this API for locale-sensitive formatting, retrieve a 'message
+// template' in the ICU message format from a message bundle (e.g. with
+// l10n_util::GetStringUTF16()) and pass it to FormatWith{Named,Numbered}Args.
+//
+// MessageFormat specs:
+//   http://icu-project.org/apiref/icu4j/com/ibm/icu/text/MessageFormat.html
+//   http://icu-project.org/apiref/icu4c/classicu_1_1DecimalFormat.html#details
+// Examples:
+//   http://userguide.icu-project.org/formatparse/messages
+//   message_formatter_unittest.cc
+//   go/plurals inside Google.
+//   TODO(jshin): Document this API in md format docs.
+// Caveat:
+//   When plural/select/gender is used along with other format specifiers such
+//   as date or number, plural/select/gender should be at the top level. It's
+//   not an ICU restriction but a constraint imposed by Google's translation
+//   infrastructure. Message A does not work. It must be revised to Message B.
+//
+//     A.
+//       Rated <ph name="RATING">{0, number,0.0}<ex>3.2</ex></ph>
+//       by {1, plural, =1{a user} other{# users}}
+//
+//     B.
+//       {1, plural,
+//         =1{Rated <ph name="RATING">{0, number,0.0}<ex>3.2</ex></ph>
+//             by a user.}
+//         other{Rated <ph name="RATING">{0, number,0.0}<ex>3.2</ex></ph>
+//               by # users.}}
+
+class BASE_I18N_EXPORT MessageFormatter {
+ public:
+  static string16 FormatWithNamedArgs(
+      StringPiece16 msg,
+      StringPiece name0 = StringPiece(),
+      const internal::MessageArg& arg0 = internal::MessageArg(),
+      StringPiece name1 = StringPiece(),
+      const internal::MessageArg& arg1 = internal::MessageArg(),
+      StringPiece name2 = StringPiece(),
+      const internal::MessageArg& arg2 = internal::MessageArg(),
+      StringPiece name3 = StringPiece(),
+      const internal::MessageArg& arg3 = internal::MessageArg(),
+      StringPiece name4 = StringPiece(),
+      const internal::MessageArg& arg4 = internal::MessageArg(),
+      StringPiece name5 = StringPiece(),
+      const internal::MessageArg& arg5 = internal::MessageArg(),
+      StringPiece name6 = StringPiece(),
+      const internal::MessageArg& arg6 = internal::MessageArg());
+
+  static string16 FormatWithNumberedArgs(
+      StringPiece16 msg,
+      const internal::MessageArg& arg0 = internal::MessageArg(),
+      const internal::MessageArg& arg1 = internal::MessageArg(),
+      const internal::MessageArg& arg2 = internal::MessageArg(),
+      const internal::MessageArg& arg3 = internal::MessageArg(),
+      const internal::MessageArg& arg4 = internal::MessageArg(),
+      const internal::MessageArg& arg5 = internal::MessageArg(),
+      const internal::MessageArg& arg6 = internal::MessageArg());
+
+ private:
+  MessageFormatter() = delete;
+  DISALLOW_COPY_AND_ASSIGN(MessageFormatter);
+};
+
+}  // namespace i18n
+}  // namespace base
+
+#endif  // BASE_I18N_MESSAGE_FORMATTER_H_
diff --git a/base/i18n/message_formatter_unittest.cc b/base/i18n/message_formatter_unittest.cc
new file mode 100644
index 0000000..a6f4613
--- /dev/null
+++ b/base/i18n/message_formatter_unittest.cc
@@ -0,0 +1,185 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/i18n/message_formatter.h"
+
+#include <memory>
+
+#include "base/i18n/rtl.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/time/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/icu/source/common/unicode/unistr.h"
+#include "third_party/icu/source/i18n/unicode/datefmt.h"
+#include "third_party/icu/source/i18n/unicode/msgfmt.h"
+
+typedef testing::Test MessageFormatterTest;
+
+namespace base {
+namespace i18n {
+
+class MessageFormatterTest : public testing::Test {
+ protected:
+  MessageFormatterTest() {
+    original_locale_ = GetConfiguredLocale();
+    SetICUDefaultLocale("en-US");
+  }
+  ~MessageFormatterTest() override {
+    SetICUDefaultLocale(original_locale_);
+  }
+
+ private:
+  std::string original_locale_;
+};
+
+namespace {
+
+void AppendFormattedDateTime(const std::unique_ptr<icu::DateFormat>& df,
+                             const Time& now,
+                             std::string* result) {
+  icu::UnicodeString formatted;
+  df->format(static_cast<UDate>(now.ToJsTime()), formatted).
+      toUTF8String(*result);
+}
+
+}  // namespace
+
+TEST_F(MessageFormatterTest, PluralNamedArgs) {
+  const string16 pattern = ASCIIToUTF16(
+      "{num_people, plural, "
+      "=0 {I met nobody in {place}.}"
+      "=1 {I met a person in {place}.}"
+      "other {I met # people in {place}.}}");
+
+  std::string result = UTF16ToASCII(MessageFormatter::FormatWithNamedArgs(
+      pattern, "num_people", 0, "place", "Paris"));
+  EXPECT_EQ("I met nobody in Paris.", result);
+  result = UTF16ToASCII(MessageFormatter::FormatWithNamedArgs(
+      pattern, "num_people", 1, "place", "Paris"));
+  EXPECT_EQ("I met a person in Paris.", result);
+  result = UTF16ToASCII(MessageFormatter::FormatWithNamedArgs(
+      pattern, "num_people", 5, "place", "Paris"));
+  EXPECT_EQ("I met 5 people in Paris.", result);
+}
+
+TEST_F(MessageFormatterTest, PluralNamedArgsWithOffset) {
+  const string16 pattern = ASCIIToUTF16(
+      "{num_people, plural, offset:1 "
+      "=0 {I met nobody in {place}.}"
+      "=1 {I met {person} in {place}.}"
+      "=2 {I met {person} and one other person in {place}.}"
+      "=13 {I met {person} and a dozen other people in {place}.}"
+      "other {I met {person} and # other people in {place}.}}");
+
+  std::string result = UTF16ToASCII(MessageFormatter::FormatWithNamedArgs(
+      pattern, "num_people", 0, "place", "Paris"));
+  EXPECT_EQ("I met nobody in Paris.", result);
+  // {person} is ignored if {num_people} is 0.
+  result = UTF16ToASCII(MessageFormatter::FormatWithNamedArgs(
+      pattern, "num_people", 0, "place", "Paris", "person", "Peter"));
+  EXPECT_EQ("I met nobody in Paris.", result);
+  result = UTF16ToASCII(MessageFormatter::FormatWithNamedArgs(
+      pattern, "num_people", 1, "place", "Paris", "person", "Peter"));
+  EXPECT_EQ("I met Peter in Paris.", result);
+  result = UTF16ToASCII(MessageFormatter::FormatWithNamedArgs(
+      pattern, "num_people", 2, "place", "Paris", "person", "Peter"));
+  EXPECT_EQ("I met Peter and one other person in Paris.", result);
+  result = UTF16ToASCII(MessageFormatter::FormatWithNamedArgs(
+      pattern, "num_people", 13, "place", "Paris", "person", "Peter"));
+  EXPECT_EQ("I met Peter and a dozen other people in Paris.", result);
+  result = UTF16ToASCII(MessageFormatter::FormatWithNamedArgs(
+      pattern, "num_people", 50, "place", "Paris", "person", "Peter"));
+  EXPECT_EQ("I met Peter and 49 other people in Paris.", result);
+}
+
+TEST_F(MessageFormatterTest, PluralNumberedArgs) {
+  const string16 pattern = ASCIIToUTF16(
+      "{1, plural, "
+      "=1 {The cert for {0} expired yesterday.}"
+      "=7 {The cert for {0} expired a week ago.}"
+      "other {The cert for {0} expired # days ago.}}");
+
+  std::string result = UTF16ToASCII(MessageFormatter::FormatWithNumberedArgs(
+      pattern, "example.com", 1));
+  EXPECT_EQ("The cert for example.com expired yesterday.", result);
+  result = UTF16ToASCII(MessageFormatter::FormatWithNumberedArgs(
+      pattern, "example.com", 7));
+  EXPECT_EQ("The cert for example.com expired a week ago.", result);
+  result = UTF16ToASCII(MessageFormatter::FormatWithNumberedArgs(
+      pattern, "example.com", 15));
+  EXPECT_EQ("The cert for example.com expired 15 days ago.", result);
+}
+
+TEST_F(MessageFormatterTest, PluralNumberedArgsWithDate) {
+  const string16 pattern = ASCIIToUTF16(
+      "{1, plural, "
+      "=1 {The cert for {0} expired yesterday. Today is {2,date,full}}"
+      "other {The cert for {0} expired # days ago. Today is {2,date,full}}}");
+
+  base::Time now = base::Time::Now();
+  using icu::DateFormat;
+  std::unique_ptr<DateFormat> df(
+      DateFormat::createDateInstance(DateFormat::FULL));
+  std::string second_sentence = " Today is ";
+  AppendFormattedDateTime(df, now, &second_sentence);
+
+  std::string result = UTF16ToASCII(MessageFormatter::FormatWithNumberedArgs(
+      pattern, "example.com", 1, now));
+  EXPECT_EQ("The cert for example.com expired yesterday." + second_sentence,
+            result);
+  result = UTF16ToASCII(MessageFormatter::FormatWithNumberedArgs(
+      pattern, "example.com", 15, now));
+  EXPECT_EQ("The cert for example.com expired 15 days ago." + second_sentence,
+            result);
+}
+
+TEST_F(MessageFormatterTest, DateTimeAndNumber) {
+  // Note that using 'mph' for all locales is not a good i18n practice.
+  const string16 pattern = ASCIIToUTF16(
+      "At {0,time, short} on {0,date, medium}, "
+      "there was {1} at building {2,number,integer}. "
+      "The speed of the wind was {3,number,###.#} mph.");
+
+  using icu::DateFormat;
+  std::unique_ptr<DateFormat> tf(
+      DateFormat::createTimeInstance(DateFormat::SHORT));
+  std::unique_ptr<DateFormat> df(
+      DateFormat::createDateInstance(DateFormat::MEDIUM));
+
+  base::Time now = base::Time::Now();
+  std::string expected = "At ";
+  AppendFormattedDateTime(tf, now, &expected);
+  expected.append(" on ");
+  AppendFormattedDateTime(df, now, &expected);
+  expected.append(", there was an explosion at building 3. "
+                  "The speed of the wind was 37.4 mph.");
+
+  EXPECT_EQ(expected, UTF16ToASCII(MessageFormatter::FormatWithNumberedArgs(
+      pattern, now, "an explosion", 3, 37.413)));
+}
+
+TEST_F(MessageFormatterTest, SelectorSingleOrMultiple) {
+  const string16 pattern = ASCIIToUTF16(
+      "{0, select,"
+      "single {Select a file to upload.}"
+      "multiple {Select files to upload.}"
+      "other {UNUSED}}");
+
+  std::string result = UTF16ToASCII(MessageFormatter::FormatWithNumberedArgs(
+      pattern, "single"));
+  EXPECT_EQ("Select a file to upload.", result);
+  result = UTF16ToASCII(MessageFormatter::FormatWithNumberedArgs(
+      pattern, "multiple"));
+  EXPECT_EQ("Select files to upload.", result);
+
+  // fallback if a parameter is not selectors specified in the message pattern.
+  result = UTF16ToASCII(MessageFormatter::FormatWithNumberedArgs(
+      pattern, "foobar"));
+  EXPECT_EQ("UNUSED", result);
+}
+
+}  // namespace i18n
+}  // namespace base
diff --git a/base/i18n/number_formatting.cc b/base/i18n/number_formatting.cc
new file mode 100644
index 0000000..0ab031e
--- /dev/null
+++ b/base/i18n/number_formatting.cc
@@ -0,0 +1,97 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/i18n/number_formatting.h"
+
+#include <stddef.h>
+
+#include <memory>
+
+#include "base/format_macros.h"
+#include "base/i18n/message_formatter.h"
+#include "base/i18n/unicodestring.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "third_party/icu/source/common/unicode/ustring.h"
+#include "third_party/icu/source/i18n/unicode/numfmt.h"
+
+namespace base {
+
+namespace {
+
+// A simple wrapper around icu::NumberFormat that allows for resetting it
+// (as LazyInstance does not).
+struct NumberFormatWrapper {
+  NumberFormatWrapper() {
+    Reset();
+  }
+
+  void Reset() {
+    // There's no ICU call to destroy a NumberFormat object other than
+    // operator delete, so use the default Delete, which calls operator delete.
+    // This can cause problems if a different allocator is used by this file
+    // than by ICU.
+    UErrorCode status = U_ZERO_ERROR;
+    number_format.reset(icu::NumberFormat::createInstance(status));
+    DCHECK(U_SUCCESS(status));
+  }
+
+  std::unique_ptr<icu::NumberFormat> number_format;
+};
+
+LazyInstance<NumberFormatWrapper>::DestructorAtExit g_number_format_int =
+    LAZY_INSTANCE_INITIALIZER;
+LazyInstance<NumberFormatWrapper>::DestructorAtExit g_number_format_float =
+    LAZY_INSTANCE_INITIALIZER;
+
+}  // namespace
+
+string16 FormatNumber(int64_t number) {
+  icu::NumberFormat* number_format =
+      g_number_format_int.Get().number_format.get();
+
+  if (!number_format) {
+    // As a fallback, just return the raw number in a string.
+    return ASCIIToUTF16(StringPrintf("%" PRId64, number));
+  }
+  icu::UnicodeString ustr;
+  number_format->format(number, ustr);
+
+  return i18n::UnicodeStringToString16(ustr);
+}
+
+string16 FormatDouble(double number, int fractional_digits) {
+  icu::NumberFormat* number_format =
+      g_number_format_float.Get().number_format.get();
+
+  if (!number_format) {
+    // As a fallback, just return the raw number in a string.
+    return ASCIIToUTF16(StringPrintf("%f", number));
+  }
+  number_format->setMaximumFractionDigits(fractional_digits);
+  number_format->setMinimumFractionDigits(fractional_digits);
+  icu::UnicodeString ustr;
+  number_format->format(number, ustr);
+
+  return i18n::UnicodeStringToString16(ustr);
+}
+
+string16 FormatPercent(int number) {
+  return i18n::MessageFormatter::FormatWithNumberedArgs(
+      ASCIIToUTF16("{0,number,percent}"), static_cast<double>(number) / 100.0);
+}
+
+namespace testing {
+
+void ResetFormatters() {
+  g_number_format_int.Get().Reset();
+  g_number_format_float.Get().Reset();
+}
+
+}  // namespace testing
+
+}  // namespace base
diff --git a/base/i18n/number_formatting.h b/base/i18n/number_formatting.h
new file mode 100644
index 0000000..9636bf4
--- /dev/null
+++ b/base/i18n/number_formatting.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_I18N_NUMBER_FORMATTING_H_
+#define BASE_I18N_NUMBER_FORMATTING_H_
+
+#include <stdint.h>
+
+#include "base/i18n/base_i18n_export.h"
+#include "base/strings/string16.h"
+
+namespace base {
+
+// Return a number formatted with separators in the user's locale.
+// Ex: FormatNumber(1234567) => "1,234,567" in English, "1.234.567" in German
+BASE_I18N_EXPORT string16 FormatNumber(int64_t number);
+
+// Return a number formatted with separators in the user's locale.
+// Ex: FormatDouble(1234567.8, 1)
+//         => "1,234,567.8" in English, "1.234.567,8" in German
+BASE_I18N_EXPORT string16 FormatDouble(double number, int fractional_digits);
+
+// Return a percentage formatted with space and symbol in the user's locale.
+// Ex: FormatPercent(12) => "12%" in English, "12 %" in Romanian
+BASE_I18N_EXPORT string16 FormatPercent(int number);
+
+namespace testing {
+
+// Causes cached formatters to be discarded and recreated. Only useful for
+// testing.
+BASE_I18N_EXPORT void ResetFormatters();
+
+}  // namespace testing
+
+}  // namespace base
+
+#endif  // BASE_I18N_NUMBER_FORMATTING_H_
diff --git a/base/i18n/number_formatting_unittest.cc b/base/i18n/number_formatting_unittest.cc
new file mode 100644
index 0000000..d2eb568
--- /dev/null
+++ b/base/i18n/number_formatting_unittest.cc
@@ -0,0 +1,142 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <limits>
+
+#include "base/i18n/number_formatting.h"
+#include "base/i18n/rtl.h"
+#include "base/macros.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/icu_test_util.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/icu/source/i18n/unicode/usearch.h"
+
+namespace base {
+namespace {
+
+TEST(NumberFormattingTest, FormatNumber) {
+  static const struct {
+    int64_t number;
+    const char* expected_english;
+    const char* expected_german;
+  } cases[] = {
+    {0, "0", "0"},
+    {1024, "1,024", "1.024"},
+    {std::numeric_limits<int64_t>::max(),
+        "9,223,372,036,854,775,807", "9.223.372.036.854.775.807"},
+    {std::numeric_limits<int64_t>::min(),
+        "-9,223,372,036,854,775,808", "-9.223.372.036.854.775.808"},
+    {-42, "-42", "-42"},
+  };
+
+  test::ScopedRestoreICUDefaultLocale restore_locale;
+
+  for (size_t i = 0; i < arraysize(cases); ++i) {
+    i18n::SetICUDefaultLocale("en");
+    testing::ResetFormatters();
+    EXPECT_EQ(cases[i].expected_english,
+              UTF16ToUTF8(FormatNumber(cases[i].number)));
+    i18n::SetICUDefaultLocale("de");
+    testing::ResetFormatters();
+    EXPECT_EQ(cases[i].expected_german,
+              UTF16ToUTF8(FormatNumber(cases[i].number)));
+  }
+}
+
+TEST(NumberFormattingTest, FormatDouble) {
+  static const struct {
+    double number;
+    int frac_digits;
+    const char* expected_english;
+    const char* expected_german;
+  } cases[] = {
+    {0.0, 0, "0", "0"},
+#if !defined(OS_ANDROID)
+    // Bionic can't printf negative zero correctly.
+    {-0.0, 4, "-0.0000", "-0,0000"},
+#endif
+    {1024.2, 0, "1,024", "1.024"},
+    {-1024.223, 2, "-1,024.22", "-1.024,22"},
+    {std::numeric_limits<double>::max(), 6,
+     "179,769,313,486,231,570,000,000,000,000,000,000,000,000,000,000,000,"
+     "000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,"
+     "000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,"
+     "000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,"
+     "000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,"
+     "000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,"
+     "000.000000",
+     "179.769.313.486.231.570.000.000.000.000.000.000.000.000.000.000.000."
+     "000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000."
+     "000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000."
+     "000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000."
+     "000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000."
+     "000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000."
+     "000,000000"},
+    {std::numeric_limits<double>::min(), 2, "0.00", "0,00"},
+    {-42.7, 3, "-42.700", "-42,700"},
+  };
+
+  test::ScopedRestoreICUDefaultLocale restore_locale;
+  for (size_t i = 0; i < arraysize(cases); ++i) {
+    i18n::SetICUDefaultLocale("en");
+    testing::ResetFormatters();
+    EXPECT_EQ(cases[i].expected_english,
+              UTF16ToUTF8(FormatDouble(cases[i].number, cases[i].frac_digits)));
+    i18n::SetICUDefaultLocale("de");
+    testing::ResetFormatters();
+    EXPECT_EQ(cases[i].expected_german,
+              UTF16ToUTF8(FormatDouble(cases[i].number, cases[i].frac_digits)));
+  }
+}
+
+TEST(NumberFormattingTest, FormatPercent) {
+  static const struct {
+    int64_t number;
+    const char* expected_english;
+    const char* expected_german;  // Note: Space before % isn't \x20.
+    // Note: Eastern Arabic-Indic digits (U+06Fx) for Persian and
+    // Arabic-Indic digits (U+066x) for Arabic in Egypt(ar-EG). In Arabic (ar),
+    // uses European digits (Google-patch).
+    // See https://unicode.org/cldr/trac/ticket/9040 for details.
+    // See also https://unicode.org/cldr/trac/ticket/10176 .
+    // For now, take what CLDR 32 has (percent sign to the right of
+    // a number in Persian).
+    const char* expected_persian;
+    const char* expected_arabic;
+    const char* expected_arabic_egypt;
+  } cases[] = {
+      {0, "0%", u8"0\u00a0%", u8"\u06f0\u066a", u8"0\u200e%\u200e",
+       u8"\u0660\u066a\u061c"},
+      {42, "42%", "42\u00a0%", u8"\u06f4\u06f2\u066a", u8"42\u200e%\u200e",
+       "\u0664\u0662\u066a\u061c"},
+      {1024, "1,024%", "1.024\u00a0%", u8"\u06f1\u066c\u06f0\u06f2\u06f4\u066a",
+       "1,024\u200e%\u200e", "\u0661\u066c\u0660\u0662\u0664\u066a\u061c"},
+  };
+
+  test::ScopedRestoreICUDefaultLocale restore_locale;
+  for (size_t i = 0; i < arraysize(cases); ++i) {
+    i18n::SetICUDefaultLocale("en");
+    EXPECT_EQ(ASCIIToUTF16(cases[i].expected_english),
+              FormatPercent(cases[i].number));
+    i18n::SetICUDefaultLocale("de");
+    EXPECT_EQ(UTF8ToUTF16(cases[i].expected_german),
+              FormatPercent(cases[i].number));
+    i18n::SetICUDefaultLocale("fa");
+    EXPECT_EQ(UTF8ToUTF16(cases[i].expected_persian),
+              FormatPercent(cases[i].number));
+    i18n::SetICUDefaultLocale("ar");
+    EXPECT_EQ(UTF8ToUTF16(cases[i].expected_arabic),
+              FormatPercent(cases[i].number));
+    i18n::SetICUDefaultLocale("ar-EG");
+    EXPECT_EQ(UTF8ToUTF16(cases[i].expected_arabic_egypt),
+              FormatPercent(cases[i].number));
+  }
+}
+
+}  // namespace
+}  // namespace base
diff --git a/base/i18n/rtl.cc b/base/i18n/rtl.cc
new file mode 100644
index 0000000..5a8db8a
--- /dev/null
+++ b/base/i18n/rtl.cc
@@ -0,0 +1,496 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/i18n/rtl.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <algorithm>
+
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/i18n/base_i18n_switches.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "base/strings/sys_string_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
+#include "third_party/icu/source/common/unicode/locid.h"
+#include "third_party/icu/source/common/unicode/uchar.h"
+#include "third_party/icu/source/common/unicode/uscript.h"
+#include "third_party/icu/source/i18n/unicode/coll.h"
+
+#if defined(OS_IOS)
+#include "base/debug/crash_logging.h"
+#include "base/ios/ios_util.h"
+#endif
+
+namespace {
+
+// Extract language, country and variant, but ignore keywords.  For example,
+// en-US, ca@valencia, ca-ES@valencia.
+std::string GetLocaleString(const icu::Locale& locale) {
+  const char* language = locale.getLanguage();
+  const char* country = locale.getCountry();
+  const char* variant = locale.getVariant();
+
+  std::string result =
+      (language != nullptr && *language != '\0') ? language : "und";
+
+  if (country != nullptr && *country != '\0') {
+    result += '-';
+    result += country;
+  }
+
+  if (variant != nullptr && *variant != '\0')
+    result += '@' + base::ToLowerASCII(variant);
+
+  return result;
+}
+
+// Returns LEFT_TO_RIGHT or RIGHT_TO_LEFT if |character| has strong
+// directionality, returns UNKNOWN_DIRECTION if it doesn't. Please refer to
+// http://unicode.org/reports/tr9/ for more information.
+base::i18n::TextDirection GetCharacterDirection(UChar32 character) {
+  static bool has_switch = base::CommandLine::ForCurrentProcess()->HasSwitch(
+      switches::kForceTextDirection);
+  if (has_switch) {
+    base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+    std::string force_flag =
+        command_line->GetSwitchValueASCII(switches::kForceTextDirection);
+
+    if (force_flag == switches::kForceDirectionRTL)
+      return base::i18n::RIGHT_TO_LEFT;
+    if (force_flag == switches::kForceDirectionLTR)
+      return base::i18n::LEFT_TO_RIGHT;
+  }
+  // Now that we have the character, we use ICU in order to query for the
+  // appropriate Unicode BiDi character type.
+  int32_t property = u_getIntPropertyValue(character, UCHAR_BIDI_CLASS);
+  if ((property == U_RIGHT_TO_LEFT) ||
+      (property == U_RIGHT_TO_LEFT_ARABIC) ||
+      (property == U_RIGHT_TO_LEFT_EMBEDDING) ||
+      (property == U_RIGHT_TO_LEFT_OVERRIDE)) {
+    return base::i18n::RIGHT_TO_LEFT;
+  } else if ((property == U_LEFT_TO_RIGHT) ||
+             (property == U_LEFT_TO_RIGHT_EMBEDDING) ||
+             (property == U_LEFT_TO_RIGHT_OVERRIDE)) {
+    return base::i18n::LEFT_TO_RIGHT;
+  }
+  return base::i18n::UNKNOWN_DIRECTION;
+}
+
+}  // namespace
+
+namespace base {
+namespace i18n {
+
+// Represents the locale-specific ICU text direction.
+static TextDirection g_icu_text_direction = UNKNOWN_DIRECTION;
+
+// Convert the ICU default locale to a string.
+std::string GetConfiguredLocale() {
+  return GetLocaleString(icu::Locale::getDefault());
+}
+
+// Convert the ICU canonicalized locale to a string.
+std::string GetCanonicalLocale(const std::string& locale) {
+  return GetLocaleString(icu::Locale::createCanonical(locale.c_str()));
+}
+
+// Convert Chrome locale name to ICU locale name
+std::string ICULocaleName(const std::string& locale_string) {
+  // If not Spanish, just return it.
+  if (locale_string.substr(0, 2) != "es")
+    return locale_string;
+  // Expand es to es-ES.
+  if (LowerCaseEqualsASCII(locale_string, "es"))
+    return "es-ES";
+  // Map es-419 (Latin American Spanish) to es-FOO depending on the system
+  // locale.  If it's es-RR other than es-ES, map to es-RR. Otherwise, map
+  // to es-MX (the most populous in Spanish-speaking Latin America).
+  if (LowerCaseEqualsASCII(locale_string, "es-419")) {
+    const icu::Locale& locale = icu::Locale::getDefault();
+    std::string language = locale.getLanguage();
+    const char* country = locale.getCountry();
+    if (LowerCaseEqualsASCII(language, "es") &&
+      !LowerCaseEqualsASCII(country, "es")) {
+        language += '-';
+        language += country;
+        return language;
+    }
+    return "es-MX";
+  }
+  // Currently, Chrome has only "es" and "es-419", but later we may have
+  // more specific "es-RR".
+  return locale_string;
+}
+
+void SetICUDefaultLocale(const std::string& locale_string) {
+#if defined(OS_IOS)
+  static base::debug::CrashKeyString* crash_key_locale =
+      base::debug::AllocateCrashKeyString("icu_locale_input",
+                                          base::debug::CrashKeySize::Size256);
+  base::debug::SetCrashKeyString(crash_key_locale, locale_string);
+#endif
+  icu::Locale locale(ICULocaleName(locale_string).c_str());
+  UErrorCode error_code = U_ZERO_ERROR;
+  const char* lang = locale.getLanguage();
+  if (lang != nullptr && *lang != '\0') {
+    icu::Locale::setDefault(locale, error_code);
+  } else {
+    LOG(ERROR) << "Failed to set the ICU default locale to " << locale_string
+               << ". Falling back to en-US.";
+    icu::Locale::setDefault(icu::Locale::getUS(), error_code);
+  }
+  g_icu_text_direction = UNKNOWN_DIRECTION;
+}
+
+bool IsRTL() {
+  return ICUIsRTL();
+}
+
+void SetRTLForTesting(bool rtl) {
+  SetICUDefaultLocale(rtl ? "he" : "en");
+  DCHECK_EQ(rtl, IsRTL());
+}
+
+bool ICUIsRTL() {
+  if (g_icu_text_direction == UNKNOWN_DIRECTION) {
+    const icu::Locale& locale = icu::Locale::getDefault();
+    g_icu_text_direction = GetTextDirectionForLocaleInStartUp(locale.getName());
+  }
+  return g_icu_text_direction == RIGHT_TO_LEFT;
+}
+
+TextDirection GetForcedTextDirection() {
+// On iOS, check for RTL forcing.
+#if defined(OS_IOS)
+  if (base::ios::IsInForcedRTL())
+    return base::i18n::RIGHT_TO_LEFT;
+#endif
+
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+  if (command_line->HasSwitch(switches::kForceUIDirection)) {
+    std::string force_flag =
+        command_line->GetSwitchValueASCII(switches::kForceUIDirection);
+
+    if (force_flag == switches::kForceDirectionLTR)
+      return base::i18n::LEFT_TO_RIGHT;
+
+    if (force_flag == switches::kForceDirectionRTL)
+      return base::i18n::RIGHT_TO_LEFT;
+  }
+
+  return base::i18n::UNKNOWN_DIRECTION;
+}
+
+TextDirection GetTextDirectionForLocaleInStartUp(const char* locale_name) {
+  // Check for direction forcing.
+  TextDirection forced_direction = GetForcedTextDirection();
+  if (forced_direction != UNKNOWN_DIRECTION)
+    return forced_direction;
+
+  // This list needs to be updated in alphabetical order if we add more RTL
+  // locales.
+  static const char kRTLLanguageCodes[][3] = {"ar", "fa", "he", "iw", "ur"};
+  std::vector<StringPiece> locale_split =
+      SplitStringPiece(locale_name, "-_", KEEP_WHITESPACE, SPLIT_WANT_ALL);
+  const StringPiece& language_code = locale_split[0];
+  if (std::binary_search(kRTLLanguageCodes,
+                         kRTLLanguageCodes + arraysize(kRTLLanguageCodes),
+                         language_code))
+    return RIGHT_TO_LEFT;
+  return LEFT_TO_RIGHT;
+}
+
+TextDirection GetTextDirectionForLocale(const char* locale_name) {
+  // Check for direction forcing.
+  TextDirection forced_direction = GetForcedTextDirection();
+  if (forced_direction != UNKNOWN_DIRECTION)
+    return forced_direction;
+
+  UErrorCode status = U_ZERO_ERROR;
+  ULayoutType layout_dir = uloc_getCharacterOrientation(locale_name, &status);
+  DCHECK(U_SUCCESS(status));
+  // Treat anything other than RTL as LTR.
+  return (layout_dir != ULOC_LAYOUT_RTL) ? LEFT_TO_RIGHT : RIGHT_TO_LEFT;
+}
+
+TextDirection GetFirstStrongCharacterDirection(const string16& text) {
+  const UChar* string = text.c_str();
+  size_t length = text.length();
+  size_t position = 0;
+  while (position < length) {
+    UChar32 character;
+    size_t next_position = position;
+    U16_NEXT(string, next_position, length, character);
+    TextDirection direction = GetCharacterDirection(character);
+    if (direction != UNKNOWN_DIRECTION)
+      return direction;
+    position = next_position;
+  }
+  return LEFT_TO_RIGHT;
+}
+
+TextDirection GetLastStrongCharacterDirection(const string16& text) {
+  const UChar* string = text.c_str();
+  size_t position = text.length();
+  while (position > 0) {
+    UChar32 character;
+    size_t prev_position = position;
+    U16_PREV(string, 0, prev_position, character);
+    TextDirection direction = GetCharacterDirection(character);
+    if (direction != UNKNOWN_DIRECTION)
+      return direction;
+    position = prev_position;
+  }
+  return LEFT_TO_RIGHT;
+}
+
+TextDirection GetStringDirection(const string16& text) {
+  const UChar* string = text.c_str();
+  size_t length = text.length();
+  size_t position = 0;
+
+  TextDirection result(UNKNOWN_DIRECTION);
+  while (position < length) {
+    UChar32 character;
+    size_t next_position = position;
+    U16_NEXT(string, next_position, length, character);
+    TextDirection direction = GetCharacterDirection(character);
+    if (direction != UNKNOWN_DIRECTION) {
+      if (result != UNKNOWN_DIRECTION && result != direction)
+        return UNKNOWN_DIRECTION;
+      result = direction;
+    }
+    position = next_position;
+  }
+
+  // Handle the case of a string not containing any strong directionality
+  // characters defaulting to LEFT_TO_RIGHT.
+  if (result == UNKNOWN_DIRECTION)
+    return LEFT_TO_RIGHT;
+
+  return result;
+}
+
+#if defined(OS_WIN)
+bool AdjustStringForLocaleDirection(string16* text) {
+  if (!IsRTL() || text->empty())
+    return false;
+
+  // Marking the string as LTR if the locale is RTL and the string does not
+  // contain strong RTL characters. Otherwise, mark the string as RTL.
+  bool has_rtl_chars = StringContainsStrongRTLChars(*text);
+  if (!has_rtl_chars)
+    WrapStringWithLTRFormatting(text);
+  else
+    WrapStringWithRTLFormatting(text);
+
+  return true;
+}
+
+bool UnadjustStringForLocaleDirection(string16* text) {
+  if (!IsRTL() || text->empty())
+    return false;
+
+  *text = StripWrappingBidiControlCharacters(*text);
+  return true;
+}
+#else
+bool AdjustStringForLocaleDirection(string16* text) {
+  // On OS X & GTK the directionality of a label is determined by the first
+  // strongly directional character.
+  // However, we want to make sure that in an LTR-language-UI all strings are
+  // left aligned and vice versa.
+  // A problem can arise if we display a string which starts with user input.
+  // User input may be of the opposite directionality to the UI. So the whole
+  // string will be displayed in the opposite directionality, e.g. if we want to
+  // display in an LTR UI [such as US English]:
+  //
+  // EMAN_NOISNETXE is now installed.
+  //
+  // Since EXTENSION_NAME begins with a strong RTL char, the label's
+  // directionality will be set to RTL and the string will be displayed visually
+  // as:
+  //
+  // .is now installed EMAN_NOISNETXE
+  //
+  // In order to solve this issue, we prepend an LRM to the string. An LRM is a
+  // strongly directional LTR char.
+  // We also append an LRM at the end, which ensures that we're in an LTR
+  // context.
+
+  // Unlike Windows, Linux and OS X can correctly display RTL glyphs out of the
+  // box so there is no issue with displaying zero-width bidi control characters
+  // on any system.  Thus no need for the !IsRTL() check here.
+  if (text->empty())
+    return false;
+
+  bool ui_direction_is_rtl = IsRTL();
+
+  bool has_rtl_chars = StringContainsStrongRTLChars(*text);
+  if (!ui_direction_is_rtl && has_rtl_chars) {
+    WrapStringWithRTLFormatting(text);
+    text->insert(static_cast<size_t>(0), static_cast<size_t>(1),
+                 kLeftToRightMark);
+    text->push_back(kLeftToRightMark);
+  } else if (ui_direction_is_rtl && has_rtl_chars) {
+    WrapStringWithRTLFormatting(text);
+    text->insert(static_cast<size_t>(0), static_cast<size_t>(1),
+                 kRightToLeftMark);
+    text->push_back(kRightToLeftMark);
+  } else if (ui_direction_is_rtl) {
+    WrapStringWithLTRFormatting(text);
+    text->insert(static_cast<size_t>(0), static_cast<size_t>(1),
+                 kRightToLeftMark);
+    text->push_back(kRightToLeftMark);
+  } else {
+    return false;
+  }
+
+  return true;
+}
+
+bool UnadjustStringForLocaleDirection(string16* text) {
+  if (text->empty())
+    return false;
+
+  size_t begin_index = 0;
+  char16 begin = text->at(begin_index);
+  if (begin == kLeftToRightMark ||
+      begin == kRightToLeftMark) {
+    ++begin_index;
+  }
+
+  size_t end_index = text->length() - 1;
+  char16 end = text->at(end_index);
+  if (end == kLeftToRightMark ||
+      end == kRightToLeftMark) {
+    --end_index;
+  }
+
+  string16 unmarked_text =
+      text->substr(begin_index, end_index - begin_index + 1);
+  *text = StripWrappingBidiControlCharacters(unmarked_text);
+  return true;
+}
+
+#endif  // !OS_WIN
+
+void EnsureTerminatedDirectionalFormatting(string16* text) {
+  int count = 0;
+  for (auto c : *text) {
+    if (c == kLeftToRightEmbeddingMark || c == kRightToLeftEmbeddingMark ||
+        c == kLeftToRightOverride || c == kRightToLeftOverride) {
+      ++count;
+    } else if (c == kPopDirectionalFormatting && count > 0) {
+      --count;
+    }
+  }
+  for (int j = 0; j < count; j++)
+    text->push_back(kPopDirectionalFormatting);
+}
+
+void SanitizeUserSuppliedString(string16* text) {
+  EnsureTerminatedDirectionalFormatting(text);
+  AdjustStringForLocaleDirection(text);
+}
+
+bool StringContainsStrongRTLChars(const string16& text) {
+  const UChar* string = text.c_str();
+  size_t length = text.length();
+  size_t position = 0;
+  while (position < length) {
+    UChar32 character;
+    size_t next_position = position;
+    U16_NEXT(string, next_position, length, character);
+
+    // Now that we have the character, we use ICU in order to query for the
+    // appropriate Unicode BiDi character type.
+    int32_t property = u_getIntPropertyValue(character, UCHAR_BIDI_CLASS);
+    if ((property == U_RIGHT_TO_LEFT) || (property == U_RIGHT_TO_LEFT_ARABIC))
+      return true;
+
+    position = next_position;
+  }
+
+  return false;
+}
+
+void WrapStringWithLTRFormatting(string16* text) {
+  if (text->empty())
+    return;
+
+  // Inserting an LRE (Left-To-Right Embedding) mark as the first character.
+  text->insert(static_cast<size_t>(0), static_cast<size_t>(1),
+               kLeftToRightEmbeddingMark);
+
+  // Inserting a PDF (Pop Directional Formatting) mark as the last character.
+  text->push_back(kPopDirectionalFormatting);
+}
+
+void WrapStringWithRTLFormatting(string16* text) {
+  if (text->empty())
+    return;
+
+  // Inserting an RLE (Right-To-Left Embedding) mark as the first character.
+  text->insert(static_cast<size_t>(0), static_cast<size_t>(1),
+               kRightToLeftEmbeddingMark);
+
+  // Inserting a PDF (Pop Directional Formatting) mark as the last character.
+  text->push_back(kPopDirectionalFormatting);
+}
+
+void WrapPathWithLTRFormatting(const FilePath& path,
+                               string16* rtl_safe_path) {
+  // Wrap the overall path with LRE-PDF pair which essentialy marks the
+  // string as a Left-To-Right string.
+  // Inserting an LRE (Left-To-Right Embedding) mark as the first character.
+  rtl_safe_path->push_back(kLeftToRightEmbeddingMark);
+#if defined(OS_MACOSX)
+    rtl_safe_path->append(UTF8ToUTF16(path.value()));
+#elif defined(OS_WIN)
+    rtl_safe_path->append(path.value());
+#else  // defined(OS_POSIX) && !defined(OS_MACOSX)
+    std::wstring wide_path = base::SysNativeMBToWide(path.value());
+    rtl_safe_path->append(WideToUTF16(wide_path));
+#endif
+  // Inserting a PDF (Pop Directional Formatting) mark as the last character.
+  rtl_safe_path->push_back(kPopDirectionalFormatting);
+}
+
+string16 GetDisplayStringInLTRDirectionality(const string16& text) {
+  // Always wrap the string in RTL UI (it may be appended to RTL string).
+  // Also wrap strings with an RTL first strong character direction in LTR UI.
+  if (IsRTL() || GetFirstStrongCharacterDirection(text) == RIGHT_TO_LEFT) {
+    string16 text_mutable(text);
+    WrapStringWithLTRFormatting(&text_mutable);
+    return text_mutable;
+  }
+  return text;
+}
+
+string16 StripWrappingBidiControlCharacters(const string16& text) {
+  if (text.empty())
+    return text;
+  size_t begin_index = 0;
+  char16 begin = text[begin_index];
+  if (begin == kLeftToRightEmbeddingMark ||
+      begin == kRightToLeftEmbeddingMark ||
+      begin == kLeftToRightOverride ||
+      begin == kRightToLeftOverride)
+    ++begin_index;
+  size_t end_index = text.length() - 1;
+  if (text[end_index] == kPopDirectionalFormatting)
+    --end_index;
+  return text.substr(begin_index, end_index - begin_index + 1);
+}
+
+}  // namespace i18n
+}  // namespace base
diff --git a/base/i18n/rtl_unittest.cc b/base/i18n/rtl_unittest.cc
new file mode 100644
index 0000000..fbdd1a1
--- /dev/null
+++ b/base/i18n/rtl_unittest.cc
@@ -0,0 +1,556 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/i18n/rtl.h"
+
+#include <stddef.h>
+
+#include <algorithm>
+
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "base/strings/string_util.h"
+#include "base/strings/sys_string_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/icu_test_util.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/platform_test.h"
+#include "third_party/icu/source/common/unicode/locid.h"
+#include "third_party/icu/source/i18n/unicode/usearch.h"
+
+namespace base {
+namespace i18n {
+
+class RTLTest : public PlatformTest {
+};
+
+TEST_F(RTLTest, GetFirstStrongCharacterDirection) {
+  struct {
+    const wchar_t* text;
+    TextDirection direction;
+  } cases[] = {
+    // Test pure LTR string.
+    { L"foo bar", LEFT_TO_RIGHT },
+    // Test pure RTL string.
+    { L"\x05d0\x05d1\x05d2 \x05d3\x0d4\x05d5", RIGHT_TO_LEFT},
+    // Test bidi string in which the first character with strong directionality
+    // is a character with type L.
+    { L"foo \x05d0 bar", LEFT_TO_RIGHT },
+    // Test bidi string in which the first character with strong directionality
+    // is a character with type R.
+    { L"\x05d0 foo bar", RIGHT_TO_LEFT },
+    // Test bidi string which starts with a character with weak directionality
+    // and in which the first character with strong directionality is a
+    // character with type L.
+    { L"!foo \x05d0 bar", LEFT_TO_RIGHT },
+    // Test bidi string which starts with a character with weak directionality
+    // and in which the first character with strong directionality is a
+    // character with type R.
+    { L",\x05d0 foo bar", RIGHT_TO_LEFT },
+    // Test bidi string in which the first character with strong directionality
+    // is a character with type LRE.
+    { L"\x202a \x05d0 foo  bar", LEFT_TO_RIGHT },
+    // Test bidi string in which the first character with strong directionality
+    // is a character with type LRO.
+    { L"\x202d \x05d0 foo  bar", LEFT_TO_RIGHT },
+    // Test bidi string in which the first character with strong directionality
+    // is a character with type RLE.
+    { L"\x202b foo \x05d0 bar", RIGHT_TO_LEFT },
+    // Test bidi string in which the first character with strong directionality
+    // is a character with type RLO.
+    { L"\x202e foo \x05d0 bar", RIGHT_TO_LEFT },
+    // Test bidi string in which the first character with strong directionality
+    // is a character with type AL.
+    { L"\x0622 foo \x05d0 bar", RIGHT_TO_LEFT },
+    // Test a string without strong directionality characters.
+    { L",!.{}", LEFT_TO_RIGHT },
+    // Test empty string.
+    { L"", LEFT_TO_RIGHT },
+    // Test characters in non-BMP (e.g. Phoenician letters. Please refer to
+    // http://demo.icu-project.org/icu-bin/ubrowse?scr=151&b=10910 for more
+    // information).
+    {
+#if defined(WCHAR_T_IS_UTF32)
+      L" ! \x10910" L"abc 123",
+#elif defined(WCHAR_T_IS_UTF16)
+      L" ! \xd802\xdd10" L"abc 123",
+#else
+#error wchar_t should be either UTF-16 or UTF-32
+#endif
+      RIGHT_TO_LEFT },
+    {
+#if defined(WCHAR_T_IS_UTF32)
+      L" ! \x10401" L"abc 123",
+#elif defined(WCHAR_T_IS_UTF16)
+      L" ! \xd801\xdc01" L"abc 123",
+#else
+#error wchar_t should be either UTF-16 or UTF-32
+#endif
+      LEFT_TO_RIGHT },
+   };
+
+  for (size_t i = 0; i < arraysize(cases); ++i)
+    EXPECT_EQ(cases[i].direction,
+              GetFirstStrongCharacterDirection(WideToUTF16(cases[i].text)));
+}
+
+
+// Note that the cases with LRE, LRO, RLE and RLO are invalid for
+// GetLastStrongCharacterDirection because they should be followed by PDF
+// character.
+TEST_F(RTLTest, GetLastStrongCharacterDirection) {
+  struct {
+    const wchar_t* text;
+    TextDirection direction;
+  } cases[] = {
+    // Test pure LTR string.
+    { L"foo bar", LEFT_TO_RIGHT },
+    // Test pure RTL string.
+    { L"\x05d0\x05d1\x05d2 \x05d3\x0d4\x05d5", RIGHT_TO_LEFT},
+    // Test bidi string in which the last character with strong directionality
+    // is a character with type L.
+    { L"foo \x05d0 bar", LEFT_TO_RIGHT },
+    // Test bidi string in which the last character with strong directionality
+    // is a character with type R.
+    { L"\x05d0 foo bar \x05d3", RIGHT_TO_LEFT },
+    // Test bidi string which ends with a character with weak directionality
+    // and in which the last character with strong directionality is a
+    // character with type L.
+    { L"!foo \x05d0 bar!", LEFT_TO_RIGHT },
+    // Test bidi string which ends with a character with weak directionality
+    // and in which the last character with strong directionality is a
+    // character with type R.
+    { L",\x05d0 foo bar \x05d1,", RIGHT_TO_LEFT },
+    // Test bidi string in which the last character with strong directionality
+    // is a character with type AL.
+    { L"\x0622 foo \x05d0 bar \x0622", RIGHT_TO_LEFT },
+    // Test a string without strong directionality characters.
+    { L",!.{}", LEFT_TO_RIGHT },
+    // Test empty string.
+    { L"", LEFT_TO_RIGHT },
+    // Test characters in non-BMP (e.g. Phoenician letters. Please refer to
+    // http://demo.icu-project.org/icu-bin/ubrowse?scr=151&b=10910 for more
+    // information).
+    {
+#if defined(WCHAR_T_IS_UTF32)
+       L"abc 123" L" ! \x10910 !",
+#elif defined(WCHAR_T_IS_UTF16)
+       L"abc 123" L" ! \xd802\xdd10 !",
+#else
+#error wchar_t should be either UTF-16 or UTF-32
+#endif
+      RIGHT_TO_LEFT },
+    {
+#if defined(WCHAR_T_IS_UTF32)
+       L"abc 123" L" ! \x10401 !",
+#elif defined(WCHAR_T_IS_UTF16)
+       L"abc 123" L" ! \xd801\xdc01 !",
+#else
+#error wchar_t should be either UTF-16 or UTF-32
+#endif
+      LEFT_TO_RIGHT },
+   };
+
+  for (size_t i = 0; i < arraysize(cases); ++i)
+    EXPECT_EQ(cases[i].direction,
+              GetLastStrongCharacterDirection(WideToUTF16(cases[i].text)));
+}
+
+TEST_F(RTLTest, GetStringDirection) {
+  struct {
+    const wchar_t* text;
+    TextDirection direction;
+  } cases[] = {
+    // Test pure LTR string.
+    { L"foobar", LEFT_TO_RIGHT },
+    { L".foobar", LEFT_TO_RIGHT },
+    { L"foo, bar", LEFT_TO_RIGHT },
+    // Test pure LTR with strong directionality characters of type LRE.
+    { L"\x202a\x202a", LEFT_TO_RIGHT },
+    { L".\x202a\x202a", LEFT_TO_RIGHT },
+    { L"\x202a, \x202a", LEFT_TO_RIGHT },
+    // Test pure LTR with strong directionality characters of type LRO.
+    { L"\x202d\x202d", LEFT_TO_RIGHT },
+    { L".\x202d\x202d", LEFT_TO_RIGHT },
+    { L"\x202d, \x202d", LEFT_TO_RIGHT },
+    // Test pure LTR with various types of strong directionality characters.
+    { L"foo \x202a\x202d", LEFT_TO_RIGHT },
+    { L".\x202d foo \x202a", LEFT_TO_RIGHT },
+    { L"\x202a, \x202d foo", LEFT_TO_RIGHT },
+    // Test pure RTL with strong directionality characters of type R.
+    { L"\x05d0\x05d0", RIGHT_TO_LEFT },
+    { L".\x05d0\x05d0", RIGHT_TO_LEFT },
+    { L"\x05d0, \x05d0", RIGHT_TO_LEFT },
+    // Test pure RTL with strong directionality characters of type RLE.
+    { L"\x202b\x202b", RIGHT_TO_LEFT },
+    { L".\x202b\x202b", RIGHT_TO_LEFT },
+    { L"\x202b, \x202b", RIGHT_TO_LEFT },
+    // Test pure RTL with strong directionality characters of type RLO.
+    { L"\x202e\x202e", RIGHT_TO_LEFT },
+    { L".\x202e\x202e", RIGHT_TO_LEFT },
+    { L"\x202e, \x202e", RIGHT_TO_LEFT },
+    // Test pure RTL with strong directionality characters of type AL.
+    { L"\x0622\x0622", RIGHT_TO_LEFT },
+    { L".\x0622\x0622", RIGHT_TO_LEFT },
+    { L"\x0622, \x0622", RIGHT_TO_LEFT },
+    // Test pure RTL with various types of strong directionality characters.
+    { L"\x05d0\x202b\x202e\x0622", RIGHT_TO_LEFT },
+    { L".\x202b\x202e\x0622\x05d0", RIGHT_TO_LEFT },
+    { L"\x0622\x202e, \x202b\x05d0", RIGHT_TO_LEFT },
+    // Test bidi strings.
+    { L"foo \x05d0 bar", UNKNOWN_DIRECTION },
+    { L"\x202b foo bar", UNKNOWN_DIRECTION },
+    { L"!foo \x0622 bar", UNKNOWN_DIRECTION },
+    { L"\x202a\x202b", UNKNOWN_DIRECTION },
+    { L"\x202e\x202d", UNKNOWN_DIRECTION },
+    { L"\x0622\x202a", UNKNOWN_DIRECTION },
+    { L"\x202d\x05d0", UNKNOWN_DIRECTION },
+    // Test a string without strong directionality characters.
+    { L",!.{}", LEFT_TO_RIGHT },
+    // Test empty string.
+    { L"", LEFT_TO_RIGHT },
+    {
+#if defined(WCHAR_T_IS_UTF32)
+      L" ! \x10910" L"abc 123",
+#elif defined(WCHAR_T_IS_UTF16)
+      L" ! \xd802\xdd10" L"abc 123",
+#else
+#error wchar_t should be either UTF-16 or UTF-32
+#endif
+      UNKNOWN_DIRECTION },
+    {
+#if defined(WCHAR_T_IS_UTF32)
+      L" ! \x10401" L"abc 123",
+#elif defined(WCHAR_T_IS_UTF16)
+      L" ! \xd801\xdc01" L"abc 123",
+#else
+#error wchar_t should be either UTF-16 or UTF-32
+#endif
+      LEFT_TO_RIGHT },
+   };
+
+  for (size_t i = 0; i < arraysize(cases); ++i)
+    EXPECT_EQ(cases[i].direction,
+              GetStringDirection(WideToUTF16(cases[i].text)));
+}
+
+TEST_F(RTLTest, WrapPathWithLTRFormatting) {
+  const wchar_t* cases[] = {
+    // Test common path, such as "c:\foo\bar".
+    L"c:/foo/bar",
+    // Test path with file name, such as "c:\foo\bar\test.jpg".
+    L"c:/foo/bar/test.jpg",
+    // Test path ending with punctuation, such as "c:\(foo)\bar.".
+    L"c:/(foo)/bar.",
+    // Test path ending with separator, such as "c:\foo\bar\".
+    L"c:/foo/bar/",
+    // Test path with RTL character.
+    L"c:/\x05d0",
+    // Test path with 2 level RTL directory names.
+    L"c:/\x05d0/\x0622",
+    // Test path with mixed RTL/LTR directory names and ending with punctuation.
+    L"c:/\x05d0/\x0622/(foo)/b.a.r.",
+    // Test path without driver name, such as "/foo/bar/test/jpg".
+    L"/foo/bar/test.jpg",
+    // Test path start with current directory, such as "./foo".
+    L"./foo",
+    // Test path start with parent directory, such as "../foo/bar.jpg".
+    L"../foo/bar.jpg",
+    // Test absolute path, such as "//foo/bar.jpg".
+    L"//foo/bar.jpg",
+    // Test path with mixed RTL/LTR directory names.
+    L"c:/foo/\x05d0/\x0622/\x05d1.jpg",
+    // Test empty path.
+    L""
+  };
+
+  for (size_t i = 0; i < arraysize(cases); ++i) {
+    FilePath path;
+#if defined(OS_WIN)
+    std::wstring win_path(cases[i]);
+    std::replace(win_path.begin(), win_path.end(), '/', '\\');
+    path = FilePath(win_path);
+    std::wstring wrapped_expected =
+        std::wstring(L"\x202a") + win_path + L"\x202c";
+#else
+    path = FilePath(base::SysWideToNativeMB(cases[i]));
+    std::wstring wrapped_expected =
+        std::wstring(L"\x202a") + cases[i] + L"\x202c";
+#endif
+    string16 localized_file_path_string;
+    WrapPathWithLTRFormatting(path, &localized_file_path_string);
+
+    std::wstring wrapped_actual = UTF16ToWide(localized_file_path_string);
+    EXPECT_EQ(wrapped_expected, wrapped_actual);
+  }
+}
+
+TEST_F(RTLTest, WrapString) {
+  const wchar_t* cases[] = {
+    L" . ",
+    L"abc",
+    L"a" L"\x5d0\x5d1",
+    L"a" L"\x5d1" L"b",
+    L"\x5d0\x5d1\x5d2",
+    L"\x5d0\x5d1" L"a",
+    L"\x5d0" L"a" L"\x5d1",
+  };
+
+  const bool was_rtl = IsRTL();
+
+  test::ScopedRestoreICUDefaultLocale restore_locale;
+  for (size_t i = 0; i < 2; ++i) {
+    // Toggle the application default text direction (to try each direction).
+    SetRTLForTesting(!IsRTL());
+
+    string16 empty;
+    WrapStringWithLTRFormatting(&empty);
+    EXPECT_TRUE(empty.empty());
+    WrapStringWithRTLFormatting(&empty);
+    EXPECT_TRUE(empty.empty());
+
+    for (size_t i = 0; i < arraysize(cases); ++i) {
+      string16 input = WideToUTF16(cases[i]);
+      string16 ltr_wrap = input;
+      WrapStringWithLTRFormatting(&ltr_wrap);
+      EXPECT_EQ(ltr_wrap[0], kLeftToRightEmbeddingMark);
+      EXPECT_EQ(ltr_wrap.substr(1, ltr_wrap.length() - 2), input);
+      EXPECT_EQ(ltr_wrap[ltr_wrap.length() -1], kPopDirectionalFormatting);
+
+      string16 rtl_wrap = input;
+      WrapStringWithRTLFormatting(&rtl_wrap);
+      EXPECT_EQ(rtl_wrap[0], kRightToLeftEmbeddingMark);
+      EXPECT_EQ(rtl_wrap.substr(1, rtl_wrap.length() - 2), input);
+      EXPECT_EQ(rtl_wrap[rtl_wrap.length() -1], kPopDirectionalFormatting);
+    }
+  }
+
+  EXPECT_EQ(was_rtl, IsRTL());
+}
+
+TEST_F(RTLTest, GetDisplayStringInLTRDirectionality) {
+  struct {
+    const wchar_t* path;
+    bool wrap_ltr;
+    bool wrap_rtl;
+  } cases[] = {
+    { L"test",                   false, true },
+    { L"test.html",              false, true },
+    { L"\x05d0\x05d1\x05d2",     true,  true },
+    { L"\x05d0\x05d1\x05d2.txt", true,  true },
+    { L"\x05d0" L"abc",          true,  true },
+    { L"\x05d0" L"abc.txt",      true,  true },
+    { L"abc\x05d0\x05d1",        false, true },
+    { L"abc\x05d0\x05d1.jpg",    false, true },
+  };
+
+  const bool was_rtl = IsRTL();
+
+  test::ScopedRestoreICUDefaultLocale restore_locale;
+  for (size_t i = 0; i < 2; ++i) {
+    // Toggle the application default text direction (to try each direction).
+    SetRTLForTesting(!IsRTL());
+    for (size_t i = 0; i < arraysize(cases); ++i) {
+      string16 input = WideToUTF16(cases[i].path);
+      string16 output = GetDisplayStringInLTRDirectionality(input);
+      // Test the expected wrapping behavior for the current UI directionality.
+      if (IsRTL() ? cases[i].wrap_rtl : cases[i].wrap_ltr)
+        EXPECT_NE(output, input);
+      else
+        EXPECT_EQ(output, input);
+    }
+  }
+
+  EXPECT_EQ(was_rtl, IsRTL());
+}
+
+TEST_F(RTLTest, GetTextDirection) {
+  EXPECT_EQ(RIGHT_TO_LEFT, GetTextDirectionForLocale("ar"));
+  EXPECT_EQ(RIGHT_TO_LEFT, GetTextDirectionForLocale("ar_EG"));
+  EXPECT_EQ(RIGHT_TO_LEFT, GetTextDirectionForLocale("he"));
+  EXPECT_EQ(RIGHT_TO_LEFT, GetTextDirectionForLocale("he_IL"));
+  // iw is an obsolete code for Hebrew.
+  EXPECT_EQ(RIGHT_TO_LEFT, GetTextDirectionForLocale("iw"));
+  // Although we're not yet localized to Farsi and Urdu, we
+  // do have the text layout direction information for them.
+  EXPECT_EQ(RIGHT_TO_LEFT, GetTextDirectionForLocale("fa"));
+  EXPECT_EQ(RIGHT_TO_LEFT, GetTextDirectionForLocale("ur"));
+#if 0
+  // Enable these when we include the minimal locale data for Azerbaijani
+  // written in Arabic and Dhivehi. At the moment, our copy of
+  // ICU data does not have entries for them.
+  EXPECT_EQ(RIGHT_TO_LEFT, GetTextDirectionForLocale("az_Arab"));
+  // Dhivehi that uses Thaana script.
+  EXPECT_EQ(RIGHT_TO_LEFT, GetTextDirectionForLocale("dv"));
+#endif
+  EXPECT_EQ(LEFT_TO_RIGHT, GetTextDirectionForLocale("en"));
+  // Chinese in China with '-'.
+  EXPECT_EQ(LEFT_TO_RIGHT, GetTextDirectionForLocale("zh-CN"));
+  // Filipino : 3-letter code
+  EXPECT_EQ(LEFT_TO_RIGHT, GetTextDirectionForLocale("fil"));
+  // Russian
+  EXPECT_EQ(LEFT_TO_RIGHT, GetTextDirectionForLocale("ru"));
+  // Japanese that uses multiple scripts
+  EXPECT_EQ(LEFT_TO_RIGHT, GetTextDirectionForLocale("ja"));
+}
+
+TEST_F(RTLTest, GetTextDirectionForLocaleInStartUp) {
+  EXPECT_EQ(RIGHT_TO_LEFT, GetTextDirectionForLocaleInStartUp("ar"));
+  EXPECT_EQ(RIGHT_TO_LEFT, GetTextDirectionForLocaleInStartUp("ar_EG"));
+  EXPECT_EQ(RIGHT_TO_LEFT, GetTextDirectionForLocaleInStartUp("he"));
+  EXPECT_EQ(RIGHT_TO_LEFT, GetTextDirectionForLocaleInStartUp("he_IL"));
+  // iw is an obsolete code for Hebrew.
+  EXPECT_EQ(RIGHT_TO_LEFT, GetTextDirectionForLocaleInStartUp("iw"));
+  // Although we're not yet localized to Farsi and Urdu, we
+  // do have the text layout direction information for them.
+  EXPECT_EQ(RIGHT_TO_LEFT, GetTextDirectionForLocaleInStartUp("fa"));
+  EXPECT_EQ(RIGHT_TO_LEFT, GetTextDirectionForLocaleInStartUp("ur"));
+  EXPECT_EQ(LEFT_TO_RIGHT, GetTextDirectionForLocaleInStartUp("en"));
+  // Chinese in China with '-'.
+  EXPECT_EQ(LEFT_TO_RIGHT, GetTextDirectionForLocaleInStartUp("zh-CN"));
+  // Filipino : 3-letter code
+  EXPECT_EQ(LEFT_TO_RIGHT, GetTextDirectionForLocaleInStartUp("fil"));
+  // Russian
+  EXPECT_EQ(LEFT_TO_RIGHT, GetTextDirectionForLocaleInStartUp("ru"));
+  // Japanese that uses multiple scripts
+  EXPECT_EQ(LEFT_TO_RIGHT, GetTextDirectionForLocaleInStartUp("ja"));
+}
+
+TEST_F(RTLTest, UnadjustStringForLocaleDirection) {
+  // These test strings are borrowed from WrapPathWithLTRFormatting
+  const wchar_t* cases[] = {
+    L"foo bar",
+    L"foo \x05d0 bar",
+    L"\x05d0 foo bar",
+    L"!foo \x05d0 bar",
+    L",\x05d0 foo bar",
+    L"\x202a \x05d0 foo  bar",
+    L"\x202d \x05d0 foo  bar",
+    L"\x202b foo \x05d0 bar",
+    L"\x202e foo \x05d0 bar",
+    L"\x0622 foo \x05d0 bar",
+  };
+
+  const bool was_rtl = IsRTL();
+
+  test::ScopedRestoreICUDefaultLocale restore_locale;
+  for (size_t i = 0; i < 2; ++i) {
+    // Toggle the application default text direction (to try each direction).
+    SetRTLForTesting(!IsRTL());
+
+    for (size_t i = 0; i < arraysize(cases); ++i) {
+      string16 test_case = WideToUTF16(cases[i]);
+      string16 adjusted_string = test_case;
+
+      if (!AdjustStringForLocaleDirection(&adjusted_string))
+        continue;
+
+      EXPECT_NE(test_case, adjusted_string);
+      EXPECT_TRUE(UnadjustStringForLocaleDirection(&adjusted_string));
+      EXPECT_EQ(test_case, adjusted_string) << " for test case [" << test_case
+                                            << "] with IsRTL() == " << IsRTL();
+    }
+  }
+
+  EXPECT_EQ(was_rtl, IsRTL());
+}
+
+TEST_F(RTLTest, EnsureTerminatedDirectionalFormatting) {
+  struct {
+    const wchar_t* unformated_text;
+    const wchar_t* formatted_text;
+  } cases[] = {
+      // Tests string without any dir-formatting characters.
+      {L"google.com", L"google.com"},
+      // Tests string with properly terminated dir-formatting character.
+      {L"\x202egoogle.com\x202c", L"\x202egoogle.com\x202c"},
+      // Tests string with over-terminated dir-formatting characters.
+      {L"\x202egoogle\x202c.com\x202c", L"\x202egoogle\x202c.com\x202c"},
+      // Tests string beginning with a dir-formatting character.
+      {L"\x202emoc.elgoog", L"\x202emoc.elgoog\x202c"},
+      // Tests string that over-terminates then re-opens.
+      {L"\x202egoogle\x202c\x202c.\x202eom",
+       L"\x202egoogle\x202c\x202c.\x202eom\x202c"},
+      // Tests string containing a dir-formatting character in the middle.
+      {L"google\x202e.com", L"google\x202e.com\x202c"},
+      // Tests string with multiple dir-formatting characters.
+      {L"\x202egoogle\x202e.com/\x202eguest",
+       L"\x202egoogle\x202e.com/\x202eguest\x202c\x202c\x202c"},
+      // Test the other dir-formatting characters (U+202A, U+202B, and U+202D).
+      {L"\x202agoogle.com", L"\x202agoogle.com\x202c"},
+      {L"\x202bgoogle.com", L"\x202bgoogle.com\x202c"},
+      {L"\x202dgoogle.com", L"\x202dgoogle.com\x202c"},
+  };
+
+  const bool was_rtl = IsRTL();
+
+  test::ScopedRestoreICUDefaultLocale restore_locale;
+  for (size_t i = 0; i < 2; ++i) {
+    // Toggle the application default text direction (to try each direction).
+    SetRTLForTesting(!IsRTL());
+    for (size_t i = 0; i < arraysize(cases); ++i) {
+      string16 unsanitized_text = WideToUTF16(cases[i].unformated_text);
+      string16 sanitized_text = WideToUTF16(cases[i].formatted_text);
+      EnsureTerminatedDirectionalFormatting(&unsanitized_text);
+      EXPECT_EQ(sanitized_text, unsanitized_text);
+    }
+  }
+  EXPECT_EQ(was_rtl, IsRTL());
+}
+
+TEST_F(RTLTest, SanitizeUserSuppliedString) {
+  struct {
+    const wchar_t* unformatted_text;
+    const wchar_t* formatted_text;
+  } cases[] = {
+      // Tests RTL string with properly terminated dir-formatting character.
+      {L"\x202eكبير Google التطبيق\x202c", L"\x202eكبير Google التطبيق\x202c"},
+      // Tests RTL string with over-terminated dir-formatting characters.
+      {L"\x202eكبير Google\x202cالتطبيق\x202c",
+       L"\x202eكبير Google\x202cالتطبيق\x202c"},
+      // Tests RTL string that over-terminates then re-opens.
+      {L"\x202eكبير Google\x202c\x202cالتطبيق\x202e",
+       L"\x202eكبير Google\x202c\x202cالتطبيق\x202e\x202c"},
+      // Tests RTL string with multiple dir-formatting characters.
+      {L"\x202eك\x202eبير Google الت\x202eطبيق",
+       L"\x202eك\x202eبير Google الت\x202eطبيق\x202c\x202c\x202c"},
+      // Test the other dir-formatting characters (U+202A, U+202B, and U+202D).
+      {L"\x202aكبير Google التطبيق", L"\x202aكبير Google التطبيق\x202c"},
+      {L"\x202bكبير Google التطبيق", L"\x202bكبير Google التطبيق\x202c"},
+      {L"\x202dكبير Google التطبيق", L"\x202dكبير Google التطبيق\x202c"},
+
+  };
+
+  for (size_t i = 0; i < arraysize(cases); ++i) {
+    // On Windows for an LTR locale, no changes to the string are made.
+    string16 prefix, suffix = WideToUTF16(L"");
+#if !defined(OS_WIN)
+    prefix = WideToUTF16(L"\x200e\x202b");
+    suffix = WideToUTF16(L"\x202c\x200e");
+#endif  // !OS_WIN
+    string16 unsanitized_text = WideToUTF16(cases[i].unformatted_text);
+    string16 sanitized_text =
+        prefix + WideToUTF16(cases[i].formatted_text) + suffix;
+    SanitizeUserSuppliedString(&unsanitized_text);
+    EXPECT_EQ(sanitized_text, unsanitized_text);
+  }
+}
+
+class SetICULocaleTest : public PlatformTest {};
+
+TEST_F(SetICULocaleTest, OverlongLocaleId) {
+  test::ScopedRestoreICUDefaultLocale restore_locale;
+  std::string id("fr-ca-x-foo");
+  while (id.length() < 152)
+    id.append("-x-foo");
+  SetICUDefaultLocale(id);
+  EXPECT_STRNE("en_US", icu::Locale::getDefault().getName());
+  id.append("zzz");
+  SetICUDefaultLocale(id);
+  EXPECT_STREQ("en_US", icu::Locale::getDefault().getName());
+}
+
+}  // namespace i18n
+}  // namespace base
diff --git a/base/i18n/streaming_utf8_validator.cc b/base/i18n/streaming_utf8_validator.cc
new file mode 100644
index 0000000..19c86a3
--- /dev/null
+++ b/base/i18n/streaming_utf8_validator.cc
@@ -0,0 +1,59 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This implementation doesn't use ICU. The ICU macros are oriented towards
+// character-at-a-time processing, whereas byte-at-a-time processing is easier
+// with streaming input.
+
+#include "base/i18n/streaming_utf8_validator.h"
+
+#include "base/i18n/utf8_validator_tables.h"
+#include "base/logging.h"
+
+namespace base {
+namespace {
+
+uint8_t StateTableLookup(uint8_t offset) {
+  DCHECK_LT(offset, internal::kUtf8ValidatorTablesSize);
+  return internal::kUtf8ValidatorTables[offset];
+}
+
+}  // namespace
+
+StreamingUtf8Validator::State StreamingUtf8Validator::AddBytes(const char* data,
+                                                               size_t size) {
+  // Copy |state_| into a local variable so that the compiler doesn't have to be
+  // careful of aliasing.
+  uint8_t state = state_;
+  for (const char* p = data; p != data + size; ++p) {
+    if ((*p & 0x80) == 0) {
+      if (state == 0)
+        continue;
+      state = internal::I18N_UTF8_VALIDATOR_INVALID_INDEX;
+      break;
+    }
+    const uint8_t shift_amount = StateTableLookup(state);
+    const uint8_t shifted_char = (*p & 0x7F) >> shift_amount;
+    state = StateTableLookup(state + shifted_char + 1);
+    // State may be INVALID here, but this code is optimised for the case of
+    // valid UTF-8 and it is more efficient (by about 2%) to not attempt an
+    // early loop exit unless we hit an ASCII character.
+  }
+  state_ = state;
+  return state == 0 ? VALID_ENDPOINT
+      : state == internal::I18N_UTF8_VALIDATOR_INVALID_INDEX
+      ? INVALID
+      : VALID_MIDPOINT;
+}
+
+void StreamingUtf8Validator::Reset() {
+  state_ = 0u;
+}
+
+bool StreamingUtf8Validator::Validate(const std::string& string) {
+  return StreamingUtf8Validator().AddBytes(string.data(), string.size()) ==
+         VALID_ENDPOINT;
+}
+
+}  // namespace base
diff --git a/base/i18n/streaming_utf8_validator.h b/base/i18n/streaming_utf8_validator.h
new file mode 100644
index 0000000..ebf38a6
--- /dev/null
+++ b/base/i18n/streaming_utf8_validator.h
@@ -0,0 +1,66 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// A streaming validator for UTF-8. Validation is based on the definition in
+// RFC-3629. In particular, it does not reject the invalid characters rejected
+// by base::IsStringUTF8().
+//
+// The implementation detects errors on the first possible byte.
+
+#ifndef BASE_I18N_STREAMING_UTF8_VALIDATOR_H_
+#define BASE_I18N_STREAMING_UTF8_VALIDATOR_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <string>
+
+#include "base/i18n/base_i18n_export.h"
+#include "base/macros.h"
+
+namespace base {
+
+class BASE_I18N_EXPORT StreamingUtf8Validator {
+ public:
+  // The validator exposes 3 states. It starts in state VALID_ENDPOINT. As it
+  // processes characters it alternates between VALID_ENDPOINT and
+  // VALID_MIDPOINT. If it encounters an invalid byte or UTF-8 sequence the
+  // state changes permanently to INVALID.
+  enum State {
+    VALID_ENDPOINT,
+    VALID_MIDPOINT,
+    INVALID
+  };
+
+  StreamingUtf8Validator() : state_(0u) {}
+  // Trivial destructor intentionally omitted.
+
+  // Validate |size| bytes starting at |data|. If the concatenation of all calls
+  // to AddBytes() since this object was constructed or reset is a valid UTF-8
+  // string, returns VALID_ENDPOINT. If it could be the prefix of a valid UTF-8
+  // string, returns VALID_MIDPOINT. If an invalid byte or UTF-8 sequence was
+  // present, returns INVALID.
+  State AddBytes(const char* data, size_t size);
+
+  // Return the object to a freshly-constructed state so that it can be re-used.
+  void Reset();
+
+  // Validate a complete string using the same criteria. Returns true if the
+  // string only contains complete, valid UTF-8 codepoints.
+  static bool Validate(const std::string& string);
+
+ private:
+  // The current state of the validator. Value 0 is the initial/valid state.
+  // The state is stored as an offset into |kUtf8ValidatorTables|. The special
+  // state |kUtf8InvalidState| is invalid.
+  uint8_t state_;
+
+  // This type could be made copyable but there is currently no use-case for
+  // it.
+  DISALLOW_COPY_AND_ASSIGN(StreamingUtf8Validator);
+};
+
+}  // namespace base
+
+#endif  // BASE_I18N_STREAMING_UTF8_VALIDATOR_H_
diff --git a/base/i18n/streaming_utf8_validator_perftest.cc b/base/i18n/streaming_utf8_validator_perftest.cc
new file mode 100644
index 0000000..ad328f8
--- /dev/null
+++ b/base/i18n/streaming_utf8_validator_perftest.cc
@@ -0,0 +1,240 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// All data that is passed through a WebSocket with type "Text" needs to be
+// validated as UTF8. Since this is done on the IO thread, it needs to be
+// reasonably fast.
+
+// We are only interested in the performance on valid UTF8. Invalid UTF8 will
+// result in a connection failure, so is unlikely to become a source of
+// performance issues.
+
+#include "base/i18n/streaming_utf8_validator.h"
+
+#include <stddef.h>
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/test/perf_time_logger.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace {
+
+// We want to test ranges of valid UTF-8 sequences. These ranges are inclusive.
+// They are intended to be large enough that the validator needs to do
+// meaningful work while being in some sense "realistic" (eg. control characters
+// are not included).
+const char kOneByteSeqRangeStart[] = " ";  // U+0020
+const char kOneByteSeqRangeEnd[] = "~";    // U+007E
+
+const char kTwoByteSeqRangeStart[] = "\xc2\xa0";  // U+00A0 non-breaking space
+const char kTwoByteSeqRangeEnd[] = "\xc9\x8f";    // U+024F small y with stroke
+
+const char kThreeByteSeqRangeStart[] = "\xe3\x81\x82";  // U+3042 Hiragana "a"
+const char kThreeByteSeqRangeEnd[] = "\xe9\xbf\x83";    // U+9FC3 "to blink"
+
+const char kFourByteSeqRangeStart[] = "\xf0\xa0\x80\x8b";  // U+2000B
+const char kFourByteSeqRangeEnd[] = "\xf0\xaa\x9a\xb2";    // U+2A6B2
+
+// The different lengths of strings to test.
+const size_t kTestLengths[] = {1, 32, 256, 32768, 1 << 20};
+
+// Simplest possible byte-at-a-time validator, to provide a baseline
+// for comparison. This is only tried on 1-byte UTF-8 sequences, as
+// the results will not be meaningful with sequences containing
+// top-bit-set bytes.
+bool IsString7Bit(const std::string& s) {
+  for (std::string::const_iterator it = s.begin(); it != s.end(); ++it) {
+    if (*it & 0x80)
+      return false;
+  }
+  return true;
+}
+
+// Assumes that |previous| is a valid UTF-8 sequence, and attempts to return
+// the next one. Is just barely smart enough to iterate through the ranges
+// defined about.
+std::string NextUtf8Sequence(const std::string& previous) {
+  DCHECK(StreamingUtf8Validator::Validate(previous));
+  std::string next = previous;
+  for (int i = static_cast<int>(previous.length() - 1); i >= 0; --i) {
+    // All bytes in a UTF-8 sequence except the first one are
+    // constrained to the range 0x80 to 0xbf, inclusive. When we
+    // increment past 0xbf, we carry into the previous byte.
+    if (i > 0 && next[i] == '\xbf') {
+      next[i] = '\x80';
+      continue;  // carry
+    }
+    ++next[i];
+    break;  // no carry
+  }
+  DCHECK(StreamingUtf8Validator::Validate(next))
+      << "Result \"" << next << "\" failed validation";
+  return next;
+}
+
+typedef bool (*TestTargetType)(const std::string&);
+
+// Run fuction |target| over |test_string| |times| times, and report the results
+// using |description|.
+bool RunTest(const std::string& description,
+             TestTargetType target,
+             const std::string& test_string,
+             int times) {
+  base::PerfTimeLogger timer(description.c_str());
+  bool result = true;
+  for (int i = 0; i < times; ++i) {
+    result = target(test_string) && result;
+  }
+  timer.Done();
+  return result;
+}
+
+// Construct a string by repeating |input| enough times to equal or exceed
+// |length|.
+std::string ConstructRepeatedTestString(const std::string& input,
+                                        size_t length) {
+  std::string output = input;
+  while (output.length() * 2 < length) {
+    output += output;
+  }
+  if (output.length() < length) {
+    output += ConstructRepeatedTestString(input, length - output.length());
+  }
+  return output;
+}
+
+// Construct a string by expanding the range of UTF-8 sequences
+// between |input_start| and |input_end|, inclusive, and then
+// repeating the resulting string until it equals or exceeds |length|
+// bytes. |input_start| and |input_end| must be valid UTF-8
+// sequences.
+std::string ConstructRangedTestString(const std::string& input_start,
+                                      const std::string& input_end,
+                                      size_t length) {
+  std::string output = input_start;
+  std::string input = input_start;
+  while (output.length() < length && input != input_end) {
+    input = NextUtf8Sequence(input);
+    output += input;
+  }
+  if (output.length() < length) {
+    output = ConstructRepeatedTestString(output, length);
+  }
+  return output;
+}
+
+struct TestFunctionDescription {
+  TestTargetType function;
+  const char* function_name;
+};
+
+bool IsStringUTF8(const std::string& str) {
+  return base::IsStringUTF8(base::StringPiece(str));
+}
+
+// IsString7Bit is intentionally placed last so it can be excluded easily.
+const TestFunctionDescription kTestFunctions[] = {
+    {&StreamingUtf8Validator::Validate, "StreamingUtf8Validator"},
+    {&IsStringUTF8, "IsStringUTF8"}, {&IsString7Bit, "IsString7Bit"}};
+
+// Construct a test string from |construct_test_string| for each of the lengths
+// in |kTestLengths| in turn. For each string, run each test in |test_functions|
+// for a number of iterations such that the total number of bytes validated
+// is around 16MB.
+void RunSomeTests(
+    const char format[],
+    base::Callback<std::string(size_t length)> construct_test_string,
+    const TestFunctionDescription* test_functions,
+    size_t test_count) {
+  for (size_t i = 0; i < arraysize(kTestLengths); ++i) {
+    const size_t length = kTestLengths[i];
+    const std::string test_string = construct_test_string.Run(length);
+    const int real_length = static_cast<int>(test_string.length());
+    const int times = (1 << 24) / real_length;
+    for (size_t test_index = 0; test_index < test_count; ++test_index) {
+      EXPECT_TRUE(RunTest(StringPrintf(format,
+                                       test_functions[test_index].function_name,
+                                       real_length,
+                                       times),
+                          test_functions[test_index].function,
+                          test_string,
+                          times));
+    }
+  }
+}
+
+TEST(StreamingUtf8ValidatorPerfTest, OneByteRepeated) {
+  RunSomeTests("%s: bytes=1 repeated length=%d repeat=%d",
+               base::Bind(ConstructRepeatedTestString, kOneByteSeqRangeStart),
+               kTestFunctions,
+               3);
+}
+
+TEST(StreamingUtf8ValidatorPerfTest, OneByteRange) {
+  RunSomeTests("%s: bytes=1 ranged length=%d repeat=%d",
+               base::Bind(ConstructRangedTestString,
+                          kOneByteSeqRangeStart,
+                          kOneByteSeqRangeEnd),
+               kTestFunctions,
+               3);
+}
+
+TEST(StreamingUtf8ValidatorPerfTest, TwoByteRepeated) {
+  RunSomeTests("%s: bytes=2 repeated length=%d repeat=%d",
+               base::Bind(ConstructRepeatedTestString, kTwoByteSeqRangeStart),
+               kTestFunctions,
+               2);
+}
+
+TEST(StreamingUtf8ValidatorPerfTest, TwoByteRange) {
+  RunSomeTests("%s: bytes=2 ranged length=%d repeat=%d",
+               base::Bind(ConstructRangedTestString,
+                          kTwoByteSeqRangeStart,
+                          kTwoByteSeqRangeEnd),
+               kTestFunctions,
+               2);
+}
+
+TEST(StreamingUtf8ValidatorPerfTest, ThreeByteRepeated) {
+  RunSomeTests(
+      "%s: bytes=3 repeated length=%d repeat=%d",
+      base::Bind(ConstructRepeatedTestString, kThreeByteSeqRangeStart),
+      kTestFunctions,
+      2);
+}
+
+TEST(StreamingUtf8ValidatorPerfTest, ThreeByteRange) {
+  RunSomeTests("%s: bytes=3 ranged length=%d repeat=%d",
+               base::Bind(ConstructRangedTestString,
+                          kThreeByteSeqRangeStart,
+                          kThreeByteSeqRangeEnd),
+               kTestFunctions,
+               2);
+}
+
+TEST(StreamingUtf8ValidatorPerfTest, FourByteRepeated) {
+  RunSomeTests("%s: bytes=4 repeated length=%d repeat=%d",
+               base::Bind(ConstructRepeatedTestString, kFourByteSeqRangeStart),
+               kTestFunctions,
+               2);
+}
+
+TEST(StreamingUtf8ValidatorPerfTest, FourByteRange) {
+  RunSomeTests("%s: bytes=4 ranged length=%d repeat=%d",
+               base::Bind(ConstructRangedTestString,
+                          kFourByteSeqRangeStart,
+                          kFourByteSeqRangeEnd),
+               kTestFunctions,
+               2);
+}
+
+}  // namespace
+}  // namespace base
diff --git a/base/i18n/streaming_utf8_validator_unittest.cc b/base/i18n/streaming_utf8_validator_unittest.cc
new file mode 100644
index 0000000..f9772d0
--- /dev/null
+++ b/base/i18n/streaming_utf8_validator_unittest.cc
@@ -0,0 +1,412 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/i18n/streaming_utf8_validator.h"
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <string>
+
+#include "base/macros.h"
+#include "base/strings/string_piece.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// Define BASE_I18N_UTF8_VALIDATOR_THOROUGH_TEST to verify that this class
+// accepts exactly the same set of 4-byte strings as ICU-based validation. This
+// tests every possible 4-byte string, so it is too slow to run routinely on
+// low-powered machines.
+//
+// #define BASE_I18N_UTF8_VALIDATOR_THOROUGH_TEST
+
+#ifdef BASE_I18N_UTF8_VALIDATOR_THOROUGH_TEST
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversion_utils.h"
+#include "base/synchronization/lock.h"
+#include "base/task_scheduler/post_task.h"
+#include "base/task_scheduler/task_scheduler.h"
+#include "third_party/icu/source/common/unicode/utf8.h"
+
+#endif  // BASE_I18N_UTF8_VALIDATOR_THOROUGH_TEST
+
+namespace base {
+namespace {
+
+// Avoid having to qualify the enum values in the tests.
+const StreamingUtf8Validator::State VALID_ENDPOINT =
+    StreamingUtf8Validator::VALID_ENDPOINT;
+const StreamingUtf8Validator::State VALID_MIDPOINT =
+    StreamingUtf8Validator::VALID_MIDPOINT;
+const StreamingUtf8Validator::State INVALID = StreamingUtf8Validator::INVALID;
+
+#ifdef BASE_I18N_UTF8_VALIDATOR_THOROUGH_TEST
+
+const uint32_t kThoroughTestChunkSize = 1 << 24;
+
+class StreamingUtf8ValidatorThoroughTest : public ::testing::Test {
+ protected:
+  StreamingUtf8ValidatorThoroughTest()
+      : tasks_dispatched_(0), tasks_finished_(0) {}
+
+  // This uses the same logic as base::IsStringUTF8 except it considers
+  // non-characters valid (and doesn't require a string as input).
+  static bool IsStringUtf8(const char* src, int32_t src_len) {
+    int32_t char_index = 0;
+
+    while (char_index < src_len) {
+      int32_t code_point;
+      U8_NEXT(src, char_index, src_len, code_point);
+      if (!base::IsValidCodepoint(code_point))
+        return false;
+    }
+    return true;
+  }
+
+  // Converts the passed-in integer to a 4 byte string and then
+  // verifies that IsStringUtf8 and StreamingUtf8Validator agree on
+  // whether it is valid UTF-8 or not.
+  void TestNumber(uint32_t n) const {
+    char test[sizeof n];
+    memcpy(test, &n, sizeof n);
+    StreamingUtf8Validator validator;
+    EXPECT_EQ(IsStringUtf8(test, sizeof n),
+              validator.AddBytes(test, sizeof n) == VALID_ENDPOINT)
+        << "Difference of opinion for \""
+        << base::StringPrintf("\\x%02X\\x%02X\\x%02X\\x%02X",
+                              test[0] & 0xFF,
+                              test[1] & 0xFF,
+                              test[2] & 0xFF,
+                              test[3] & 0xFF) << "\"";
+  }
+
+ public:
+  // Tests the 4-byte sequences corresponding to the |size| integers
+  // starting at |begin|. This is intended to be run from a worker
+  // pool. Signals |all_done_| at the end if it thinks all tasks are
+  // finished.
+  void TestRange(uint32_t begin, uint32_t size) {
+    for (uint32_t i = 0; i < size; ++i) {
+      TestNumber(begin + i);
+    }
+    base::AutoLock al(lock_);
+    ++tasks_finished_;
+    LOG(INFO) << tasks_finished_ << " / " << tasks_dispatched_
+              << " tasks done\n";
+  }
+
+ protected:
+  base::Lock lock_;
+  int tasks_dispatched_;
+  int tasks_finished_;
+};
+
+TEST_F(StreamingUtf8ValidatorThoroughTest, TestEverything) {
+  base::TaskScheduler::CreateAndStartWithDefaultParams(
+      "StreamingUtf8ValidatorThoroughTest");
+  {
+    base::AutoLock al(lock_);
+    uint32_t begin = 0;
+    do {
+      base::PostTaskWithTraits(
+          FROM_HERE, {base::TaskShutdownBehavior::BLOCK_SHUTDOWN},
+          base::BindOnce(&StreamingUtf8ValidatorThoroughTest::TestRange,
+                         base::Unretained(this), begin,
+                         kThoroughTestChunkSize));
+      ++tasks_dispatched_;
+      begin += kThoroughTestChunkSize;
+    } while (begin != 0);
+  }
+  base::TaskScheduler::GetInstance()->Shutdown();
+  base::TaskScheduler::GetInstance()->JoinForTesting();
+  base::TaskScheduler::SetInstance(nullptr);
+}
+
+#endif  // BASE_I18N_UTF8_VALIDATOR_THOROUGH_TEST
+
+// These valid and invalid UTF-8 sequences are based on the tests from
+// base/strings/string_util_unittest.cc
+
+// All of the strings in |valid| must represent a single codepoint, because
+// partial sequences are constructed by taking non-empty prefixes of these
+// strings.
+const char* const valid[] = {"\r",           "\n",           "a",
+                             "\xc2\x81",     "\xe1\x80\xbf", "\xf1\x80\xa0\xbf",
+                             "\xef\xbb\xbf",  // UTF-8 BOM
+};
+
+const char* const* const valid_end = valid + arraysize(valid);
+
+const char* const invalid[] = {
+    // always invalid bytes
+    "\xc0", "\xc1",
+    "\xf5", "\xf6", "\xf7",
+    "\xf8", "\xf9", "\xfa", "\xfb", "\xfc", "\xfd", "\xfe", "\xff",
+    // surrogate code points
+    "\xed\xa0\x80", "\xed\x0a\x8f", "\xed\xbf\xbf",
+    //
+    // overlong sequences
+    "\xc0\x80",              // U+0000
+    "\xc1\x80",              // "A"
+    "\xc1\x81",              // "B"
+    "\xe0\x80\x80",          // U+0000
+    "\xe0\x82\x80",          // U+0080
+    "\xe0\x9f\xbf",          // U+07ff
+    "\xf0\x80\x80\x8D",      // U+000D
+    "\xf0\x80\x82\x91",      // U+0091
+    "\xf0\x80\xa0\x80",      // U+0800
+    "\xf0\x8f\xbb\xbf",      // U+FEFF (BOM)
+    "\xf8\x80\x80\x80\xbf",  // U+003F
+    "\xfc\x80\x80\x80\xa0\xa5",
+    //
+    // Beyond U+10FFFF
+    "\xf4\x90\x80\x80",          // U+110000
+    "\xf8\xa0\xbf\x80\xbf",      // 5 bytes
+    "\xfc\x9c\xbf\x80\xbf\x80",  // 6 bytes
+    //
+    // BOMs in UTF-16(BE|LE)
+    "\xfe\xff", "\xff\xfe",
+};
+
+const char* const* const invalid_end = invalid + arraysize(invalid);
+
+// A ForwardIterator which returns all the non-empty prefixes of the elements of
+// "valid".
+class PartialIterator {
+ public:
+  // The constructor returns the first iterator, ie. it is equivalent to
+  // begin().
+  PartialIterator() : index_(0), prefix_length_(0) { Advance(); }
+  // The trivial destructor left intentionally undefined.
+  // This is a value type; the default copy constructor and assignment operator
+  // generated by the compiler are used.
+
+  static PartialIterator end() { return PartialIterator(arraysize(valid), 1); }
+
+  PartialIterator& operator++() {
+    Advance();
+    return *this;
+  }
+
+  base::StringPiece operator*() const {
+    return base::StringPiece(valid[index_], prefix_length_);
+  }
+
+  bool operator==(const PartialIterator& rhs) const {
+    return index_ == rhs.index_ && prefix_length_ == rhs.prefix_length_;
+  }
+
+  bool operator!=(const PartialIterator& rhs) const { return !(rhs == *this); }
+
+ private:
+  // This constructor is used by the end() method.
+  PartialIterator(size_t index, size_t prefix_length)
+      : index_(index), prefix_length_(prefix_length) {}
+
+  void Advance() {
+    if (index_ < arraysize(valid) && prefix_length_ < strlen(valid[index_]))
+      ++prefix_length_;
+    while (index_ < arraysize(valid) &&
+           prefix_length_ == strlen(valid[index_])) {
+      ++index_;
+      prefix_length_ = 1;
+    }
+  }
+
+  // The UTF-8 sequence, as an offset into the |valid| array.
+  size_t index_;
+  size_t prefix_length_;
+};
+
+// A test fixture for tests which test one UTF-8 sequence (or invalid
+// byte sequence) at a time.
+class StreamingUtf8ValidatorSingleSequenceTest : public ::testing::Test {
+ protected:
+  // Iterator must be convertible when de-referenced to StringPiece.
+  template <typename Iterator>
+  void CheckRange(Iterator begin,
+                  Iterator end,
+                  StreamingUtf8Validator::State expected) {
+    for (Iterator it = begin; it != end; ++it) {
+      StreamingUtf8Validator validator;
+      base::StringPiece sequence = *it;
+      EXPECT_EQ(expected,
+                validator.AddBytes(sequence.data(), sequence.size()))
+          << "Failed for \"" << sequence << "\"";
+    }
+  }
+
+  // Adding input a byte at a time should make absolutely no difference.
+  template <typename Iterator>
+  void CheckRangeByteAtATime(Iterator begin,
+                             Iterator end,
+                             StreamingUtf8Validator::State expected) {
+    for (Iterator it = begin; it != end; ++it) {
+      StreamingUtf8Validator validator;
+      base::StringPiece sequence = *it;
+      StreamingUtf8Validator::State state = VALID_ENDPOINT;
+      for (base::StringPiece::const_iterator cit = sequence.begin();
+           cit != sequence.end();
+           ++cit) {
+        state = validator.AddBytes(&*cit, 1);
+      }
+      EXPECT_EQ(expected, state) << "Failed for \"" << sequence << "\"";
+    }
+  }
+};
+
+// A test fixture for tests which test the concatenation of byte sequences.
+class StreamingUtf8ValidatorDoubleSequenceTest : public ::testing::Test {
+ protected:
+  // Check every possible concatenation of byte sequences from two
+  // ranges, and verify that the combination matches the expected
+  // state.
+  template <typename Iterator1, typename Iterator2>
+  void CheckCombinations(Iterator1 begin1,
+                         Iterator1 end1,
+                         Iterator2 begin2,
+                         Iterator2 end2,
+                         StreamingUtf8Validator::State expected) {
+    StreamingUtf8Validator validator;
+    for (Iterator1 it1 = begin1; it1 != end1; ++it1) {
+      base::StringPiece c1 = *it1;
+      for (Iterator2 it2 = begin2; it2 != end2; ++it2) {
+        base::StringPiece c2 = *it2;
+        validator.AddBytes(c1.data(), c1.size());
+        EXPECT_EQ(expected, validator.AddBytes(c2.data(), c2.size()))
+            << "Failed for \"" << c1 << c2 << "\"";
+        validator.Reset();
+      }
+    }
+  }
+};
+
+TEST(StreamingUtf8ValidatorTest, NothingIsValid) {
+  static const char kNothing[] = "";
+  EXPECT_EQ(VALID_ENDPOINT, StreamingUtf8Validator().AddBytes(kNothing, 0));
+}
+
+// Because the members of the |valid| array need to be non-zero length
+// sequences and are measured with strlen(), |valid| cannot be used it
+// to test the NUL character '\0', so the NUL character gets its own
+// test.
+TEST(StreamingUtf8ValidatorTest, NulIsValid) {
+  static const char kNul[] = "\x00";
+  EXPECT_EQ(VALID_ENDPOINT, StreamingUtf8Validator().AddBytes(kNul, 1));
+}
+
+// Just a basic sanity test before we start getting fancy.
+TEST(StreamingUtf8ValidatorTest, HelloWorld) {
+  static const char kHelloWorld[] = "Hello, World!";
+  EXPECT_EQ(
+      VALID_ENDPOINT,
+      StreamingUtf8Validator().AddBytes(kHelloWorld, strlen(kHelloWorld)));
+}
+
+// Check that the Reset() method works.
+TEST(StreamingUtf8ValidatorTest, ResetWorks) {
+  StreamingUtf8Validator validator;
+  EXPECT_EQ(INVALID, validator.AddBytes("\xC0", 1));
+  EXPECT_EQ(INVALID, validator.AddBytes("a", 1));
+  validator.Reset();
+  EXPECT_EQ(VALID_ENDPOINT, validator.AddBytes("a", 1));
+}
+
+TEST_F(StreamingUtf8ValidatorSingleSequenceTest, Valid) {
+  CheckRange(valid, valid_end, VALID_ENDPOINT);
+}
+
+TEST_F(StreamingUtf8ValidatorSingleSequenceTest, Partial) {
+  CheckRange(PartialIterator(), PartialIterator::end(), VALID_MIDPOINT);
+}
+
+TEST_F(StreamingUtf8ValidatorSingleSequenceTest, Invalid) {
+  CheckRange(invalid, invalid_end, INVALID);
+}
+
+TEST_F(StreamingUtf8ValidatorSingleSequenceTest, ValidByByte) {
+  CheckRangeByteAtATime(valid, valid_end, VALID_ENDPOINT);
+}
+
+TEST_F(StreamingUtf8ValidatorSingleSequenceTest, PartialByByte) {
+  CheckRangeByteAtATime(
+      PartialIterator(), PartialIterator::end(), VALID_MIDPOINT);
+}
+
+TEST_F(StreamingUtf8ValidatorSingleSequenceTest, InvalidByByte) {
+  CheckRangeByteAtATime(invalid, invalid_end, INVALID);
+}
+
+TEST_F(StreamingUtf8ValidatorDoubleSequenceTest, ValidPlusValidIsValid) {
+  CheckCombinations(valid, valid_end, valid, valid_end, VALID_ENDPOINT);
+}
+
+TEST_F(StreamingUtf8ValidatorDoubleSequenceTest, ValidPlusPartialIsPartial) {
+  CheckCombinations(valid,
+                    valid_end,
+                    PartialIterator(),
+                    PartialIterator::end(),
+                    VALID_MIDPOINT);
+}
+
+TEST_F(StreamingUtf8ValidatorDoubleSequenceTest, PartialPlusValidIsInvalid) {
+  CheckCombinations(
+      PartialIterator(), PartialIterator::end(), valid, valid_end, INVALID);
+}
+
+TEST_F(StreamingUtf8ValidatorDoubleSequenceTest, PartialPlusPartialIsInvalid) {
+  CheckCombinations(PartialIterator(),
+                    PartialIterator::end(),
+                    PartialIterator(),
+                    PartialIterator::end(),
+                    INVALID);
+}
+
+TEST_F(StreamingUtf8ValidatorDoubleSequenceTest, ValidPlusInvalidIsInvalid) {
+  CheckCombinations(valid, valid_end, invalid, invalid_end, INVALID);
+}
+
+TEST_F(StreamingUtf8ValidatorDoubleSequenceTest, InvalidPlusValidIsInvalid) {
+  CheckCombinations(invalid, invalid_end, valid, valid_end, INVALID);
+}
+
+TEST_F(StreamingUtf8ValidatorDoubleSequenceTest, InvalidPlusInvalidIsInvalid) {
+  CheckCombinations(invalid, invalid_end, invalid, invalid_end, INVALID);
+}
+
+TEST_F(StreamingUtf8ValidatorDoubleSequenceTest, InvalidPlusPartialIsInvalid) {
+  CheckCombinations(
+      invalid, invalid_end, PartialIterator(), PartialIterator::end(), INVALID);
+}
+
+TEST_F(StreamingUtf8ValidatorDoubleSequenceTest, PartialPlusInvalidIsInvalid) {
+  CheckCombinations(
+      PartialIterator(), PartialIterator::end(), invalid, invalid_end, INVALID);
+}
+
+TEST(StreamingUtf8ValidatorValidateTest, EmptyIsValid) {
+  EXPECT_TRUE(StreamingUtf8Validator::Validate(std::string()));
+}
+
+TEST(StreamingUtf8ValidatorValidateTest, SimpleValidCase) {
+  EXPECT_TRUE(StreamingUtf8Validator::Validate("\xc2\x81"));
+}
+
+TEST(StreamingUtf8ValidatorValidateTest, SimpleInvalidCase) {
+  EXPECT_FALSE(StreamingUtf8Validator::Validate("\xc0\x80"));
+}
+
+TEST(StreamingUtf8ValidatorValidateTest, TruncatedIsInvalid) {
+  EXPECT_FALSE(StreamingUtf8Validator::Validate("\xc2"));
+}
+
+}  // namespace
+}  // namespace base
diff --git a/base/i18n/string_compare.cc b/base/i18n/string_compare.cc
new file mode 100644
index 0000000..649c281
--- /dev/null
+++ b/base/i18n/string_compare.cc
@@ -0,0 +1,29 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/i18n/string_compare.h"
+
+#include "base/logging.h"
+#include "base/strings/utf_string_conversions.h"
+#include "third_party/icu/source/common/unicode/unistr.h"
+
+namespace base {
+namespace i18n {
+
+// Compares the character data stored in two different string16 strings by
+// specified Collator instance.
+UCollationResult CompareString16WithCollator(const icu::Collator& collator,
+                                             const string16& lhs,
+                                             const string16& rhs) {
+  UErrorCode error = U_ZERO_ERROR;
+  UCollationResult result = collator.compare(
+      icu::UnicodeString(FALSE, lhs.c_str(), static_cast<int>(lhs.length())),
+      icu::UnicodeString(FALSE, rhs.c_str(), static_cast<int>(rhs.length())),
+      error);
+  DCHECK(U_SUCCESS(error));
+  return result;
+}
+
+}  // namespace i18n
+}  // namespace base
diff --git a/base/i18n/string_compare.h b/base/i18n/string_compare.h
new file mode 100644
index 0000000..5fcc5fe
--- /dev/null
+++ b/base/i18n/string_compare.h
@@ -0,0 +1,28 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_I18N_STRING_COMPARE_H_
+#define BASE_I18N_STRING_COMPARE_H_
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include "base/i18n/base_i18n_export.h"
+#include "base/strings/string16.h"
+#include "third_party/icu/source/i18n/unicode/coll.h"
+
+namespace base {
+namespace i18n {
+
+// Compares the two strings using the specified collator.
+BASE_I18N_EXPORT UCollationResult
+CompareString16WithCollator(const icu::Collator& collator,
+                            const string16& lhs,
+                            const string16& rhs);
+
+}  // namespace i18n
+}  // namespace base
+
+#endif  // BASE_I18N_STRING_COMPARE_H_
diff --git a/base/i18n/string_search.cc b/base/i18n/string_search.cc
new file mode 100644
index 0000000..2f6fee4
--- /dev/null
+++ b/base/i18n/string_search.cc
@@ -0,0 +1,81 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdint.h>
+
+#include "base/i18n/string_search.h"
+#include "base/logging.h"
+
+#include "third_party/icu/source/i18n/unicode/usearch.h"
+
+namespace base {
+namespace i18n {
+
+FixedPatternStringSearchIgnoringCaseAndAccents::
+FixedPatternStringSearchIgnoringCaseAndAccents(const string16& find_this)
+    : find_this_(find_this) {
+  // usearch_open requires a valid string argument to be searched, even if we
+  // want to set it by usearch_setText afterwards. So, supplying a dummy text.
+  const string16& dummy = find_this_;
+
+  UErrorCode status = U_ZERO_ERROR;
+  search_ = usearch_open(find_this_.data(), find_this_.size(), dummy.data(),
+                         dummy.size(), uloc_getDefault(),
+                         nullptr,  // breakiter
+                         &status);
+  if (U_SUCCESS(status)) {
+    UCollator* collator = usearch_getCollator(search_);
+    ucol_setStrength(collator, UCOL_PRIMARY);
+    usearch_reset(search_);
+  }
+}
+
+FixedPatternStringSearchIgnoringCaseAndAccents::
+~FixedPatternStringSearchIgnoringCaseAndAccents() {
+  if (search_)
+    usearch_close(search_);
+}
+
+bool FixedPatternStringSearchIgnoringCaseAndAccents::Search(
+    const string16& in_this, size_t* match_index, size_t* match_length) {
+  UErrorCode status = U_ZERO_ERROR;
+  usearch_setText(search_, in_this.data(), in_this.size(), &status);
+
+  // Default to basic substring search if usearch fails. According to
+  // http://icu-project.org/apiref/icu4c/usearch_8h.html, usearch_open will fail
+  // if either |find_this| or |in_this| are empty. In either case basic
+  // substring search will give the correct return value.
+  if (!U_SUCCESS(status)) {
+    size_t index = in_this.find(find_this_);
+    if (index == string16::npos) {
+      return false;
+    } else {
+      if (match_index)
+        *match_index = index;
+      if (match_length)
+        *match_length = find_this_.size();
+      return true;
+    }
+  }
+
+  int32_t index = usearch_first(search_, &status);
+  if (!U_SUCCESS(status) || index == USEARCH_DONE)
+    return false;
+  if (match_index)
+    *match_index = static_cast<size_t>(index);
+  if (match_length)
+    *match_length = static_cast<size_t>(usearch_getMatchedLength(search_));
+  return true;
+}
+
+bool StringSearchIgnoringCaseAndAccents(const string16& find_this,
+                                        const string16& in_this,
+                                        size_t* match_index,
+                                        size_t* match_length) {
+  return FixedPatternStringSearchIgnoringCaseAndAccents(find_this).Search(
+      in_this, match_index, match_length);
+}
+
+}  // namespace i18n
+}  // namespace base
diff --git a/base/i18n/string_search.h b/base/i18n/string_search.h
new file mode 100644
index 0000000..07a29c1
--- /dev/null
+++ b/base/i18n/string_search.h
@@ -0,0 +1,55 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_I18N_STRING_SEARCH_H_
+#define BASE_I18N_STRING_SEARCH_H_
+
+#include <stddef.h>
+
+#include "base/i18n/base_i18n_export.h"
+#include "base/strings/string16.h"
+
+struct UStringSearch;
+
+namespace base {
+namespace i18n {
+
+// Returns true if |in_this| contains |find_this|. If |match_index| or
+// |match_length| are non-NULL, they are assigned the start position and total
+// length of the match.
+//
+// Only differences between base letters are taken into consideration. Case and
+// accent differences are ignored. Please refer to 'primary level' in
+// http://userguide.icu-project.org/collation/concepts for additional details.
+BASE_I18N_EXPORT
+    bool StringSearchIgnoringCaseAndAccents(const string16& find_this,
+                                            const string16& in_this,
+                                            size_t* match_index,
+                                            size_t* match_length);
+
+// This class is for speeding up multiple StringSearchIgnoringCaseAndAccents()
+// with the same |find_this| argument. |find_this| is passed as the constructor
+// argument, and precomputation for searching is done only at that timing.
+class BASE_I18N_EXPORT FixedPatternStringSearchIgnoringCaseAndAccents {
+ public:
+  explicit FixedPatternStringSearchIgnoringCaseAndAccents(
+      const string16& find_this);
+  ~FixedPatternStringSearchIgnoringCaseAndAccents();
+
+  // Returns true if |in_this| contains |find_this|. If |match_index| or
+  // |match_length| are non-NULL, they are assigned the start position and total
+  // length of the match.
+  bool Search(const string16& in_this,
+              size_t* match_index,
+              size_t* match_length);
+
+ private:
+  string16 find_this_;
+  UStringSearch* search_;
+};
+
+}  // namespace i18n
+}  // namespace base
+
+#endif  // BASE_I18N_STRING_SEARCH_H_
diff --git a/base/i18n/string_search_unittest.cc b/base/i18n/string_search_unittest.cc
new file mode 100644
index 0000000..69501d6
--- /dev/null
+++ b/base/i18n/string_search_unittest.cc
@@ -0,0 +1,228 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+
+#include <string>
+
+#include "base/i18n/rtl.h"
+#include "base/i18n/string_search.h"
+#include "base/strings/string16.h"
+#include "base/strings/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/icu/source/i18n/unicode/usearch.h"
+
+namespace base {
+namespace i18n {
+
+// Note on setting default locale for testing: The current default locale on
+// the Mac trybot is en_US_POSIX, with which primary-level collation strength
+// string search is case-sensitive, when normally it should be
+// case-insensitive. In other locales (including en_US which English speakers
+// in the U.S. use), this search would be case-insensitive as expected.
+
+TEST(StringSearchTest, ASCII) {
+  std::string default_locale(uloc_getDefault());
+  bool locale_is_posix = (default_locale == "en_US_POSIX");
+  if (locale_is_posix)
+    SetICUDefaultLocale("en_US");
+
+  size_t index = 0;
+  size_t length = 0;
+
+  EXPECT_TRUE(StringSearchIgnoringCaseAndAccents(
+      ASCIIToUTF16("hello"), ASCIIToUTF16("hello world"), &index, &length));
+  EXPECT_EQ(0U, index);
+  EXPECT_EQ(5U, length);
+
+  EXPECT_FALSE(StringSearchIgnoringCaseAndAccents(
+      ASCIIToUTF16("h    e l l o"), ASCIIToUTF16("h   e l l o"),
+      &index, &length));
+
+  EXPECT_TRUE(StringSearchIgnoringCaseAndAccents(
+      ASCIIToUTF16("aabaaa"), ASCIIToUTF16("aaabaabaaa"), &index, &length));
+  EXPECT_EQ(4U, index);
+  EXPECT_EQ(6U, length);
+
+  EXPECT_FALSE(StringSearchIgnoringCaseAndAccents(
+      ASCIIToUTF16("searching within empty string"), string16(),
+      &index, &length));
+
+  EXPECT_TRUE(StringSearchIgnoringCaseAndAccents(
+      string16(), ASCIIToUTF16("searching for empty string"), &index, &length));
+  EXPECT_EQ(0U, index);
+  EXPECT_EQ(0U, length);
+
+  EXPECT_TRUE(StringSearchIgnoringCaseAndAccents(
+      ASCIIToUTF16("case insensitivity"), ASCIIToUTF16("CaSe InSeNsItIvItY"),
+      &index, &length));
+  EXPECT_EQ(0U, index);
+  EXPECT_EQ(18U, length);
+
+  if (locale_is_posix)
+    SetICUDefaultLocale(default_locale.data());
+}
+
+TEST(StringSearchTest, UnicodeLocaleIndependent) {
+  // Base characters
+  const string16 e_base = WideToUTF16(L"e");
+  const string16 E_base = WideToUTF16(L"E");
+  const string16 a_base = WideToUTF16(L"a");
+
+  // Composed characters
+  const string16 e_with_acute_accent = WideToUTF16(L"\u00e9");
+  const string16 E_with_acute_accent = WideToUTF16(L"\u00c9");
+  const string16 e_with_grave_accent = WideToUTF16(L"\u00e8");
+  const string16 E_with_grave_accent = WideToUTF16(L"\u00c8");
+  const string16 a_with_acute_accent = WideToUTF16(L"\u00e1");
+
+  // Decomposed characters
+  const string16 e_with_acute_combining_mark = WideToUTF16(L"e\u0301");
+  const string16 E_with_acute_combining_mark = WideToUTF16(L"E\u0301");
+  const string16 e_with_grave_combining_mark = WideToUTF16(L"e\u0300");
+  const string16 E_with_grave_combining_mark = WideToUTF16(L"E\u0300");
+  const string16 a_with_acute_combining_mark = WideToUTF16(L"a\u0301");
+
+  std::string default_locale(uloc_getDefault());
+  bool locale_is_posix = (default_locale == "en_US_POSIX");
+  if (locale_is_posix)
+    SetICUDefaultLocale("en_US");
+
+  size_t index = 0;
+  size_t length = 0;
+
+  EXPECT_TRUE(StringSearchIgnoringCaseAndAccents(
+      e_base, e_with_acute_accent, &index, &length));
+  EXPECT_EQ(0U, index);
+  EXPECT_EQ(e_with_acute_accent.size(), length);
+
+  EXPECT_TRUE(StringSearchIgnoringCaseAndAccents(
+      e_with_acute_accent, e_base, &index, &length));
+  EXPECT_EQ(0U, index);
+  EXPECT_EQ(e_base.size(), length);
+
+  EXPECT_TRUE(StringSearchIgnoringCaseAndAccents(
+      e_base, e_with_acute_combining_mark, &index, &length));
+  EXPECT_EQ(0U, index);
+  EXPECT_EQ(e_with_acute_combining_mark.size(), length);
+
+  EXPECT_TRUE(StringSearchIgnoringCaseAndAccents(
+      e_with_acute_combining_mark, e_base, &index, &length));
+  EXPECT_EQ(0U, index);
+  EXPECT_EQ(e_base.size(), length);
+
+  EXPECT_TRUE(StringSearchIgnoringCaseAndAccents(
+      e_with_acute_combining_mark, e_with_acute_accent,
+      &index, &length));
+  EXPECT_EQ(0U, index);
+  EXPECT_EQ(e_with_acute_accent.size(), length);
+
+  EXPECT_TRUE(StringSearchIgnoringCaseAndAccents(
+      e_with_acute_accent, e_with_acute_combining_mark,
+      &index, &length));
+  EXPECT_EQ(0U, index);
+  EXPECT_EQ(e_with_acute_combining_mark.size(), length);
+
+  EXPECT_TRUE(StringSearchIgnoringCaseAndAccents(
+      e_with_acute_combining_mark, e_with_grave_combining_mark,
+      &index, &length));
+  EXPECT_EQ(0U, index);
+  EXPECT_EQ(e_with_grave_combining_mark.size(), length);
+
+  EXPECT_TRUE(StringSearchIgnoringCaseAndAccents(
+      e_with_grave_combining_mark, e_with_acute_combining_mark,
+      &index, &length));
+  EXPECT_EQ(0U, index);
+  EXPECT_EQ(e_with_acute_combining_mark.size(), length);
+
+  EXPECT_TRUE(StringSearchIgnoringCaseAndAccents(
+      e_with_acute_combining_mark, e_with_grave_accent, &index, &length));
+  EXPECT_EQ(0U, index);
+  EXPECT_EQ(e_with_grave_accent.size(), length);
+
+  EXPECT_TRUE(StringSearchIgnoringCaseAndAccents(
+      e_with_grave_accent, e_with_acute_combining_mark, &index, &length));
+  EXPECT_EQ(0U, index);
+  EXPECT_EQ(e_with_acute_combining_mark.size(), length);
+
+  EXPECT_TRUE(StringSearchIgnoringCaseAndAccents(
+      E_with_acute_accent, e_with_acute_accent, &index, &length));
+  EXPECT_EQ(0U, index);
+  EXPECT_EQ(e_with_acute_accent.size(), length);
+
+  EXPECT_TRUE(StringSearchIgnoringCaseAndAccents(
+      E_with_grave_accent, e_with_acute_accent, &index, &length));
+  EXPECT_EQ(0U, index);
+  EXPECT_EQ(e_with_acute_accent.size(), length);
+
+  EXPECT_TRUE(StringSearchIgnoringCaseAndAccents(
+      E_with_acute_combining_mark, e_with_grave_accent, &index, &length));
+  EXPECT_EQ(0U, index);
+  EXPECT_EQ(e_with_grave_accent.size(), length);
+
+  EXPECT_TRUE(StringSearchIgnoringCaseAndAccents(
+      E_with_grave_combining_mark, e_with_acute_accent, &index, &length));
+  EXPECT_EQ(0U, index);
+  EXPECT_EQ(e_with_acute_accent.size(), length);
+
+  EXPECT_TRUE(StringSearchIgnoringCaseAndAccents(
+      E_base, e_with_grave_accent, &index, &length));
+  EXPECT_EQ(0U, index);
+  EXPECT_EQ(e_with_grave_accent.size(), length);
+
+  EXPECT_FALSE(StringSearchIgnoringCaseAndAccents(
+      a_with_acute_accent, e_with_acute_accent, &index, &length));
+
+  EXPECT_FALSE(StringSearchIgnoringCaseAndAccents(
+      a_with_acute_combining_mark, e_with_acute_combining_mark,
+      &index, &length));
+
+  if (locale_is_posix)
+    SetICUDefaultLocale(default_locale.data());
+}
+
+TEST(StringSearchTest, UnicodeLocaleDependent) {
+  // Base characters
+  const string16 a_base = WideToUTF16(L"a");
+
+  // Composed characters
+  const string16 a_with_ring = WideToUTF16(L"\u00e5");
+
+  EXPECT_TRUE(StringSearchIgnoringCaseAndAccents(a_base, a_with_ring, nullptr,
+                                                 nullptr));
+
+  const char* default_locale = uloc_getDefault();
+  SetICUDefaultLocale("da");
+
+  EXPECT_FALSE(StringSearchIgnoringCaseAndAccents(a_base, a_with_ring, nullptr,
+                                                  nullptr));
+
+  SetICUDefaultLocale(default_locale);
+}
+
+TEST(StringSearchTest, FixedPatternMultipleSearch) {
+  std::string default_locale(uloc_getDefault());
+  bool locale_is_posix = (default_locale == "en_US_POSIX");
+  if (locale_is_posix)
+    SetICUDefaultLocale("en_US");
+
+  size_t index = 0;
+  size_t length = 0;
+
+  // Search "hello" over multiple texts.
+  FixedPatternStringSearchIgnoringCaseAndAccents query(ASCIIToUTF16("hello"));
+  EXPECT_TRUE(query.Search(ASCIIToUTF16("12hello34"), &index, &length));
+  EXPECT_EQ(2U, index);
+  EXPECT_EQ(5U, length);
+  EXPECT_FALSE(query.Search(ASCIIToUTF16("bye"), &index, &length));
+  EXPECT_TRUE(query.Search(ASCIIToUTF16("hELLo"), &index, &length));
+  EXPECT_EQ(0U, index);
+  EXPECT_EQ(5U, length);
+
+  if (locale_is_posix)
+    SetICUDefaultLocale(default_locale.data());
+}
+
+}  // namespace i18n
+}  // namespace base
diff --git a/base/i18n/time_formatting.cc b/base/i18n/time_formatting.cc
new file mode 100644
index 0000000..3a5394a
--- /dev/null
+++ b/base/i18n/time_formatting.cc
@@ -0,0 +1,301 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/i18n/time_formatting.h"
+
+#include <stddef.h>
+
+#include <memory>
+
+#include "base/i18n/unicodestring.h"
+#include "base/logging.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/time/time.h"
+#include "third_party/icu/source/common/unicode/utypes.h"
+#include "third_party/icu/source/i18n/unicode/datefmt.h"
+#include "third_party/icu/source/i18n/unicode/dtitvfmt.h"
+#include "third_party/icu/source/i18n/unicode/dtptngen.h"
+#include "third_party/icu/source/i18n/unicode/fmtable.h"
+#include "third_party/icu/source/i18n/unicode/measfmt.h"
+#include "third_party/icu/source/i18n/unicode/smpdtfmt.h"
+
+namespace base {
+namespace {
+
+string16 TimeFormat(const icu::DateFormat* formatter,
+                    const Time& time) {
+  DCHECK(formatter);
+  icu::UnicodeString date_string;
+
+  formatter->format(static_cast<UDate>(time.ToDoubleT() * 1000), date_string);
+  return i18n::UnicodeStringToString16(date_string);
+}
+
+string16 TimeFormatWithoutAmPm(const icu::DateFormat* formatter,
+                               const Time& time) {
+  DCHECK(formatter);
+  icu::UnicodeString time_string;
+
+  icu::FieldPosition ampm_field(icu::DateFormat::kAmPmField);
+  formatter->format(
+      static_cast<UDate>(time.ToDoubleT() * 1000), time_string, ampm_field);
+  int ampm_length = ampm_field.getEndIndex() - ampm_field.getBeginIndex();
+  if (ampm_length) {
+    int begin = ampm_field.getBeginIndex();
+    // Doesn't include any spacing before the field.
+    if (begin)
+      begin--;
+    time_string.removeBetween(begin, ampm_field.getEndIndex());
+  }
+  return i18n::UnicodeStringToString16(time_string);
+}
+
+icu::SimpleDateFormat CreateSimpleDateFormatter(const char* pattern) {
+  // Generate a locale-dependent format pattern. The generator will take
+  // care of locale-dependent formatting issues like which separator to
+  // use (some locales use '.' instead of ':'), and where to put the am/pm
+  // marker.
+  UErrorCode status = U_ZERO_ERROR;
+  std::unique_ptr<icu::DateTimePatternGenerator> generator(
+      icu::DateTimePatternGenerator::createInstance(status));
+  DCHECK(U_SUCCESS(status));
+  icu::UnicodeString generated_pattern =
+      generator->getBestPattern(icu::UnicodeString(pattern), status);
+  DCHECK(U_SUCCESS(status));
+
+  // Then, format the time using the generated pattern.
+  icu::SimpleDateFormat formatter(generated_pattern, status);
+  DCHECK(U_SUCCESS(status));
+
+  return formatter;
+}
+
+UMeasureFormatWidth DurationWidthToMeasureWidth(DurationFormatWidth width) {
+  switch (width) {
+    case DURATION_WIDTH_WIDE: return UMEASFMT_WIDTH_WIDE;
+    case DURATION_WIDTH_SHORT: return UMEASFMT_WIDTH_SHORT;
+    case DURATION_WIDTH_NARROW: return UMEASFMT_WIDTH_NARROW;
+    case DURATION_WIDTH_NUMERIC: return UMEASFMT_WIDTH_NUMERIC;
+  }
+  NOTREACHED();
+  return UMEASFMT_WIDTH_COUNT;
+}
+
+const char* DateFormatToString(DateFormat format) {
+  switch (format) {
+    case DATE_FORMAT_YEAR_MONTH:
+      return UDAT_YEAR_MONTH;
+    case DATE_FORMAT_MONTH_WEEKDAY_DAY:
+      return UDAT_MONTH_WEEKDAY_DAY;
+  }
+  NOTREACHED();
+  return UDAT_YEAR_MONTH_DAY;
+}
+
+}  // namespace
+
+string16 TimeFormatTimeOfDay(const Time& time) {
+  // We can omit the locale parameter because the default should match
+  // Chrome's application locale.
+  std::unique_ptr<icu::DateFormat> formatter(
+      icu::DateFormat::createTimeInstance(icu::DateFormat::kShort));
+  return TimeFormat(formatter.get(), time);
+}
+
+string16 TimeFormatTimeOfDayWithMilliseconds(const Time& time) {
+  icu::SimpleDateFormat formatter = CreateSimpleDateFormatter("HmsSSS");
+  return TimeFormatWithoutAmPm(&formatter, time);
+}
+
+string16 TimeFormatTimeOfDayWithHourClockType(const Time& time,
+                                              HourClockType type,
+                                              AmPmClockType ampm) {
+  // Just redirect to the normal function if the default type matches the
+  // given type.
+  HourClockType default_type = GetHourClockType();
+  if (default_type == type && (type == k24HourClock || ampm == kKeepAmPm)) {
+    return TimeFormatTimeOfDay(time);
+  }
+
+  const char* base_pattern = (type == k12HourClock ? "ahm" : "Hm");
+  icu::SimpleDateFormat formatter = CreateSimpleDateFormatter(base_pattern);
+
+  if (ampm == kKeepAmPm) {
+    return TimeFormat(&formatter, time);
+  } else {
+    return TimeFormatWithoutAmPm(&formatter, time);
+  }
+}
+
+string16 TimeFormatShortDate(const Time& time) {
+  std::unique_ptr<icu::DateFormat> formatter(
+      icu::DateFormat::createDateInstance(icu::DateFormat::kMedium));
+  return TimeFormat(formatter.get(), time);
+}
+
+string16 TimeFormatShortDateNumeric(const Time& time) {
+  std::unique_ptr<icu::DateFormat> formatter(
+      icu::DateFormat::createDateInstance(icu::DateFormat::kShort));
+  return TimeFormat(formatter.get(), time);
+}
+
+string16 TimeFormatShortDateAndTime(const Time& time) {
+  std::unique_ptr<icu::DateFormat> formatter(
+      icu::DateFormat::createDateTimeInstance(icu::DateFormat::kShort));
+  return TimeFormat(formatter.get(), time);
+}
+
+string16 TimeFormatShortDateAndTimeWithTimeZone(const Time& time) {
+  std::unique_ptr<icu::DateFormat> formatter(
+      icu::DateFormat::createDateTimeInstance(icu::DateFormat::kShort,
+                                              icu::DateFormat::kLong));
+  return TimeFormat(formatter.get(), time);
+}
+
+string16 TimeFormatMonthAndYear(const Time& time) {
+  icu::SimpleDateFormat formatter =
+      CreateSimpleDateFormatter(DateFormatToString(DATE_FORMAT_YEAR_MONTH));
+  return TimeFormat(&formatter, time);
+}
+
+string16 TimeFormatFriendlyDateAndTime(const Time& time) {
+  std::unique_ptr<icu::DateFormat> formatter(
+      icu::DateFormat::createDateTimeInstance(icu::DateFormat::kFull));
+  return TimeFormat(formatter.get(), time);
+}
+
+string16 TimeFormatFriendlyDate(const Time& time) {
+  std::unique_ptr<icu::DateFormat> formatter(
+      icu::DateFormat::createDateInstance(icu::DateFormat::kFull));
+  return TimeFormat(formatter.get(), time);
+}
+
+string16 TimeFormatWithPattern(const Time& time, const char* pattern) {
+  icu::SimpleDateFormat formatter = CreateSimpleDateFormatter(pattern);
+  return TimeFormat(&formatter, time);
+}
+
+bool TimeDurationFormat(const TimeDelta time,
+                        const DurationFormatWidth width,
+                        string16* out) {
+  DCHECK(out);
+  UErrorCode status = U_ZERO_ERROR;
+  const int total_minutes = static_cast<int>(time.InSecondsF() / 60 + 0.5);
+  const int hours = total_minutes / 60;
+  const int minutes = total_minutes % 60;
+  UMeasureFormatWidth u_width = DurationWidthToMeasureWidth(width);
+
+  // TODO(derat): Delete the |status| checks and LOG(ERROR) calls throughout
+  // this function once the cause of http://crbug.com/677043 is tracked down.
+  const icu::Measure measures[] = {
+      icu::Measure(hours, icu::MeasureUnit::createHour(status), status),
+      icu::Measure(minutes, icu::MeasureUnit::createMinute(status), status)};
+  if (U_FAILURE(status)) {
+    LOG(ERROR) << "Creating MeasureUnit or Measure for " << hours << "h"
+               << minutes << "m failed: " << u_errorName(status);
+    return false;
+  }
+
+  icu::MeasureFormat measure_format(icu::Locale::getDefault(), u_width, status);
+  if (U_FAILURE(status)) {
+    LOG(ERROR) << "Creating MeasureFormat for "
+               << icu::Locale::getDefault().getName()
+               << " failed: " << u_errorName(status);
+    return false;
+  }
+
+  icu::UnicodeString formatted;
+  icu::FieldPosition ignore(icu::FieldPosition::DONT_CARE);
+  measure_format.formatMeasures(measures, 2, formatted, ignore, status);
+  if (U_FAILURE(status)) {
+    LOG(ERROR) << "formatMeasures failed: " << u_errorName(status);
+    return false;
+  }
+
+  *out = i18n::UnicodeStringToString16(formatted);
+  return true;
+}
+
+bool TimeDurationFormatWithSeconds(const TimeDelta time,
+                                   const DurationFormatWidth width,
+                                   string16* out) {
+  DCHECK(out);
+  UErrorCode status = U_ZERO_ERROR;
+  const int64_t total_seconds = static_cast<int>(time.InSecondsF() + 0.5);
+  const int hours = total_seconds / 3600;
+  const int minutes = (total_seconds - hours * 3600) / 60;
+  const int seconds = total_seconds % 60;
+  UMeasureFormatWidth u_width = DurationWidthToMeasureWidth(width);
+
+  const icu::Measure measures[] = {
+      icu::Measure(hours, icu::MeasureUnit::createHour(status), status),
+      icu::Measure(minutes, icu::MeasureUnit::createMinute(status), status),
+      icu::Measure(seconds, icu::MeasureUnit::createSecond(status), status)};
+  icu::MeasureFormat measure_format(icu::Locale::getDefault(), u_width, status);
+  icu::UnicodeString formatted;
+  icu::FieldPosition ignore(icu::FieldPosition::DONT_CARE);
+  measure_format.formatMeasures(measures, 3, formatted, ignore, status);
+  *out = i18n::UnicodeStringToString16(formatted);
+  return U_SUCCESS(status) == TRUE;
+}
+
+string16 DateIntervalFormat(const Time& begin_time,
+                            const Time& end_time,
+                            DateFormat format) {
+  UErrorCode status = U_ZERO_ERROR;
+
+  std::unique_ptr<icu::DateIntervalFormat> formatter(
+      icu::DateIntervalFormat::createInstance(DateFormatToString(format),
+                                              status));
+
+  icu::FieldPosition pos = 0;
+  UDate start_date = static_cast<UDate>(begin_time.ToDoubleT() * 1000);
+  UDate end_date = static_cast<UDate>(end_time.ToDoubleT() * 1000);
+  icu::DateInterval interval(start_date, end_date);
+  icu::UnicodeString formatted;
+  formatter->format(&interval, formatted, pos, status);
+  return i18n::UnicodeStringToString16(formatted);
+}
+
+HourClockType GetHourClockType() {
+  // TODO(satorux,jshin): Rework this with ures_getByKeyWithFallback()
+  // once it becomes public. The short time format can be found at
+  // "calendar/gregorian/DateTimePatterns/3" in the resources.
+  std::unique_ptr<icu::SimpleDateFormat> formatter(
+      static_cast<icu::SimpleDateFormat*>(
+          icu::DateFormat::createTimeInstance(icu::DateFormat::kShort)));
+  // Retrieve the short time format.
+  icu::UnicodeString pattern_unicode;
+  formatter->toPattern(pattern_unicode);
+
+  // Determine what hour clock type the current locale uses, by checking
+  // "a" (am/pm marker) in the short time format. This is reliable as "a"
+  // is used by all of 12-hour clock formats, but not any of 24-hour clock
+  // formats, as shown below.
+  //
+  // % grep -A4 DateTimePatterns third_party/icu/source/data/locales/*.txt |
+  //   grep -B1 -- -- |grep -v -- '--' |
+  //   perl -nle 'print $1 if /^\S+\s+"(.*)"/' |sort -u
+  //
+  // H.mm
+  // H:mm
+  // HH.mm
+  // HH:mm
+  // a h:mm
+  // ah:mm
+  // ahh:mm
+  // h-mm a
+  // h:mm a
+  // hh:mm a
+  //
+  // See http://userguide.icu-project.org/formatparse/datetime for details
+  // about the date/time format syntax.
+  if (pattern_unicode.indexOf('a') == -1) {
+    return k24HourClock;
+  } else {
+    return k12HourClock;
+  }
+}
+
+}  // namespace base
diff --git a/base/i18n/time_formatting.h b/base/i18n/time_formatting.h
new file mode 100644
index 0000000..41793b3
--- /dev/null
+++ b/base/i18n/time_formatting.h
@@ -0,0 +1,142 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Basic time formatting methods.  These methods use the current locale
+// formatting for displaying the time.
+
+#ifndef BASE_I18N_TIME_FORMATTING_H_
+#define BASE_I18N_TIME_FORMATTING_H_
+
+#include "base/compiler_specific.h"
+#include "base/i18n/base_i18n_export.h"
+#include "base/strings/string16.h"
+
+namespace base {
+
+class Time;
+class TimeDelta;
+
+// Argument type used to specify the hour clock type.
+enum HourClockType {
+  k12HourClock,  // Uses 1-12. e.g., "3:07 PM"
+  k24HourClock,  // Uses 0-23. e.g., "15:07"
+};
+
+// Argument type used to specify whether or not to include AM/PM sign.
+enum AmPmClockType {
+  kDropAmPm,  // Drops AM/PM sign. e.g., "3:07"
+  kKeepAmPm,  // Keeps AM/PM sign. e.g., "3:07 PM"
+};
+
+// Should match UMeasureFormatWidth in measfmt.h; replicated here to avoid
+// requiring third_party/icu dependencies with this file.
+enum DurationFormatWidth {
+  DURATION_WIDTH_WIDE,    // "3 hours, 7 minutes"
+  DURATION_WIDTH_SHORT,   // "3 hr, 7 min"
+  DURATION_WIDTH_NARROW,  // "3h 7m"
+  DURATION_WIDTH_NUMERIC  // "3:07"
+};
+
+// Date formats from third_party/icu/source/i18n/unicode/udat.h. Add more as
+// necessary.
+enum DateFormat {
+  // November 2007
+  DATE_FORMAT_YEAR_MONTH,
+  // Tuesday, 7 November
+  DATE_FORMAT_MONTH_WEEKDAY_DAY,
+};
+
+// TODO(derat@chromium.org): Update all of these functions to return boolean
+// "success" values and use out-params for formatted strings:
+// http://crbug.com/698802
+
+// Returns the time of day, e.g., "3:07 PM".
+BASE_I18N_EXPORT string16 TimeFormatTimeOfDay(const Time& time);
+
+// Returns the time of day in 24-hour clock format with millisecond accuracy,
+// e.g., "15:07:30.568"
+BASE_I18N_EXPORT string16 TimeFormatTimeOfDayWithMilliseconds(const Time& time);
+
+// Returns the time of day in the specified hour clock type. e.g.
+// "3:07 PM" (type == k12HourClock, ampm == kKeepAmPm).
+// "3:07"    (type == k12HourClock, ampm == kDropAmPm).
+// "15:07"   (type == k24HourClock).
+BASE_I18N_EXPORT string16 TimeFormatTimeOfDayWithHourClockType(
+    const Time& time,
+    HourClockType type,
+    AmPmClockType ampm);
+
+// Returns a shortened date, e.g. "Nov 7, 2007"
+BASE_I18N_EXPORT string16 TimeFormatShortDate(const Time& time);
+
+// Returns a numeric date such as 12/13/52.
+BASE_I18N_EXPORT string16 TimeFormatShortDateNumeric(const Time& time);
+
+// Returns a numeric date and time such as "12/13/52 2:44:30 PM".
+BASE_I18N_EXPORT string16 TimeFormatShortDateAndTime(const Time& time);
+
+// Returns a month and year, e.g. "November 2007"
+BASE_I18N_EXPORT string16 TimeFormatMonthAndYear(const Time& time);
+
+// Returns a numeric date and time with time zone such as
+// "12/13/52 2:44:30 PM PST".
+BASE_I18N_EXPORT string16
+TimeFormatShortDateAndTimeWithTimeZone(const Time& time);
+
+// Formats a time in a friendly sentence format, e.g.
+// "Monday, March 6, 2008 2:44:30 PM".
+BASE_I18N_EXPORT string16 TimeFormatFriendlyDateAndTime(const Time& time);
+
+// Formats a time in a friendly sentence format, e.g.
+// "Monday, March 6, 2008".
+BASE_I18N_EXPORT string16 TimeFormatFriendlyDate(const Time& time);
+
+// Formats a time using a skeleton to produce a format for different locales
+// when an unusual time format is needed, e.g. "Feb. 2, 18:00".
+//
+// See http://userguide.icu-project.org/formatparse/datetime for details.
+BASE_I18N_EXPORT string16 TimeFormatWithPattern(const Time& time,
+                                                const char* pattern);
+
+// Formats a time duration of hours and minutes into various formats, e.g.,
+// "3:07" or "3 hours, 7 minutes", and returns true on success. See
+// DurationFormatWidth for details.
+//
+// Please don't use width = DURATION_WIDTH_NUMERIC when the time duration
+// can possibly be larger than 24h, as the hour value will be cut below 24
+// after formatting.
+// TODO(chengx): fix function output when width = DURATION_WIDTH_NUMERIC
+// (http://crbug.com/675791)
+BASE_I18N_EXPORT bool TimeDurationFormat(const TimeDelta time,
+                                         const DurationFormatWidth width,
+                                         string16* out) WARN_UNUSED_RESULT;
+
+// Formats a time duration of hours, minutes and seconds into various formats,
+// e.g., "3:07:30" or "3 hours, 7 minutes, 30 seconds", and returns true on
+// success. See DurationFormatWidth for details.
+//
+// Please don't use width = DURATION_WIDTH_NUMERIC when the time duration
+// can possibly be larger than 24h, as the hour value will be cut below 24
+// after formatting.
+// TODO(chengx): fix function output when width = DURATION_WIDTH_NUMERIC
+// (http://crbug.com/675791)
+BASE_I18N_EXPORT bool TimeDurationFormatWithSeconds(
+    const TimeDelta time,
+    const DurationFormatWidth width,
+    string16* out) WARN_UNUSED_RESULT;
+
+// Formats a date interval into various formats, e.g. "2 December - 4 December"
+// or "March 2016 - December 2016". See DateFormat for details.
+BASE_I18N_EXPORT string16 DateIntervalFormat(const Time& begin_time,
+                                             const Time& end_time,
+                                             DateFormat format);
+
+// Gets the hour clock type of the current locale. e.g.
+// k12HourClock (en-US).
+// k24HourClock (en-GB).
+BASE_I18N_EXPORT HourClockType GetHourClockType();
+
+}  // namespace base
+
+#endif  // BASE_I18N_TIME_FORMATTING_H_
diff --git a/base/i18n/time_formatting_unittest.cc b/base/i18n/time_formatting_unittest.cc
new file mode 100644
index 0000000..29b2597
--- /dev/null
+++ b/base/i18n/time_formatting_unittest.cc
@@ -0,0 +1,433 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/i18n/time_formatting.h"
+
+#include <memory>
+
+#include "base/i18n/rtl.h"
+#include "base/i18n/unicodestring.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/icu_test_util.h"
+#include "base/time/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/icu/source/common/unicode/uversion.h"
+#include "third_party/icu/source/i18n/unicode/calendar.h"
+#include "third_party/icu/source/i18n/unicode/timezone.h"
+#include "third_party/icu/source/i18n/unicode/tzfmt.h"
+
+namespace base {
+namespace {
+
+const Time::Exploded kTestDateTimeExploded = {
+    2011, 4,  6, 30,  // Sat, Apr 30, 2011
+    22,   42, 7, 0    // 22:42:07.000 in UTC = 15:42:07 in US PDT.
+};
+
+// Returns difference between the local time and GMT formatted as string.
+// This function gets |time| because the difference depends on time,
+// see https://en.wikipedia.org/wiki/Daylight_saving_time for details.
+string16 GetShortTimeZone(const Time& time) {
+  UErrorCode status = U_ZERO_ERROR;
+  std::unique_ptr<icu::TimeZone> zone(icu::TimeZone::createDefault());
+  std::unique_ptr<icu::TimeZoneFormat> zone_formatter(
+      icu::TimeZoneFormat::createInstance(icu::Locale::getDefault(), status));
+  EXPECT_TRUE(U_SUCCESS(status));
+  icu::UnicodeString name;
+  zone_formatter->format(UTZFMT_STYLE_SPECIFIC_SHORT, *zone,
+                         static_cast<UDate>(time.ToDoubleT() * 1000),
+                         name, nullptr);
+  return i18n::UnicodeStringToString16(name);
+}
+
+// Calls TimeDurationFormat() with |delta| and |width| and returns the resulting
+// string. On failure, adds a failed expectation and returns an empty string.
+string16 TimeDurationFormatString(const TimeDelta& delta,
+                                  DurationFormatWidth width) {
+  string16 str;
+  EXPECT_TRUE(TimeDurationFormat(delta, width, &str))
+      << "Failed to format " << delta.ToInternalValue() << " with width "
+      << width;
+  return str;
+}
+
+// Calls TimeDurationFormatWithSeconds() with |delta| and |width| and returns
+// the resulting string. On failure, adds a failed expectation and returns an
+// empty string.
+string16 TimeDurationFormatWithSecondsString(const TimeDelta& delta,
+                                             DurationFormatWidth width) {
+  string16 str;
+  EXPECT_TRUE(TimeDurationFormatWithSeconds(delta, width, &str))
+      << "Failed to format " << delta.ToInternalValue() << " with width "
+      << width;
+  return str;
+}
+
+class ScopedRestoreDefaultTimezone {
+ public:
+  ScopedRestoreDefaultTimezone(const char* zoneid) {
+    original_zone_.reset(icu::TimeZone::createDefault());
+    icu::TimeZone::adoptDefault(icu::TimeZone::createTimeZone(zoneid));
+  }
+  ~ScopedRestoreDefaultTimezone() {
+    icu::TimeZone::adoptDefault(original_zone_.release());
+  }
+
+  ScopedRestoreDefaultTimezone(const ScopedRestoreDefaultTimezone&) = delete;
+  ScopedRestoreDefaultTimezone& operator=(const ScopedRestoreDefaultTimezone&) =
+      delete;
+
+ private:
+  std::unique_ptr<icu::TimeZone> original_zone_;
+};
+
+TEST(TimeFormattingTest, TimeFormatTimeOfDayDefault12h) {
+  // Test for a locale defaulted to 12h clock.
+  // As an instance, we use third_party/icu/source/data/locales/en.txt.
+  test::ScopedRestoreICUDefaultLocale restore_locale;
+  i18n::SetICUDefaultLocale("en_US");
+  ScopedRestoreDefaultTimezone la_time("America/Los_Angeles");
+
+  Time time;
+  EXPECT_TRUE(Time::FromUTCExploded(kTestDateTimeExploded, &time));
+  string16 clock24h(ASCIIToUTF16("15:42"));
+  string16 clock12h_pm(ASCIIToUTF16("3:42 PM"));
+  string16 clock12h(ASCIIToUTF16("3:42"));
+  string16 clock24h_millis(ASCIIToUTF16("15:42:07.000"));
+
+  // The default is 12h clock.
+  EXPECT_EQ(clock12h_pm, TimeFormatTimeOfDay(time));
+  EXPECT_EQ(clock24h_millis, TimeFormatTimeOfDayWithMilliseconds(time));
+  EXPECT_EQ(k12HourClock, GetHourClockType());
+  // k{Keep,Drop}AmPm should not affect for 24h clock.
+  EXPECT_EQ(clock24h,
+            TimeFormatTimeOfDayWithHourClockType(time,
+                                                 k24HourClock,
+                                                 kKeepAmPm));
+  EXPECT_EQ(clock24h,
+            TimeFormatTimeOfDayWithHourClockType(time,
+                                                 k24HourClock,
+                                                 kDropAmPm));
+  // k{Keep,Drop}AmPm affects for 12h clock.
+  EXPECT_EQ(clock12h_pm,
+            TimeFormatTimeOfDayWithHourClockType(time,
+                                                 k12HourClock,
+                                                 kKeepAmPm));
+  EXPECT_EQ(clock12h,
+            TimeFormatTimeOfDayWithHourClockType(time,
+                                                 k12HourClock,
+                                                 kDropAmPm));
+}
+
+TEST(TimeFormattingTest, TimeFormatTimeOfDayDefault24h) {
+  // Test for a locale defaulted to 24h clock.
+  // As an instance, we use third_party/icu/source/data/locales/en_GB.txt.
+  test::ScopedRestoreICUDefaultLocale restore_locale;
+  i18n::SetICUDefaultLocale("en_GB");
+  ScopedRestoreDefaultTimezone la_time("America/Los_Angeles");
+
+  Time time;
+  EXPECT_TRUE(Time::FromUTCExploded(kTestDateTimeExploded, &time));
+  string16 clock24h(ASCIIToUTF16("15:42"));
+  string16 clock12h_pm(ASCIIToUTF16("3:42 pm"));
+  string16 clock12h(ASCIIToUTF16("3:42"));
+  string16 clock24h_millis(ASCIIToUTF16("15:42:07.000"));
+
+  // The default is 24h clock.
+  EXPECT_EQ(clock24h, TimeFormatTimeOfDay(time));
+  EXPECT_EQ(clock24h_millis, TimeFormatTimeOfDayWithMilliseconds(time));
+  EXPECT_EQ(k24HourClock, GetHourClockType());
+  // k{Keep,Drop}AmPm should not affect for 24h clock.
+  EXPECT_EQ(clock24h,
+            TimeFormatTimeOfDayWithHourClockType(time,
+                                                 k24HourClock,
+                                                 kKeepAmPm));
+  EXPECT_EQ(clock24h,
+            TimeFormatTimeOfDayWithHourClockType(time,
+                                                 k24HourClock,
+                                                 kDropAmPm));
+  // k{Keep,Drop}AmPm affects for 12h clock.
+  EXPECT_EQ(clock12h_pm,
+            TimeFormatTimeOfDayWithHourClockType(time,
+                                                 k12HourClock,
+                                                 kKeepAmPm));
+  EXPECT_EQ(clock12h,
+            TimeFormatTimeOfDayWithHourClockType(time,
+                                                 k12HourClock,
+                                                 kDropAmPm));
+}
+
+TEST(TimeFormattingTest, TimeFormatTimeOfDayJP) {
+  // Test for a locale that uses different mark than "AM" and "PM".
+  // As an instance, we use third_party/icu/source/data/locales/ja.txt.
+  test::ScopedRestoreICUDefaultLocale restore_locale;
+  i18n::SetICUDefaultLocale("ja_JP");
+  ScopedRestoreDefaultTimezone la_time("America/Los_Angeles");
+
+  Time time;
+  EXPECT_TRUE(Time::FromUTCExploded(kTestDateTimeExploded, &time));
+  string16 clock24h(ASCIIToUTF16("15:42"));
+  string16 clock12h_pm(UTF8ToUTF16(u8"午後3:42"));
+  string16 clock12h(ASCIIToUTF16("3:42"));
+
+  // The default is 24h clock.
+  EXPECT_EQ(clock24h, TimeFormatTimeOfDay(time));
+  EXPECT_EQ(k24HourClock, GetHourClockType());
+  // k{Keep,Drop}AmPm should not affect for 24h clock.
+  EXPECT_EQ(clock24h, TimeFormatTimeOfDayWithHourClockType(time, k24HourClock,
+                                                           kKeepAmPm));
+  EXPECT_EQ(clock24h, TimeFormatTimeOfDayWithHourClockType(time, k24HourClock,
+                                                           kDropAmPm));
+  // k{Keep,Drop}AmPm affects for 12h clock.
+  EXPECT_EQ(clock12h_pm, TimeFormatTimeOfDayWithHourClockType(
+                             time, k12HourClock, kKeepAmPm));
+  EXPECT_EQ(clock12h, TimeFormatTimeOfDayWithHourClockType(time, k12HourClock,
+                                                           kDropAmPm));
+}
+
+TEST(TimeFormattingTest, TimeFormatTimeOfDayDE) {
+  // German uses 24h by default, but uses 'AM', 'PM' for 12h format.
+  test::ScopedRestoreICUDefaultLocale restore_locale;
+  i18n::SetICUDefaultLocale("de");
+  ScopedRestoreDefaultTimezone la_time("America/Los_Angeles");
+
+  Time time;
+  EXPECT_TRUE(Time::FromUTCExploded(kTestDateTimeExploded, &time));
+  string16 clock24h(ASCIIToUTF16("15:42"));
+  string16 clock12h_pm(UTF8ToUTF16("3:42 PM"));
+  string16 clock12h(ASCIIToUTF16("3:42"));
+
+  // The default is 24h clock.
+  EXPECT_EQ(clock24h, TimeFormatTimeOfDay(time));
+  EXPECT_EQ(k24HourClock, GetHourClockType());
+  // k{Keep,Drop}AmPm should not affect for 24h clock.
+  EXPECT_EQ(clock24h,
+            TimeFormatTimeOfDayWithHourClockType(time,
+                                                 k24HourClock,
+                                                 kKeepAmPm));
+  EXPECT_EQ(clock24h,
+            TimeFormatTimeOfDayWithHourClockType(time,
+                                                 k24HourClock,
+                                                 kDropAmPm));
+  // k{Keep,Drop}AmPm affects for 12h clock.
+  EXPECT_EQ(clock12h_pm,
+            TimeFormatTimeOfDayWithHourClockType(time,
+                                                 k12HourClock,
+                                                 kKeepAmPm));
+  EXPECT_EQ(clock12h,
+            TimeFormatTimeOfDayWithHourClockType(time,
+                                                 k12HourClock,
+                                                 kDropAmPm));
+}
+
+TEST(TimeFormattingTest, TimeFormatDateUS) {
+  // See third_party/icu/source/data/locales/en.txt.
+  // The date patterns are "EEEE, MMMM d, y", "MMM d, y", and "M/d/yy".
+  test::ScopedRestoreICUDefaultLocale restore_locale;
+  i18n::SetICUDefaultLocale("en_US");
+  ScopedRestoreDefaultTimezone la_time("America/Los_Angeles");
+
+  Time time;
+  EXPECT_TRUE(Time::FromUTCExploded(kTestDateTimeExploded, &time));
+
+  EXPECT_EQ(ASCIIToUTF16("Apr 30, 2011"), TimeFormatShortDate(time));
+  EXPECT_EQ(ASCIIToUTF16("4/30/11"), TimeFormatShortDateNumeric(time));
+
+  EXPECT_EQ(ASCIIToUTF16("4/30/11, 3:42:07 PM"),
+            TimeFormatShortDateAndTime(time));
+  EXPECT_EQ(ASCIIToUTF16("4/30/11, 3:42:07 PM ") + GetShortTimeZone(time),
+            TimeFormatShortDateAndTimeWithTimeZone(time));
+
+  EXPECT_EQ(ASCIIToUTF16("April 2011"), TimeFormatMonthAndYear(time));
+
+  EXPECT_EQ(ASCIIToUTF16("Saturday, April 30, 2011 at 3:42:07 PM"),
+            TimeFormatFriendlyDateAndTime(time));
+
+  EXPECT_EQ(ASCIIToUTF16("Saturday, April 30, 2011"),
+            TimeFormatFriendlyDate(time));
+}
+
+TEST(TimeFormattingTest, TimeFormatDateGB) {
+  // See third_party/icu/source/data/locales/en_GB.txt.
+  // The date patterns are "EEEE, d MMMM y", "d MMM y", and "dd/MM/yyyy".
+  test::ScopedRestoreICUDefaultLocale restore_locale;
+  i18n::SetICUDefaultLocale("en_GB");
+  ScopedRestoreDefaultTimezone la_time("America/Los_Angeles");
+
+  Time time;
+  EXPECT_TRUE(Time::FromUTCExploded(kTestDateTimeExploded, &time));
+
+  EXPECT_EQ(ASCIIToUTF16("30 Apr 2011"), TimeFormatShortDate(time));
+  EXPECT_EQ(ASCIIToUTF16("30/04/2011"), TimeFormatShortDateNumeric(time));
+  EXPECT_EQ(ASCIIToUTF16("30/04/2011, 15:42:07"),
+            TimeFormatShortDateAndTime(time));
+  EXPECT_EQ(ASCIIToUTF16("30/04/2011, 15:42:07 ") + GetShortTimeZone(time),
+            TimeFormatShortDateAndTimeWithTimeZone(time));
+  EXPECT_EQ(ASCIIToUTF16("April 2011"), TimeFormatMonthAndYear(time));
+  EXPECT_EQ(ASCIIToUTF16("Saturday, 30 April 2011 at 15:42:07"),
+            TimeFormatFriendlyDateAndTime(time));
+  EXPECT_EQ(ASCIIToUTF16("Saturday, 30 April 2011"),
+            TimeFormatFriendlyDate(time));
+}
+
+TEST(TimeFormattingTest, TimeFormatWithPattern) {
+  test::ScopedRestoreICUDefaultLocale restore_locale;
+  ScopedRestoreDefaultTimezone la_time("America/Los_Angeles");
+
+  Time time;
+  EXPECT_TRUE(Time::FromUTCExploded(kTestDateTimeExploded, &time));
+
+  i18n::SetICUDefaultLocale("en_US");
+  EXPECT_EQ(ASCIIToUTF16("Apr 30, 2011"), TimeFormatWithPattern(time, "yMMMd"));
+  EXPECT_EQ(ASCIIToUTF16("April 30, 3:42:07 PM"),
+            TimeFormatWithPattern(time, "MMMMdjmmss"));
+
+  i18n::SetICUDefaultLocale("en_GB");
+  EXPECT_EQ(ASCIIToUTF16("30 Apr 2011"), TimeFormatWithPattern(time, "yMMMd"));
+  EXPECT_EQ(ASCIIToUTF16("30 April, 15:42:07"),
+            TimeFormatWithPattern(time, "MMMMdjmmss"));
+
+  i18n::SetICUDefaultLocale("ja_JP");
+  EXPECT_EQ(UTF8ToUTF16(u8"2011年4月30日"),
+            TimeFormatWithPattern(time, "yMMMd"));
+  EXPECT_EQ(UTF8ToUTF16(u8"4月30日 15:42:07"),
+            TimeFormatWithPattern(time, "MMMMdjmmss"));
+}
+
+TEST(TimeFormattingTest, TimeDurationFormat) {
+  test::ScopedRestoreICUDefaultLocale restore_locale;
+  TimeDelta delta = TimeDelta::FromMinutes(15 * 60 + 42);
+
+  // US English.
+  i18n::SetICUDefaultLocale("en_US");
+  EXPECT_EQ(ASCIIToUTF16("15 hours, 42 minutes"),
+            TimeDurationFormatString(delta, DURATION_WIDTH_WIDE));
+  EXPECT_EQ(ASCIIToUTF16("15 hr, 42 min"),
+            TimeDurationFormatString(delta, DURATION_WIDTH_SHORT));
+  EXPECT_EQ(ASCIIToUTF16("15h 42m"),
+            TimeDurationFormatString(delta, DURATION_WIDTH_NARROW));
+  EXPECT_EQ(ASCIIToUTF16("15:42"),
+            TimeDurationFormatString(delta, DURATION_WIDTH_NUMERIC));
+
+  // Danish, with Latin alphabet but different abbreviations and punctuation.
+  i18n::SetICUDefaultLocale("da");
+  EXPECT_EQ(ASCIIToUTF16("15 timer og 42 minutter"),
+            TimeDurationFormatString(delta, DURATION_WIDTH_WIDE));
+  EXPECT_EQ(ASCIIToUTF16("15 t og 42 min."),
+            TimeDurationFormatString(delta, DURATION_WIDTH_SHORT));
+  EXPECT_EQ(ASCIIToUTF16("15 t og 42 min"),
+            TimeDurationFormatString(delta, DURATION_WIDTH_NARROW));
+  EXPECT_EQ(ASCIIToUTF16("15.42"),
+            TimeDurationFormatString(delta, DURATION_WIDTH_NUMERIC));
+
+  // Persian, with non-Arabic numbers.
+  i18n::SetICUDefaultLocale("fa");
+  string16 fa_wide = UTF8ToUTF16(
+      u8"\u06f1\u06f5 \u0633\u0627\u0639\u062a \u0648 \u06f4\u06f2 \u062f\u0642"
+      u8"\u06cc\u0642\u0647");
+  string16 fa_short = UTF8ToUTF16(
+      u8"\u06f1\u06f5 \u0633\u0627\u0639\u062a\u060c\u200f \u06f4\u06f2 \u062f"
+      u8"\u0642\u06cc\u0642\u0647");
+  string16 fa_narrow = UTF8ToUTF16(
+      u8"\u06f1\u06f5 \u0633\u0627\u0639\u062a \u06f4\u06f2 \u062f\u0642\u06cc"
+      u8"\u0642\u0647");
+  string16 fa_numeric = UTF8ToUTF16(u8"\u06f1\u06f5:\u06f4\u06f2");
+  EXPECT_EQ(fa_wide, TimeDurationFormatString(delta, DURATION_WIDTH_WIDE));
+  EXPECT_EQ(fa_short, TimeDurationFormatString(delta, DURATION_WIDTH_SHORT));
+  EXPECT_EQ(fa_narrow, TimeDurationFormatString(delta, DURATION_WIDTH_NARROW));
+  EXPECT_EQ(fa_numeric,
+            TimeDurationFormatString(delta, DURATION_WIDTH_NUMERIC));
+}
+
+TEST(TimeFormattingTest, TimeDurationFormatWithSeconds) {
+  test::ScopedRestoreICUDefaultLocale restore_locale;
+
+  // US English.
+  i18n::SetICUDefaultLocale("en_US");
+
+  // Test different formats.
+  TimeDelta delta = TimeDelta::FromSeconds(15 * 3600 + 42 * 60 + 30);
+  EXPECT_EQ(ASCIIToUTF16("15 hours, 42 minutes, 30 seconds"),
+            TimeDurationFormatWithSecondsString(delta, DURATION_WIDTH_WIDE));
+  EXPECT_EQ(ASCIIToUTF16("15 hr, 42 min, 30 sec"),
+            TimeDurationFormatWithSecondsString(delta, DURATION_WIDTH_SHORT));
+  EXPECT_EQ(ASCIIToUTF16("15h 42m 30s"),
+            TimeDurationFormatWithSecondsString(delta, DURATION_WIDTH_NARROW));
+  EXPECT_EQ(ASCIIToUTF16("15:42:30"),
+            TimeDurationFormatWithSecondsString(delta, DURATION_WIDTH_NUMERIC));
+
+  // Test edge case when hour >= 100.
+  delta = TimeDelta::FromSeconds(125 * 3600 + 42 * 60 + 30);
+  EXPECT_EQ(ASCIIToUTF16("125 hours, 42 minutes, 30 seconds"),
+            TimeDurationFormatWithSecondsString(delta, DURATION_WIDTH_WIDE));
+  EXPECT_EQ(ASCIIToUTF16("125 hr, 42 min, 30 sec"),
+            TimeDurationFormatWithSecondsString(delta, DURATION_WIDTH_SHORT));
+  EXPECT_EQ(ASCIIToUTF16("125h 42m 30s"),
+            TimeDurationFormatWithSecondsString(delta, DURATION_WIDTH_NARROW));
+
+  // Test edge case when minute = 0.
+  delta = TimeDelta::FromSeconds(15 * 3600 + 0 * 60 + 30);
+  EXPECT_EQ(ASCIIToUTF16("15 hours, 0 minutes, 30 seconds"),
+            TimeDurationFormatWithSecondsString(delta, DURATION_WIDTH_WIDE));
+  EXPECT_EQ(ASCIIToUTF16("15 hr, 0 min, 30 sec"),
+            TimeDurationFormatWithSecondsString(delta, DURATION_WIDTH_SHORT));
+  EXPECT_EQ(ASCIIToUTF16("15h 0m 30s"),
+            TimeDurationFormatWithSecondsString(delta, DURATION_WIDTH_NARROW));
+  EXPECT_EQ(ASCIIToUTF16("15:00:30"),
+            TimeDurationFormatWithSecondsString(delta, DURATION_WIDTH_NUMERIC));
+
+  // Test edge case when second = 0.
+  delta = TimeDelta::FromSeconds(15 * 3600 + 42 * 60 + 0);
+  EXPECT_EQ(ASCIIToUTF16("15 hours, 42 minutes, 0 seconds"),
+            TimeDurationFormatWithSecondsString(delta, DURATION_WIDTH_WIDE));
+  EXPECT_EQ(ASCIIToUTF16("15 hr, 42 min, 0 sec"),
+            TimeDurationFormatWithSecondsString(delta, DURATION_WIDTH_SHORT));
+  EXPECT_EQ(ASCIIToUTF16("15h 42m 0s"),
+            TimeDurationFormatWithSecondsString(delta, DURATION_WIDTH_NARROW));
+  EXPECT_EQ(ASCIIToUTF16("15:42:00"),
+            TimeDurationFormatWithSecondsString(delta, DURATION_WIDTH_NUMERIC));
+}
+
+TEST(TimeFormattingTest, TimeIntervalFormat) {
+  test::ScopedRestoreICUDefaultLocale restore_locale;
+  i18n::SetICUDefaultLocale("en_US");
+  ScopedRestoreDefaultTimezone la_time("America/Los_Angeles");
+
+  const Time::Exploded kTestIntervalEndTimeExploded = {
+      2011, 5,  6, 28,  // Sat, May 28, 2012
+      22,   42, 7, 0    // 22:42:07.000
+  };
+
+  Time begin_time;
+  EXPECT_TRUE(Time::FromUTCExploded(kTestDateTimeExploded, &begin_time));
+  Time end_time;
+  EXPECT_TRUE(Time::FromUTCExploded(kTestIntervalEndTimeExploded, &end_time));
+
+  EXPECT_EQ(
+      UTF8ToUTF16(u8"Saturday, April 30 – Saturday, May 28"),
+      DateIntervalFormat(begin_time, end_time, DATE_FORMAT_MONTH_WEEKDAY_DAY));
+
+  const Time::Exploded kTestIntervalBeginTimeExploded = {
+      2011, 5,  1, 16,  // Mon, May 16, 2012
+      22,   42, 7, 0    // 22:42:07.000
+  };
+  EXPECT_TRUE(
+      Time::FromUTCExploded(kTestIntervalBeginTimeExploded, &begin_time));
+  EXPECT_EQ(
+      UTF8ToUTF16(u8"Monday, May 16 – Saturday, May 28"),
+      DateIntervalFormat(begin_time, end_time, DATE_FORMAT_MONTH_WEEKDAY_DAY));
+
+  i18n::SetICUDefaultLocale("en_GB");
+  EXPECT_EQ(
+      UTF8ToUTF16(u8"Monday 16 – Saturday 28 May"),
+      DateIntervalFormat(begin_time, end_time, DATE_FORMAT_MONTH_WEEKDAY_DAY));
+
+  i18n::SetICUDefaultLocale("ja");
+  EXPECT_EQ(
+      UTF8ToUTF16(u8"5月16日(月曜日)~28日(土曜日)"),
+      DateIntervalFormat(begin_time, end_time, DATE_FORMAT_MONTH_WEEKDAY_DAY));
+}
+
+}  // namespace
+}  // namespace base
diff --git a/base/i18n/timezone.cc b/base/i18n/timezone.cc
new file mode 100644
index 0000000..8624e07
--- /dev/null
+++ b/base/i18n/timezone.cc
@@ -0,0 +1,34 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/i18n/timezone.h"
+
+#include <memory>
+#include <string>
+
+#include "third_party/icu/source/common/unicode/unistr.h"
+#include "third_party/icu/source/i18n/unicode/timezone.h"
+
+namespace base {
+
+std::string CountryCodeForCurrentTimezone() {
+  std::unique_ptr<icu::TimeZone> zone(icu::TimeZone::createDefault());
+  icu::UnicodeString id;
+  // ICU returns '001' (world) for Etc/GMT. Preserve the old behavior
+  // only for Etc/GMT while returning an empty string for Etc/UTC and
+  // Etc/UCT because they're less likely to be chosen by mistake in UK in
+  // place of Europe/London (Briitish Time).
+  if (zone->getID(id) == UNICODE_STRING_SIMPLE("Etc/GMT"))
+    return "GB";
+  char region_code[4];
+  UErrorCode status = U_ZERO_ERROR;
+  int length = zone->getRegion(id, region_code, 4, status);
+  // Return an empty string if region_code is a 3-digit numeric code such
+  // as 001 (World) for Etc/UTC, Etc/UCT.
+  return (U_SUCCESS(status) && length == 2)
+             ? std::string(region_code, static_cast<size_t>(length))
+             : std::string();
+}
+
+}  // namespace base
diff --git a/base/i18n/timezone.h b/base/i18n/timezone.h
new file mode 100644
index 0000000..7557d44
--- /dev/null
+++ b/base/i18n/timezone.h
@@ -0,0 +1,24 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_I18N_TIMEZONE_H_
+#define BASE_I18N_TIMEZONE_H_
+
+#include <string>
+
+#include "base/i18n/base_i18n_export.h"
+
+namespace base {
+
+// Checks the system timezone and turns it into a two-character ISO 3166 country
+// code. This may fail (for example, it used to always fail on Android), in
+// which case it will return an empty string. It'll also return an empty string
+// when the timezone is Etc/UTC or Etc/UCT, but will return 'GB" for Etc/GMT
+// because people in the UK tends to select Etc/GMT by mistake instead of
+// Europe/London (British Time).
+BASE_I18N_EXPORT std::string CountryCodeForCurrentTimezone();
+
+}  // namespace base
+
+#endif  // BASE_I18N_TIMEZONE_H_
diff --git a/base/i18n/timezone_unittest.cc b/base/i18n/timezone_unittest.cc
new file mode 100644
index 0000000..57467dc
--- /dev/null
+++ b/base/i18n/timezone_unittest.cc
@@ -0,0 +1,27 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/i18n/timezone.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace {
+
+TEST(TimezoneTest, CountryCodeForCurrentTimezone) {
+  std::string country_code = CountryCodeForCurrentTimezone();
+  // On some systems (such as Android or some flavors of Linux), ICU may come up
+  // empty. With https://chromium-review.googlesource.com/c/512282/ , ICU will
+  // not fail any more. See also http://bugs.icu-project.org/trac/ticket/13208 .
+  // Even with that, ICU returns '001' (world) for region-agnostic timezones
+  // such as Etc/UTC and |CountryCodeForCurrentTimezone| returns an empty
+  // string so that the next fallback can be tried by a customer.
+  // TODO(jshin): Revise this to test for actual timezones using
+  // use ScopedRestoreICUDefaultTimezone.
+  if (!country_code.empty())
+    EXPECT_EQ(2U, country_code.size()) << "country_code = " << country_code;
+}
+
+}  // namespace
+}  // namespace base
diff --git a/base/i18n/unicodestring.h b/base/i18n/unicodestring.h
new file mode 100644
index 0000000..b62c526
--- /dev/null
+++ b/base/i18n/unicodestring.h
@@ -0,0 +1,32 @@
+// Copyright (c) 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_I18N_UNICODESTRING_H_
+#define BASE_I18N_UNICODESTRING_H_
+
+#include "base/strings/string16.h"
+#include "third_party/icu/source/common/unicode/unistr.h"
+#include "third_party/icu/source/common/unicode/uvernum.h"
+
+#if U_ICU_VERSION_MAJOR_NUM >= 59
+#include "third_party/icu/source/common/unicode/char16ptr.h"
+#endif
+
+namespace base {
+namespace i18n {
+
+inline string16 UnicodeStringToString16(const icu::UnicodeString& unistr) {
+#if U_ICU_VERSION_MAJOR_NUM >= 59
+  return base::string16(icu::toUCharPtr(unistr.getBuffer()),
+                        static_cast<size_t>(unistr.length()));
+#else
+  return base::string16(unistr.getBuffer(),
+                        static_cast<size_t>(unistr.length()));
+#endif
+}
+
+}  // namespace i18n
+}  // namespace base
+
+#endif  // BASE_UNICODESTRING_H_
diff --git a/base/i18n/utf8_validator_tables.cc b/base/i18n/utf8_validator_tables.cc
new file mode 100644
index 0000000..913afc7
--- /dev/null
+++ b/base/i18n/utf8_validator_tables.cc
@@ -0,0 +1,55 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file is auto-generated by build_utf8_validator_tables.
+// DO NOT EDIT.
+
+#include "base/i18n/utf8_validator_tables.h"
+
+namespace base {
+namespace internal {
+
+const uint8_t kUtf8ValidatorTables[] = {
+    // State 0, offset 0x00
+    0x00, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,  // 0x08
+    0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,  // 0x10
+    0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,  // 0x18
+    0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,  // 0x20
+    0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,  // 0x28
+    0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,  // 0x30
+    0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,  // 0x38
+    0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,  // 0x40
+    0x81, 0x81, 0x81, 0x83, 0x83, 0x83, 0x83, 0x83,  // 0x48
+    0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,  // 0x50
+    0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,  // 0x58
+    0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,  // 0x60
+    0x83, 0x86, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,  // 0x68
+    0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8e, 0x8b,  // 0x70
+    0x8b, 0x93, 0x9c, 0x9c, 0x9c, 0x9f, 0x81, 0x81,  // 0x78
+    0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,  // 0x80
+    0x81,                                            // 0x81
+    // State 1, offset 0x81
+    0x07, 0x81,                                      // 0x83
+    // State 2, offset 0x83
+    0x06, 0x00, 0x81,                                // 0x86
+    // State 3, offset 0x86
+    0x05, 0x81, 0x83, 0x81, 0x81,                    // 0x8b
+    // State 4, offset 0x8b
+    0x06, 0x83, 0x81,                                // 0x8e
+    // State 5, offset 0x8e
+    0x05, 0x83, 0x81, 0x81, 0x81,                    // 0x93
+    // State 6, offset 0x93
+    0x04, 0x81, 0x8b, 0x8b, 0x8b, 0x81, 0x81, 0x81,  // 0x9b
+    0x81,                                            // 0x9c
+    // State 7, offset 0x9c
+    0x06, 0x8b, 0x81,                                // 0x9f
+    // State 8, offset 0x9f
+    0x04, 0x8b, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,  // 0xa7
+    0x81,                                            // 0xa8
+};
+
+const size_t kUtf8ValidatorTablesSize = arraysize(kUtf8ValidatorTables);
+
+}  // namespace internal
+}  // namespace base
diff --git a/base/i18n/utf8_validator_tables.h b/base/i18n/utf8_validator_tables.h
new file mode 100644
index 0000000..939616b
--- /dev/null
+++ b/base/i18n/utf8_validator_tables.h
@@ -0,0 +1,32 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_I18N_UTF8_VALIDATOR_TABLES_H_
+#define BASE_I18N_UTF8_VALIDATOR_TABLES_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "base/macros.h"
+
+namespace base {
+namespace internal {
+
+// The tables for all states; a list of entries of the form (right_shift,
+// next_state, next_state, ....). The right_shifts are used to reduce the
+// overall size of the table. The table only covers bytes in the range
+// [0x80, 0xFF] to save space.
+extern const uint8_t kUtf8ValidatorTables[];
+
+extern const size_t kUtf8ValidatorTablesSize;
+
+// The offset of the INVALID state in kUtf8ValidatorTables.
+enum {
+  I18N_UTF8_VALIDATOR_INVALID_INDEX = 129
+};
+
+}  // namespace internal
+}  // namespace base
+
+#endif  // BASE_I18N_UTF8_VALIDATOR_TABLES_H_
diff --git a/base/json/json_perftest.cc b/base/json/json_perftest.cc
new file mode 100644
index 0000000..fc05bdc
--- /dev/null
+++ b/base/json/json_perftest.cc
@@ -0,0 +1,84 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/json/json_reader.h"
+#include "base/json/json_writer.h"
+#include "base/memory/ptr_util.h"
+#include "base/time/time.h"
+#include "base/values.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/perf/perf_test.h"
+
+namespace base {
+
+namespace {
+// Generates a simple dictionary value with simple data types, a string and a
+// list.
+std::unique_ptr<DictionaryValue> GenerateDict() {
+  auto root = std::make_unique<DictionaryValue>();
+  root->SetDouble("Double", 3.141);
+  root->SetBoolean("Bool", true);
+  root->SetInteger("Int", 42);
+  root->SetString("String", "Foo");
+
+  auto list = std::make_unique<ListValue>();
+  list->Set(0, std::make_unique<Value>(2.718));
+  list->Set(1, std::make_unique<Value>(false));
+  list->Set(2, std::make_unique<Value>(123));
+  list->Set(3, std::make_unique<Value>("Bar"));
+  root->Set("List", std::move(list));
+
+  return root;
+}
+
+// Generates a tree-like dictionary value with a size of O(breadth ** depth).
+std::unique_ptr<DictionaryValue> GenerateLayeredDict(int breadth, int depth) {
+  if (depth == 1)
+    return GenerateDict();
+
+  auto root = GenerateDict();
+  auto next = GenerateLayeredDict(breadth, depth - 1);
+
+  for (int i = 0; i < breadth; ++i) {
+    root->Set("Dict" + std::to_string(i), next->CreateDeepCopy());
+  }
+
+  return root;
+}
+
+}  // namespace
+
+class JSONPerfTest : public testing::Test {
+ public:
+  void TestWriteAndRead(int breadth, int depth) {
+    std::string description = "Breadth: " + std::to_string(breadth) +
+                              ", Depth: " + std::to_string(depth);
+    auto dict = GenerateLayeredDict(breadth, depth);
+    std::string json;
+
+    TimeTicks start_write = TimeTicks::Now();
+    JSONWriter::Write(*dict, &json);
+    TimeTicks end_write = TimeTicks::Now();
+    perf_test::PrintResult("Write", "", description,
+                           (end_write - start_write).InMillisecondsF(), "ms",
+                           true);
+
+    TimeTicks start_read = TimeTicks::Now();
+    JSONReader::Read(json);
+    TimeTicks end_read = TimeTicks::Now();
+    perf_test::PrintResult("Read", "", description,
+                           (end_read - start_read).InMillisecondsF(), "ms",
+                           true);
+  }
+};
+
+TEST_F(JSONPerfTest, StressTest) {
+  for (int i = 0; i < 4; ++i) {
+    for (int j = 0; j < 12; ++j) {
+      TestWriteAndRead(i + 1, j + 1);
+    }
+  }
+}
+
+}  // namespace base
diff --git a/base/linux_util.cc b/base/linux_util.cc
new file mode 100644
index 0000000..ddf848e
--- /dev/null
+++ b/base/linux_util.cc
@@ -0,0 +1,226 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/linux_util.h"
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+#include <vector>
+
+#include "base/command_line.h"
+#include "base/files/file_util.h"
+#include "base/memory/singleton.h"
+#include "base/process/launch.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_tokenizer.h"
+#include "base/strings/string_util.h"
+#include "base/synchronization/lock.h"
+#include "build/build_config.h"
+
+namespace {
+
+// Not needed for OS_CHROMEOS.
+#if defined(OS_LINUX)
+enum LinuxDistroState {
+  STATE_DID_NOT_CHECK  = 0,
+  STATE_CHECK_STARTED  = 1,
+  STATE_CHECK_FINISHED = 2,
+};
+
+// Helper class for GetLinuxDistro().
+class LinuxDistroHelper {
+ public:
+  // Retrieves the Singleton.
+  static LinuxDistroHelper* GetInstance() {
+    return base::Singleton<LinuxDistroHelper>::get();
+  }
+
+  // The simple state machine goes from:
+  // STATE_DID_NOT_CHECK -> STATE_CHECK_STARTED -> STATE_CHECK_FINISHED.
+  LinuxDistroHelper() : state_(STATE_DID_NOT_CHECK) {}
+  ~LinuxDistroHelper() = default;
+
+  // Retrieve the current state, if we're in STATE_DID_NOT_CHECK,
+  // we automatically move to STATE_CHECK_STARTED so nobody else will
+  // do the check.
+  LinuxDistroState State() {
+    base::AutoLock scoped_lock(lock_);
+    if (STATE_DID_NOT_CHECK == state_) {
+      state_ = STATE_CHECK_STARTED;
+      return STATE_DID_NOT_CHECK;
+    }
+    return state_;
+  }
+
+  // Indicate the check finished, move to STATE_CHECK_FINISHED.
+  void CheckFinished() {
+    base::AutoLock scoped_lock(lock_);
+    DCHECK_EQ(STATE_CHECK_STARTED, state_);
+    state_ = STATE_CHECK_FINISHED;
+  }
+
+ private:
+  base::Lock lock_;
+  LinuxDistroState state_;
+};
+#endif  // if defined(OS_LINUX)
+
+bool GetTasksForProcess(pid_t pid, std::vector<pid_t>* tids) {
+  char buf[256];
+  snprintf(buf, sizeof(buf), "/proc/%d/task", pid);
+
+  DIR* task = opendir(buf);
+  if (!task) {
+    DLOG(WARNING) << "Cannot open " << buf;
+    return false;
+  }
+
+  struct dirent* dent;
+  while ((dent = readdir(task))) {
+    char* endptr;
+    const unsigned long int tid_ul = strtoul(dent->d_name, &endptr, 10);
+    if (tid_ul == ULONG_MAX || *endptr)
+      continue;
+    tids->push_back(tid_ul);
+  }
+  closedir(task);
+  return true;
+}
+
+}  // namespace
+
+namespace base {
+
+// Account for the terminating null character.
+static const int kDistroSize = 128 + 1;
+
+// We use this static string to hold the Linux distro info. If we
+// crash, the crash handler code will send this in the crash dump.
+char g_linux_distro[kDistroSize] =
+#if defined(OS_CHROMEOS)
+    "CrOS";
+#elif defined(OS_ANDROID)
+    "Android";
+#else  // if defined(OS_LINUX)
+    "Unknown";
+#endif
+
+std::string GetLinuxDistro() {
+#if defined(OS_CHROMEOS) || defined(OS_ANDROID)
+  return g_linux_distro;
+#elif defined(OS_LINUX)
+  LinuxDistroHelper* distro_state_singleton = LinuxDistroHelper::GetInstance();
+  LinuxDistroState state = distro_state_singleton->State();
+  if (STATE_CHECK_FINISHED == state)
+    return g_linux_distro;
+  if (STATE_CHECK_STARTED == state)
+    return "Unknown"; // Don't wait for other thread to finish.
+  DCHECK_EQ(state, STATE_DID_NOT_CHECK);
+  // We do this check only once per process. If it fails, there's
+  // little reason to believe it will work if we attempt to run
+  // lsb_release again.
+  std::vector<std::string> argv;
+  argv.push_back("lsb_release");
+  argv.push_back("-d");
+  std::string output;
+  GetAppOutput(CommandLine(argv), &output);
+  if (output.length() > 0) {
+    // lsb_release -d should return: Description:<tab>Distro Info
+    const char field[] = "Description:\t";
+    if (output.compare(0, strlen(field), field) == 0) {
+      SetLinuxDistro(output.substr(strlen(field)));
+    }
+  }
+  distro_state_singleton->CheckFinished();
+  return g_linux_distro;
+#else
+  NOTIMPLEMENTED();
+  return "Unknown";
+#endif
+}
+
+void SetLinuxDistro(const std::string& distro) {
+  std::string trimmed_distro;
+  TrimWhitespaceASCII(distro, TRIM_ALL, &trimmed_distro);
+  strlcpy(g_linux_distro, trimmed_distro.c_str(), kDistroSize);
+}
+
+pid_t FindThreadIDWithSyscall(pid_t pid, const std::string& expected_data,
+                              bool* syscall_supported) {
+  if (syscall_supported != nullptr)
+    *syscall_supported = false;
+
+  std::vector<pid_t> tids;
+  if (!GetTasksForProcess(pid, &tids))
+    return -1;
+
+  std::unique_ptr<char[]> syscall_data(new char[expected_data.length()]);
+  for (pid_t tid : tids) {
+    char buf[256];
+    snprintf(buf, sizeof(buf), "/proc/%d/task/%d/syscall", pid, tid);
+    int fd = open(buf, O_RDONLY);
+    if (fd < 0)
+      continue;
+    if (syscall_supported != nullptr)
+      *syscall_supported = true;
+    bool read_ret = ReadFromFD(fd, syscall_data.get(), expected_data.length());
+    close(fd);
+    if (!read_ret)
+      continue;
+
+    if (0 == strncmp(expected_data.c_str(), syscall_data.get(),
+                     expected_data.length())) {
+      return tid;
+    }
+  }
+  return -1;
+}
+
+pid_t FindThreadID(pid_t pid, pid_t ns_tid, bool* ns_pid_supported) {
+  if (ns_pid_supported)
+    *ns_pid_supported = false;
+
+  std::vector<pid_t> tids;
+  if (!GetTasksForProcess(pid, &tids))
+    return -1;
+
+  for (pid_t tid : tids) {
+    char buf[256];
+    snprintf(buf, sizeof(buf), "/proc/%d/task/%d/status", pid, tid);
+    std::string status;
+    if (!ReadFileToString(FilePath(buf), &status))
+      return -1;
+    StringTokenizer tokenizer(status, "\n");
+    while (tokenizer.GetNext()) {
+      StringPiece value_str(tokenizer.token_piece());
+      if (!value_str.starts_with("NSpid"))
+        continue;
+      if (ns_pid_supported)
+        *ns_pid_supported = true;
+      std::vector<StringPiece> split_value_str = SplitStringPiece(
+          value_str, "\t", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
+      DCHECK_GE(split_value_str.size(), 2u);
+      int value;
+      // The last value in the list is the PID in the namespace.
+      if (StringToInt(split_value_str.back(), &value) && value == ns_tid) {
+        // The second value in the list is the real PID.
+        if (StringToInt(split_value_str[1], &value))
+          return value;
+      }
+      break;
+    }
+  }
+  return -1;
+}
+
+}  // namespace base
diff --git a/base/linux_util.h b/base/linux_util.h
new file mode 100644
index 0000000..272e06b
--- /dev/null
+++ b/base/linux_util.h
@@ -0,0 +1,44 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_LINUX_UTIL_H_
+#define BASE_LINUX_UTIL_H_
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <string>
+
+#include "base/base_export.h"
+
+namespace base {
+
+// This is declared here so the crash reporter can access the memory directly
+// in compromised context without going through the standard library.
+BASE_EXPORT extern char g_linux_distro[];
+
+// Get the Linux Distro if we can, or return "Unknown".
+BASE_EXPORT std::string GetLinuxDistro();
+
+// Set the Linux Distro string.
+BASE_EXPORT void SetLinuxDistro(const std::string& distro);
+
+// For a given process |pid|, look through all its threads and find the first
+// thread with /proc/[pid]/task/[thread_id]/syscall whose first N bytes matches
+// |expected_data|, where N is the length of |expected_data|.
+// Returns the thread id or -1 on error.  If |syscall_supported| is
+// set to false the kernel does not support syscall in procfs.
+BASE_EXPORT pid_t FindThreadIDWithSyscall(pid_t pid,
+                                          const std::string& expected_data,
+                                          bool* syscall_supported);
+
+// For a given process |pid|, look through all its threads and find the first
+// thread with /proc/[pid]/task/[thread_id]/status where NSpid matches |ns_tid|.
+// Returns the thread id or -1 on error.  If |ns_pid_supported| is
+// set to false the kernel does not support NSpid in procfs.
+BASE_EXPORT pid_t FindThreadID(pid_t pid, pid_t ns_tid, bool* ns_pid_supported);
+
+}  // namespace base
+
+#endif  // BASE_LINUX_UTIL_H_
diff --git a/base/memory/discardable_memory.cc b/base/memory/discardable_memory.cc
new file mode 100644
index 0000000..f0730aa
--- /dev/null
+++ b/base/memory/discardable_memory.cc
@@ -0,0 +1,13 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/memory/discardable_memory.h"
+
+namespace base {
+
+DiscardableMemory::DiscardableMemory() = default;
+
+DiscardableMemory::~DiscardableMemory() = default;
+
+}  // namespace base
diff --git a/base/memory/discardable_memory.h b/base/memory/discardable_memory.h
new file mode 100644
index 0000000..5c632d1
--- /dev/null
+++ b/base/memory/discardable_memory.h
@@ -0,0 +1,78 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_MEMORY_DISCARDABLE_MEMORY_H_
+#define BASE_MEMORY_DISCARDABLE_MEMORY_H_
+
+#include "base/base_export.h"
+#include "base/compiler_specific.h"
+
+namespace base {
+
+namespace trace_event {
+class MemoryAllocatorDump;
+class ProcessMemoryDump;
+}
+
+// Discardable memory is used to cache large objects without worrying about
+// blowing out memory, both on mobile devices where there is no swap, and
+// desktop devices where unused free memory should be used to help the user
+// experience. This is preferable to releasing memory in response to an OOM
+// signal because it is simpler and provides system-wide management of
+// purgable memory, though it has less flexibility as to which objects get
+// discarded.
+//
+// Discardable memory has two states: locked and unlocked. While the memory is
+// locked, it will not be discarded. Unlocking the memory allows the
+// discardable memory system and the OS to reclaim it if needed. Locks do not
+// nest.
+//
+// Notes:
+//   - The paging behavior of memory while it is locked is not specified. While
+//     mobile platforms will not swap it out, it may qualify for swapping
+//     on desktop platforms. It is not expected that this will matter, as the
+//     preferred pattern of usage for DiscardableMemory is to lock down the
+//     memory, use it as quickly as possible, and then unlock it.
+//   - Because of memory alignment, the amount of memory allocated can be
+//     larger than the requested memory size. It is not very efficient for
+//     small allocations.
+//   - A discardable memory instance is not thread safe. It is the
+//     responsibility of users of discardable memory to ensure there are no
+//     races.
+//
+class BASE_EXPORT DiscardableMemory {
+ public:
+  DiscardableMemory();
+  virtual ~DiscardableMemory();
+
+  // Locks the memory so that it will not be purged by the system. Returns
+  // true on success. If the return value is false then this object should be
+  // discarded and a new one should be created.
+  virtual bool Lock() WARN_UNUSED_RESULT = 0;
+
+  // Unlocks the memory so that it can be purged by the system. Must be called
+  // after every successful lock call.
+  virtual void Unlock() = 0;
+
+  // Returns the memory address held by this object. The object must be locked
+  // before calling this.
+  virtual void* data() const = 0;
+
+  // Handy method to simplify calling data() with a reinterpret_cast.
+  template<typename T> T* data_as() const {
+    return reinterpret_cast<T*>(data());
+  }
+
+  // Used for dumping the statistics of discardable memory allocated in tracing.
+  // Returns a new MemoryAllocatorDump in the |pmd| with the size of the
+  // discardable memory. The MemoryAllocatorDump created is owned by |pmd|. See
+  // ProcessMemoryDump::CreateAllocatorDump.
+  virtual trace_event::MemoryAllocatorDump* CreateMemoryAllocatorDump(
+      const char* name,
+      trace_event::ProcessMemoryDump* pmd) const = 0;
+};
+
+}  // namespace base
+
+#endif  // BASE_MEMORY_DISCARDABLE_MEMORY_H_
diff --git a/base/memory/discardable_memory_allocator.cc b/base/memory/discardable_memory_allocator.cc
new file mode 100644
index 0000000..3dbb276
--- /dev/null
+++ b/base/memory/discardable_memory_allocator.cc
@@ -0,0 +1,29 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/memory/discardable_memory_allocator.h"
+
+#include "base/logging.h"
+
+namespace base {
+namespace {
+
+DiscardableMemoryAllocator* g_discardable_allocator = nullptr;
+
+}  // namespace
+
+// static
+void DiscardableMemoryAllocator::SetInstance(
+    DiscardableMemoryAllocator* allocator) {
+  DCHECK(!allocator || !g_discardable_allocator);
+  g_discardable_allocator = allocator;
+}
+
+// static
+DiscardableMemoryAllocator* DiscardableMemoryAllocator::GetInstance() {
+  DCHECK(g_discardable_allocator);
+  return g_discardable_allocator;
+}
+
+}  // namespace base
diff --git a/base/memory/discardable_memory_allocator.h b/base/memory/discardable_memory_allocator.h
new file mode 100644
index 0000000..8d74b16
--- /dev/null
+++ b/base/memory/discardable_memory_allocator.h
@@ -0,0 +1,38 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_MEMORY_DISCARDABLE_MEMORY_ALLOCATOR_H_
+#define BASE_MEMORY_DISCARDABLE_MEMORY_ALLOCATOR_H_
+
+#include <stddef.h>
+
+#include <memory>
+
+#include "base/base_export.h"
+
+namespace base {
+class DiscardableMemory;
+
+class BASE_EXPORT DiscardableMemoryAllocator {
+ public:
+  // Returns the allocator instance.
+  static DiscardableMemoryAllocator* GetInstance();
+
+  // Sets the allocator instance. Can only be called once, e.g. on startup.
+  // Ownership of |instance| remains with the caller.
+  static void SetInstance(DiscardableMemoryAllocator* allocator);
+
+  // Giant WARNING: Discardable[Shared]Memory is only implemented on Android. On
+  // non-Android platforms, it behaves exactly the same as SharedMemory.
+  // See LockPages() in discardable_shared_memory.cc.
+  virtual std::unique_ptr<DiscardableMemory> AllocateLockedDiscardableMemory(
+      size_t size) = 0;
+
+ protected:
+  virtual ~DiscardableMemoryAllocator() = default;
+};
+
+}  // namespace base
+
+#endif  // BASE_MEMORY_DISCARDABLE_MEMORY_ALLOCATOR_H_
diff --git a/base/memory/discardable_shared_memory.cc b/base/memory/discardable_shared_memory.cc
new file mode 100644
index 0000000..3b6b4db
--- /dev/null
+++ b/base/memory/discardable_shared_memory.cc
@@ -0,0 +1,514 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/memory/discardable_shared_memory.h"
+
+#include <stdint.h>
+
+#include <algorithm>
+
+#include "base/atomicops.h"
+#include "base/bits.h"
+#include "base/logging.h"
+#include "base/memory/shared_memory_tracker.h"
+#include "base/numerics/safe_math.h"
+#include "base/process/process_metrics.h"
+#include "base/trace_event/memory_allocator_dump.h"
+#include "base/trace_event/process_memory_dump.h"
+#include "build/build_config.h"
+
+#if defined(OS_POSIX) && !defined(OS_NACL)
+// For madvise() which is available on all POSIX compatible systems.
+#include <sys/mman.h>
+#endif
+
+#if defined(OS_ANDROID)
+#include "third_party/ashmem/ashmem.h"
+#endif
+
+#if defined(OS_WIN)
+#include <windows.h>
+#include "base/win/windows_version.h"
+#endif
+
+namespace base {
+namespace {
+
+// Use a machine-sized pointer as atomic type. It will use the Atomic32 or
+// Atomic64 routines, depending on the architecture.
+typedef intptr_t AtomicType;
+typedef uintptr_t UAtomicType;
+
+// Template specialization for timestamp serialization/deserialization. This
+// is used to serialize timestamps using Unix time on systems where AtomicType
+// does not have enough precision to contain a timestamp in the standard
+// serialized format.
+template <int>
+Time TimeFromWireFormat(int64_t value);
+template <int>
+int64_t TimeToWireFormat(Time time);
+
+// Serialize to Unix time when using 4-byte wire format.
+// Note: 19 January 2038, this will cease to work.
+template <>
+Time ALLOW_UNUSED_TYPE TimeFromWireFormat<4>(int64_t value) {
+  return value ? Time::UnixEpoch() + TimeDelta::FromSeconds(value) : Time();
+}
+template <>
+int64_t ALLOW_UNUSED_TYPE TimeToWireFormat<4>(Time time) {
+  return time > Time::UnixEpoch() ? (time - Time::UnixEpoch()).InSeconds() : 0;
+}
+
+// Standard serialization format when using 8-byte wire format.
+template <>
+Time ALLOW_UNUSED_TYPE TimeFromWireFormat<8>(int64_t value) {
+  return Time::FromInternalValue(value);
+}
+template <>
+int64_t ALLOW_UNUSED_TYPE TimeToWireFormat<8>(Time time) {
+  return time.ToInternalValue();
+}
+
+struct SharedState {
+  enum LockState { UNLOCKED = 0, LOCKED = 1 };
+
+  explicit SharedState(AtomicType ivalue) { value.i = ivalue; }
+  SharedState(LockState lock_state, Time timestamp) {
+    int64_t wire_timestamp = TimeToWireFormat<sizeof(AtomicType)>(timestamp);
+    DCHECK_GE(wire_timestamp, 0);
+    DCHECK_EQ(lock_state & ~1, 0);
+    value.u = (static_cast<UAtomicType>(wire_timestamp) << 1) | lock_state;
+  }
+
+  LockState GetLockState() const { return static_cast<LockState>(value.u & 1); }
+
+  Time GetTimestamp() const {
+    return TimeFromWireFormat<sizeof(AtomicType)>(value.u >> 1);
+  }
+
+  // Bit 1: Lock state. Bit is set when locked.
+  // Bit 2..sizeof(AtomicType)*8: Usage timestamp. NULL time when locked or
+  // purged.
+  union {
+    AtomicType i;
+    UAtomicType u;
+  } value;
+};
+
+// Shared state is stored at offset 0 in shared memory segments.
+SharedState* SharedStateFromSharedMemory(
+    const WritableSharedMemoryMapping& shared_memory) {
+  DCHECK(shared_memory.IsValid());
+  return static_cast<SharedState*>(shared_memory.memory());
+}
+
+// Round up |size| to a multiple of page size.
+size_t AlignToPageSize(size_t size) {
+  return bits::Align(size, base::GetPageSize());
+}
+
+}  // namespace
+
+DiscardableSharedMemory::DiscardableSharedMemory()
+    : mapped_size_(0), locked_page_count_(0) {
+}
+
+DiscardableSharedMemory::DiscardableSharedMemory(
+    UnsafeSharedMemoryRegion shared_memory_region)
+    : shared_memory_region_(std::move(shared_memory_region)),
+      mapped_size_(0),
+      locked_page_count_(0) {}
+
+DiscardableSharedMemory::~DiscardableSharedMemory() = default;
+
+bool DiscardableSharedMemory::CreateAndMap(size_t size) {
+  CheckedNumeric<size_t> checked_size = size;
+  checked_size += AlignToPageSize(sizeof(SharedState));
+  if (!checked_size.IsValid())
+    return false;
+
+  shared_memory_region_ =
+      UnsafeSharedMemoryRegion::Create(checked_size.ValueOrDie());
+
+  if (!shared_memory_region_.IsValid())
+    return false;
+
+  shared_memory_mapping_ = shared_memory_region_.Map();
+  if (!shared_memory_mapping_.IsValid())
+    return false;
+
+  mapped_size_ = shared_memory_mapping_.mapped_size() -
+                 AlignToPageSize(sizeof(SharedState));
+
+  locked_page_count_ = AlignToPageSize(mapped_size_) / base::GetPageSize();
+#if DCHECK_IS_ON()
+  for (size_t page = 0; page < locked_page_count_; ++page)
+    locked_pages_.insert(page);
+#endif
+
+  DCHECK(last_known_usage_.is_null());
+  SharedState new_state(SharedState::LOCKED, Time());
+  subtle::Release_Store(
+      &SharedStateFromSharedMemory(shared_memory_mapping_)->value.i,
+      new_state.value.i);
+  return true;
+}
+
+bool DiscardableSharedMemory::Map(size_t size) {
+  DCHECK(!shared_memory_mapping_.IsValid());
+  if (shared_memory_mapping_.IsValid())
+    return false;
+
+  shared_memory_mapping_ = shared_memory_region_.MapAt(
+      0, AlignToPageSize(sizeof(SharedState)) + size);
+  if (!shared_memory_mapping_.IsValid())
+    return false;
+
+  mapped_size_ = shared_memory_mapping_.mapped_size() -
+                 AlignToPageSize(sizeof(SharedState));
+
+  locked_page_count_ = AlignToPageSize(mapped_size_) / base::GetPageSize();
+#if DCHECK_IS_ON()
+  for (size_t page = 0; page < locked_page_count_; ++page)
+    locked_pages_.insert(page);
+#endif
+
+  return true;
+}
+
+bool DiscardableSharedMemory::Unmap() {
+  if (!shared_memory_mapping_.IsValid())
+    return false;
+
+  shared_memory_mapping_ = WritableSharedMemoryMapping();
+  locked_page_count_ = 0;
+#if DCHECK_IS_ON()
+  locked_pages_.clear();
+#endif
+  mapped_size_ = 0;
+  return true;
+}
+
+DiscardableSharedMemory::LockResult DiscardableSharedMemory::Lock(
+    size_t offset, size_t length) {
+  DCHECK_EQ(AlignToPageSize(offset), offset);
+  DCHECK_EQ(AlignToPageSize(length), length);
+
+  // Calls to this function must be synchronized properly.
+  DFAKE_SCOPED_LOCK(thread_collision_warner_);
+
+  DCHECK(shared_memory_mapping_.IsValid());
+
+  // We need to successfully acquire the platform independent lock before
+  // individual pages can be locked.
+  if (!locked_page_count_) {
+    // Return false when instance has been purged or not initialized properly
+    // by checking if |last_known_usage_| is NULL.
+    if (last_known_usage_.is_null())
+      return FAILED;
+
+    SharedState old_state(SharedState::UNLOCKED, last_known_usage_);
+    SharedState new_state(SharedState::LOCKED, Time());
+    SharedState result(subtle::Acquire_CompareAndSwap(
+        &SharedStateFromSharedMemory(shared_memory_mapping_)->value.i,
+        old_state.value.i, new_state.value.i));
+    if (result.value.u != old_state.value.u) {
+      // Update |last_known_usage_| in case the above CAS failed because of
+      // an incorrect timestamp.
+      last_known_usage_ = result.GetTimestamp();
+      return FAILED;
+    }
+  }
+
+  // Zero for length means "everything onward".
+  if (!length)
+    length = AlignToPageSize(mapped_size_) - offset;
+
+  size_t start = offset / base::GetPageSize();
+  size_t end = start + length / base::GetPageSize();
+  DCHECK_LE(start, end);
+  DCHECK_LE(end, AlignToPageSize(mapped_size_) / base::GetPageSize());
+
+  // Add pages to |locked_page_count_|.
+  // Note: Locking a page that is already locked is an error.
+  locked_page_count_ += end - start;
+#if DCHECK_IS_ON()
+  // Detect incorrect usage by keeping track of exactly what pages are locked.
+  for (auto page = start; page < end; ++page) {
+    auto result = locked_pages_.insert(page);
+    DCHECK(result.second);
+  }
+  DCHECK_EQ(locked_pages_.size(), locked_page_count_);
+#endif
+
+  // Always behave as if memory was purged when trying to lock a 0 byte segment.
+  if (!length)
+      return PURGED;
+
+#if defined(OS_ANDROID)
+  // Ensure that the platform won't discard the required pages.
+  return LockPages(shared_memory_region_,
+                   AlignToPageSize(sizeof(SharedState)) + offset, length);
+#elif defined(OS_MACOSX)
+  // On macOS, there is no mechanism to lock pages. However, we do need to call
+  // madvise(MADV_FREE_REUSE) in order to correctly update accounting for memory
+  // footprint via task_info().
+  //
+  // Note that calling madvise(MADV_FREE_REUSE) on regions that haven't had
+  // madvise(MADV_FREE_REUSABLE) called on them has no effect.
+  //
+  // Note that the corresponding call to MADV_FREE_REUSABLE is in Purge(), since
+  // that's where the memory is actually released, rather than Unlock(), which
+  // is a no-op on macOS.
+  //
+  // For more information, see
+  // https://bugs.chromium.org/p/chromium/issues/detail?id=823915.
+  if (madvise(reinterpret_cast<char*>(shared_memory_mapping_.memory()) +
+                  AlignToPageSize(sizeof(SharedState)),
+              AlignToPageSize(mapped_size_), MADV_FREE_REUSE))
+    ;
+  return DiscardableSharedMemory::SUCCESS;
+#else
+  return DiscardableSharedMemory::SUCCESS;
+#endif
+}
+
+void DiscardableSharedMemory::Unlock(size_t offset, size_t length) {
+  DCHECK_EQ(AlignToPageSize(offset), offset);
+  DCHECK_EQ(AlignToPageSize(length), length);
+
+  // Calls to this function must be synchronized properly.
+  DFAKE_SCOPED_LOCK(thread_collision_warner_);
+
+  // Passing zero for |length| means "everything onward". Note that |length| may
+  // still be zero after this calculation, e.g. if |mapped_size_| is zero.
+  if (!length)
+    length = AlignToPageSize(mapped_size_) - offset;
+
+  DCHECK(shared_memory_mapping_.IsValid());
+
+  // Allow the pages to be discarded by the platform, if supported.
+  UnlockPages(shared_memory_region_,
+              AlignToPageSize(sizeof(SharedState)) + offset, length);
+
+  size_t start = offset / base::GetPageSize();
+  size_t end = start + length / base::GetPageSize();
+  DCHECK_LE(start, end);
+  DCHECK_LE(end, AlignToPageSize(mapped_size_) / base::GetPageSize());
+
+  // Remove pages from |locked_page_count_|.
+  // Note: Unlocking a page that is not locked is an error.
+  DCHECK_GE(locked_page_count_, end - start);
+  locked_page_count_ -= end - start;
+#if DCHECK_IS_ON()
+  // Detect incorrect usage by keeping track of exactly what pages are locked.
+  for (auto page = start; page < end; ++page) {
+    auto erased_count = locked_pages_.erase(page);
+    DCHECK_EQ(1u, erased_count);
+  }
+  DCHECK_EQ(locked_pages_.size(), locked_page_count_);
+#endif
+
+  // Early out and avoid releasing the platform independent lock if some pages
+  // are still locked.
+  if (locked_page_count_)
+    return;
+
+  Time current_time = Now();
+  DCHECK(!current_time.is_null());
+
+  SharedState old_state(SharedState::LOCKED, Time());
+  SharedState new_state(SharedState::UNLOCKED, current_time);
+  // Note: timestamp cannot be NULL as that is a unique value used when
+  // locked or purged.
+  DCHECK(!new_state.GetTimestamp().is_null());
+  // Timestamp precision should at least be accurate to the second.
+  DCHECK_EQ((new_state.GetTimestamp() - Time::UnixEpoch()).InSeconds(),
+            (current_time - Time::UnixEpoch()).InSeconds());
+  SharedState result(subtle::Release_CompareAndSwap(
+      &SharedStateFromSharedMemory(shared_memory_mapping_)->value.i,
+      old_state.value.i, new_state.value.i));
+
+  DCHECK_EQ(old_state.value.u, result.value.u);
+
+  last_known_usage_ = current_time;
+}
+
+void* DiscardableSharedMemory::memory() const {
+  return reinterpret_cast<uint8_t*>(shared_memory_mapping_.memory()) +
+         AlignToPageSize(sizeof(SharedState));
+}
+
+bool DiscardableSharedMemory::Purge(Time current_time) {
+  // Calls to this function must be synchronized properly.
+  DFAKE_SCOPED_LOCK(thread_collision_warner_);
+  DCHECK(shared_memory_mapping_.IsValid());
+
+  SharedState old_state(SharedState::UNLOCKED, last_known_usage_);
+  SharedState new_state(SharedState::UNLOCKED, Time());
+  SharedState result(subtle::Acquire_CompareAndSwap(
+      &SharedStateFromSharedMemory(shared_memory_mapping_)->value.i,
+      old_state.value.i, new_state.value.i));
+
+  // Update |last_known_usage_| to |current_time| if the memory is locked. This
+  // allows the caller to determine if purging failed because last known usage
+  // was incorrect or memory was locked. In the second case, the caller should
+  // most likely wait for some amount of time before attempting to purge the
+  // the memory again.
+  if (result.value.u != old_state.value.u) {
+    last_known_usage_ = result.GetLockState() == SharedState::LOCKED
+                            ? current_time
+                            : result.GetTimestamp();
+    return false;
+  }
+
+// The next section will release as much resource as can be done
+// from the purging process, until the client process notices the
+// purge and releases its own references.
+// Note: this memory will not be accessed again.  The segment will be
+// freed asynchronously at a later time, so just do the best
+// immediately.
+#if defined(OS_POSIX) && !defined(OS_NACL)
+// Linux and Android provide MADV_REMOVE which is preferred as it has a
+// behavior that can be verified in tests. Other POSIX flavors (MacOSX, BSDs),
+// provide MADV_FREE which has the same result but memory is purged lazily.
+#if defined(OS_LINUX) || defined(OS_ANDROID)
+#define MADV_PURGE_ARGUMENT MADV_REMOVE
+#elif defined(OS_MACOSX)
+// MADV_FREE_REUSABLE is similar to MADV_FREE, but also marks the pages with the
+// reusable bit, which allows both Activity Monitor and memory-infra to
+// correctly track the pages.
+#define MADV_PURGE_ARGUMENT MADV_FREE_REUSABLE
+#else
+#define MADV_PURGE_ARGUMENT MADV_FREE
+#endif
+  // Advise the kernel to remove resources associated with purged pages.
+  // Subsequent accesses of memory pages will succeed, but might result in
+  // zero-fill-on-demand pages.
+  if (madvise(reinterpret_cast<char*>(shared_memory_mapping_.memory()) +
+                  AlignToPageSize(sizeof(SharedState)),
+              AlignToPageSize(mapped_size_), MADV_PURGE_ARGUMENT)) {
+    DPLOG(ERROR) << "madvise() failed";
+  }
+#elif defined(OS_WIN)
+  if (base::win::GetVersion() >= base::win::VERSION_WIN8_1) {
+    // Discard the purged pages, which releases the physical storage (resident
+    // memory, compressed or swapped), but leaves them reserved & committed.
+    // This does not free commit for use by other applications, but allows the
+    // system to avoid compressing/swapping these pages to free physical memory.
+    static const auto discard_virtual_memory =
+        reinterpret_cast<decltype(&::DiscardVirtualMemory)>(GetProcAddress(
+            GetModuleHandle(L"kernel32.dll"), "DiscardVirtualMemory"));
+    if (discard_virtual_memory) {
+      DWORD discard_result = discard_virtual_memory(
+          reinterpret_cast<char*>(shared_memory_mapping_.memory()) +
+              AlignToPageSize(sizeof(SharedState)),
+          AlignToPageSize(mapped_size_));
+      if (discard_result != ERROR_SUCCESS) {
+        DLOG(DCHECK) << "DiscardVirtualMemory() failed in Purge(): "
+                     << logging::SystemErrorCodeToString(discard_result);
+      }
+    }
+  }
+#endif
+
+  last_known_usage_ = Time();
+  return true;
+}
+
+bool DiscardableSharedMemory::IsMemoryResident() const {
+  DCHECK(shared_memory_mapping_.IsValid());
+
+  SharedState result(subtle::NoBarrier_Load(
+      &SharedStateFromSharedMemory(shared_memory_mapping_)->value.i));
+
+  return result.GetLockState() == SharedState::LOCKED ||
+         !result.GetTimestamp().is_null();
+}
+
+bool DiscardableSharedMemory::IsMemoryLocked() const {
+  DCHECK(shared_memory_mapping_.IsValid());
+
+  SharedState result(subtle::NoBarrier_Load(
+      &SharedStateFromSharedMemory(shared_memory_mapping_)->value.i));
+
+  return result.GetLockState() == SharedState::LOCKED;
+}
+
+void DiscardableSharedMemory::Close() {
+  shared_memory_region_ = UnsafeSharedMemoryRegion();
+}
+
+void DiscardableSharedMemory::CreateSharedMemoryOwnershipEdge(
+    trace_event::MemoryAllocatorDump* local_segment_dump,
+    trace_event::ProcessMemoryDump* pmd,
+    bool is_owned) const {
+  auto* shared_memory_dump = SharedMemoryTracker::GetOrCreateSharedMemoryDump(
+      shared_memory_mapping_, pmd);
+  // TODO(ssid): Clean this by a new api to inherit size of parent dump once the
+  // we send the full PMD and calculate sizes inside chrome, crbug.com/704203.
+  size_t resident_size = shared_memory_dump->GetSizeInternal();
+  local_segment_dump->AddScalar(trace_event::MemoryAllocatorDump::kNameSize,
+                                trace_event::MemoryAllocatorDump::kUnitsBytes,
+                                resident_size);
+
+  // By creating an edge with a higher |importance| (w.r.t non-owned dumps)
+  // the tracing UI will account the effective size of the segment to the
+  // client instead of manager.
+  // TODO(ssid): Define better constants in MemoryAllocatorDump for importance
+  // values, crbug.com/754793.
+  const int kImportance = is_owned ? 2 : 0;
+  auto shared_memory_guid = shared_memory_mapping_.guid();
+  local_segment_dump->AddString("id", "hash", shared_memory_guid.ToString());
+
+  // Owned discardable segments which are allocated by client process, could
+  // have been cleared by the discardable manager. So, the segment need not
+  // exist in memory and weak dumps are created to indicate the UI that the dump
+  // should exist only if the manager also created the global dump edge.
+  if (is_owned) {
+    pmd->CreateWeakSharedMemoryOwnershipEdge(local_segment_dump->guid(),
+                                             shared_memory_guid, kImportance);
+  } else {
+    pmd->CreateSharedMemoryOwnershipEdge(local_segment_dump->guid(),
+                                         shared_memory_guid, kImportance);
+  }
+}
+
+// static
+DiscardableSharedMemory::LockResult DiscardableSharedMemory::LockPages(
+    const UnsafeSharedMemoryRegion& region,
+    size_t offset,
+    size_t length) {
+#if defined(OS_ANDROID)
+  if (region.IsValid()) {
+    int pin_result =
+        ashmem_pin_region(region.GetPlatformHandle(), offset, length);
+    if (pin_result == ASHMEM_WAS_PURGED)
+      return PURGED;
+    if (pin_result < 0)
+      return FAILED;
+  }
+#endif
+  return SUCCESS;
+}
+
+// static
+void DiscardableSharedMemory::UnlockPages(
+    const UnsafeSharedMemoryRegion& region,
+    size_t offset,
+    size_t length) {
+#if defined(OS_ANDROID)
+  if (region.IsValid()) {
+    int unpin_result =
+        ashmem_unpin_region(region.GetPlatformHandle(), offset, length);
+    DCHECK_EQ(0, unpin_result);
+  }
+#endif
+}
+
+Time DiscardableSharedMemory::Now() const {
+  return Time::Now();
+}
+
+}  // namespace base
diff --git a/base/memory/discardable_shared_memory.h b/base/memory/discardable_shared_memory.h
new file mode 100644
index 0000000..52a78b1
--- /dev/null
+++ b/base/memory/discardable_shared_memory.h
@@ -0,0 +1,187 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_MEMORY_DISCARDABLE_SHARED_MEMORY_H_
+#define BASE_MEMORY_DISCARDABLE_SHARED_MEMORY_H_
+
+#include <stddef.h>
+
+#include "base/base_export.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/shared_memory_mapping.h"
+#include "base/memory/unsafe_shared_memory_region.h"
+#include "base/threading/thread_collision_warner.h"
+#include "base/time/time.h"
+#include "build/build_config.h"
+
+#if DCHECK_IS_ON()
+#include <set>
+#endif
+
+// Linux (including Android) support the MADV_REMOVE argument with madvise()
+// which has the behavior of reliably causing zero-fill-on-demand pages to
+// be returned after a call. Here we define
+// DISCARDABLE_SHARED_MEMORY_ZERO_FILL_ON_DEMAND_PAGES_AFTER_PURGE on Linux
+// and Android to indicate that this type of behavior can be expected on
+// those platforms. Note that madvise() will still be used on other POSIX
+// platforms but doesn't provide the zero-fill-on-demand pages guarantee.
+#if defined(OS_LINUX) || defined(OS_ANDROID)
+#define DISCARDABLE_SHARED_MEMORY_ZERO_FILL_ON_DEMAND_PAGES_AFTER_PURGE
+#endif
+
+namespace base {
+
+namespace trace_event {
+class MemoryAllocatorDump;
+class ProcessMemoryDump;
+}  // namespace trace_event
+
+// Platform abstraction for discardable shared memory.
+//
+// This class is not thread-safe. Clients are responsible for synchronizing
+// access to an instance of this class.
+class BASE_EXPORT DiscardableSharedMemory {
+ public:
+  enum LockResult { SUCCESS, PURGED, FAILED };
+
+  DiscardableSharedMemory();
+
+  // Create a new DiscardableSharedMemory object from an existing, open shared
+  // memory file. Memory must be locked.
+  explicit DiscardableSharedMemory(UnsafeSharedMemoryRegion region);
+
+  // Closes any open files.
+  virtual ~DiscardableSharedMemory();
+
+  // Creates and maps a locked DiscardableSharedMemory object with |size|.
+  // Returns true on success and false on failure.
+  bool CreateAndMap(size_t size);
+
+  // Maps the locked discardable memory into the caller's address space.
+  // Returns true on success, false otherwise.
+  bool Map(size_t size);
+
+  // Unmaps the discardable shared memory from the caller's address space.
+  // Unmapping won't unlock previously locked range.
+  // Returns true if successful; returns false on error or if the memory is
+  // not mapped.
+  bool Unmap();
+
+  // The actual size of the mapped memory (may be larger than requested).
+  size_t mapped_size() const { return mapped_size_; }
+
+  // Returns a duplicated shared memory region for this DiscardableSharedMemory
+  // object.
+  UnsafeSharedMemoryRegion DuplicateRegion() const {
+    return shared_memory_region_.Duplicate();
+  }
+
+  // Returns an ID for the shared memory region. This is ID of the mapped region
+  // consistent across all processes and is valid as long as the region is not
+  // unmapped.
+  const UnguessableToken& mapped_id() const {
+    return shared_memory_mapping_.guid();
+  }
+
+  // Locks a range of memory so that it will not be purged by the system.
+  // The range of memory must be unlocked. The result of trying to lock an
+  // already locked range is undefined. |offset| and |length| must both be
+  // a multiple of the page size as returned by GetPageSize().
+  // Passing 0 for |length| means "everything onward".
+  // Returns SUCCESS if range was successfully locked and the memory is still
+  // resident, PURGED if range was successfully locked but has been purged
+  // since last time it was locked and FAILED if range could not be locked.
+  // Locking can fail for two reasons; object might have been purged, our
+  // last known usage timestamp might be out of date. Last known usage time
+  // is updated to the actual last usage timestamp if memory is still resident
+  // or 0 if not.
+  LockResult Lock(size_t offset, size_t length);
+
+  // Unlock a previously successfully locked range of memory. The range of
+  // memory must be locked. The result of trying to unlock a not
+  // previously locked range is undefined.
+  // |offset| and |length| must both be a multiple of the page size as returned
+  // by GetPageSize().
+  // Passing 0 for |length| means "everything onward".
+  void Unlock(size_t offset, size_t length);
+
+  // Gets a pointer to the opened discardable memory space. Discardable memory
+  // must have been mapped via Map().
+  void* memory() const;
+
+  // Returns the last known usage time for DiscardableSharedMemory object. This
+  // may be earlier than the "true" usage time when memory has been used by a
+  // different process. Returns NULL time if purged.
+  Time last_known_usage() const { return last_known_usage_; }
+
+  // This returns true and sets |last_known_usage_| to 0 if
+  // DiscardableSharedMemory object was successfully purged. Purging can fail
+  // for two reasons; object might be locked or our last known usage timestamp
+  // might be out of date. Last known usage time is updated to |current_time|
+  // if locked or the actual last usage timestamp if unlocked. It is often
+  // necessary to call this function twice for the object to successfully be
+  // purged. First call, updates |last_known_usage_|. Second call, successfully
+  // purges the object using the updated |last_known_usage_|.
+  // Note: there is no guarantee that multiple calls to this function will
+  // successfully purge object. DiscardableSharedMemory object might be locked
+  // or another thread/process might be able to lock and unlock it in between
+  // each call.
+  bool Purge(Time current_time);
+
+  // Returns true if memory is still resident.
+  bool IsMemoryResident() const;
+
+  // Returns true if memory is locked.
+  bool IsMemoryLocked() const;
+
+  // Closes the open discardable memory segment.
+  // It is safe to call Close repeatedly.
+  void Close();
+
+  // For tracing: Creates ownership edge to the underlying shared memory dump
+  // which is cross process in the given |pmd|. |local_segment_dump| is the dump
+  // associated with the local discardable shared memory segment and |is_owned|
+  // is true when the current process owns the segment and the effective memory
+  // is assigned to the current process.
+  void CreateSharedMemoryOwnershipEdge(
+      trace_event::MemoryAllocatorDump* local_segment_dump,
+      trace_event::ProcessMemoryDump* pmd,
+      bool is_owned) const;
+
+ private:
+  // LockPages/UnlockPages are platform-native discardable page management
+  // helper functions. Both expect |offset| to be specified relative to the
+  // base address at which |memory| is mapped, and that |offset| and |length|
+  // are page-aligned by the caller.
+  // Returns SUCCESS on platforms which do not support discardable pages.
+  static LockResult LockPages(const UnsafeSharedMemoryRegion& region,
+                              size_t offset,
+                              size_t length);
+  // UnlockPages() is a no-op on platforms not supporting discardable pages.
+  static void UnlockPages(const UnsafeSharedMemoryRegion& region,
+                          size_t offset,
+                          size_t length);
+
+  // Virtual for tests.
+  virtual Time Now() const;
+
+  UnsafeSharedMemoryRegion shared_memory_region_;
+  WritableSharedMemoryMapping shared_memory_mapping_;
+  size_t mapped_size_;
+  size_t locked_page_count_;
+#if DCHECK_IS_ON()
+  std::set<size_t> locked_pages_;
+#endif
+  // Implementation is not thread-safe but still usable if clients are
+  // synchronized somehow. Use a collision warner to detect incorrect usage.
+  DFAKE_MUTEX(thread_collision_warner_);
+  Time last_known_usage_;
+
+  DISALLOW_COPY_AND_ASSIGN(DiscardableSharedMemory);
+};
+
+}  // namespace base
+
+#endif  // BASE_MEMORY_DISCARDABLE_SHARED_MEMORY_H_
diff --git a/base/memory/discardable_shared_memory_unittest.cc b/base/memory/discardable_shared_memory_unittest.cc
new file mode 100644
index 0000000..b3d21a7
--- /dev/null
+++ b/base/memory/discardable_shared_memory_unittest.cc
@@ -0,0 +1,456 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <fcntl.h>
+#include <stdint.h>
+
+#include "base/files/scoped_file.h"
+#include "base/memory/discardable_shared_memory.h"
+#include "base/memory/shared_memory_tracker.h"
+#include "base/process/process_metrics.h"
+#include "base/trace_event/memory_allocator_dump.h"
+#include "base/trace_event/process_memory_dump.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+class TestDiscardableSharedMemory : public DiscardableSharedMemory {
+ public:
+  TestDiscardableSharedMemory() = default;
+
+  explicit TestDiscardableSharedMemory(UnsafeSharedMemoryRegion region)
+      : DiscardableSharedMemory(std::move(region)) {}
+
+  void SetNow(Time now) { now_ = now; }
+
+ private:
+  // Overriden from DiscardableSharedMemory:
+  Time Now() const override { return now_; }
+
+  Time now_;
+};
+
+TEST(DiscardableSharedMemoryTest, CreateAndMap) {
+  const uint32_t kDataSize = 1024;
+
+  TestDiscardableSharedMemory memory;
+  bool rv = memory.CreateAndMap(kDataSize);
+  ASSERT_TRUE(rv);
+  EXPECT_GE(memory.mapped_size(), kDataSize);
+  EXPECT_TRUE(memory.IsMemoryLocked());
+}
+
+TEST(DiscardableSharedMemoryTest, CreateFromHandle) {
+  const uint32_t kDataSize = 1024;
+
+  TestDiscardableSharedMemory memory1;
+  bool rv = memory1.CreateAndMap(kDataSize);
+  ASSERT_TRUE(rv);
+
+  UnsafeSharedMemoryRegion shared_region = memory1.DuplicateRegion();
+  ASSERT_TRUE(shared_region.IsValid());
+
+  TestDiscardableSharedMemory memory2(std::move(shared_region));
+  rv = memory2.Map(kDataSize);
+  ASSERT_TRUE(rv);
+  EXPECT_TRUE(memory2.IsMemoryLocked());
+}
+
+TEST(DiscardableSharedMemoryTest, LockAndUnlock) {
+  const uint32_t kDataSize = 1024;
+
+  TestDiscardableSharedMemory memory1;
+  bool rv = memory1.CreateAndMap(kDataSize);
+  ASSERT_TRUE(rv);
+
+  // Memory is initially locked. Unlock it.
+  memory1.SetNow(Time::FromDoubleT(1));
+  memory1.Unlock(0, 0);
+  EXPECT_FALSE(memory1.IsMemoryLocked());
+
+  // Lock and unlock memory.
+  DiscardableSharedMemory::LockResult lock_rv = memory1.Lock(0, 0);
+  EXPECT_EQ(DiscardableSharedMemory::SUCCESS, lock_rv);
+  memory1.SetNow(Time::FromDoubleT(2));
+  memory1.Unlock(0, 0);
+
+  // Lock again before duplicating and passing ownership to new instance.
+  lock_rv = memory1.Lock(0, 0);
+  EXPECT_EQ(DiscardableSharedMemory::SUCCESS, lock_rv);
+  EXPECT_TRUE(memory1.IsMemoryLocked());
+
+  UnsafeSharedMemoryRegion shared_region = memory1.DuplicateRegion();
+  ASSERT_TRUE(shared_region.IsValid());
+
+  TestDiscardableSharedMemory memory2(std::move(shared_region));
+  rv = memory2.Map(kDataSize);
+  ASSERT_TRUE(rv);
+
+  // Unlock second instance.
+  memory2.SetNow(Time::FromDoubleT(3));
+  memory2.Unlock(0, 0);
+
+  // Both memory instances should be unlocked now.
+  EXPECT_FALSE(memory2.IsMemoryLocked());
+  EXPECT_FALSE(memory1.IsMemoryLocked());
+
+  // Lock second instance before passing ownership back to first instance.
+  lock_rv = memory2.Lock(0, 0);
+  EXPECT_EQ(DiscardableSharedMemory::SUCCESS, lock_rv);
+
+  // Memory should still be resident and locked.
+  rv = memory1.IsMemoryResident();
+  EXPECT_TRUE(rv);
+  EXPECT_TRUE(memory1.IsMemoryLocked());
+
+  // Unlock first instance.
+  memory1.SetNow(Time::FromDoubleT(4));
+  memory1.Unlock(0, 0);
+}
+
+TEST(DiscardableSharedMemoryTest, Purge) {
+  const uint32_t kDataSize = 1024;
+
+  TestDiscardableSharedMemory memory1;
+  bool rv = memory1.CreateAndMap(kDataSize);
+  ASSERT_TRUE(rv);
+
+  UnsafeSharedMemoryRegion shared_region = memory1.DuplicateRegion();
+  ASSERT_TRUE(shared_region.IsValid());
+
+  TestDiscardableSharedMemory memory2(std::move(shared_region));
+  rv = memory2.Map(kDataSize);
+  ASSERT_TRUE(rv);
+
+  // This should fail as memory is locked.
+  rv = memory1.Purge(Time::FromDoubleT(1));
+  EXPECT_FALSE(rv);
+
+  memory2.SetNow(Time::FromDoubleT(2));
+  memory2.Unlock(0, 0);
+
+  ASSERT_TRUE(memory2.IsMemoryResident());
+
+  // Memory is unlocked, but our usage timestamp is incorrect.
+  rv = memory1.Purge(Time::FromDoubleT(3));
+  EXPECT_FALSE(rv);
+
+  ASSERT_TRUE(memory2.IsMemoryResident());
+
+  // Memory is unlocked and our usage timestamp should be correct.
+  rv = memory1.Purge(Time::FromDoubleT(4));
+  EXPECT_TRUE(rv);
+
+  // Lock should fail as memory has been purged.
+  DiscardableSharedMemory::LockResult lock_rv = memory2.Lock(0, 0);
+  EXPECT_EQ(DiscardableSharedMemory::FAILED, lock_rv);
+
+  ASSERT_FALSE(memory2.IsMemoryResident());
+}
+
+TEST(DiscardableSharedMemoryTest, LastUsed) {
+  const uint32_t kDataSize = 1024;
+
+  TestDiscardableSharedMemory memory1;
+  bool rv = memory1.CreateAndMap(kDataSize);
+  ASSERT_TRUE(rv);
+
+  UnsafeSharedMemoryRegion shared_region = memory1.DuplicateRegion();
+  ASSERT_TRUE(shared_region.IsValid());
+
+  TestDiscardableSharedMemory memory2(std::move(shared_region));
+  rv = memory2.Map(kDataSize);
+  ASSERT_TRUE(rv);
+
+  memory2.SetNow(Time::FromDoubleT(1));
+  memory2.Unlock(0, 0);
+
+  EXPECT_EQ(memory2.last_known_usage(), Time::FromDoubleT(1));
+
+  DiscardableSharedMemory::LockResult lock_rv = memory2.Lock(0, 0);
+  EXPECT_EQ(DiscardableSharedMemory::SUCCESS, lock_rv);
+
+  // This should fail as memory is locked.
+  rv = memory1.Purge(Time::FromDoubleT(2));
+  ASSERT_FALSE(rv);
+
+  // Last usage should have been updated to timestamp passed to Purge above.
+  EXPECT_EQ(memory1.last_known_usage(), Time::FromDoubleT(2));
+
+  memory2.SetNow(Time::FromDoubleT(3));
+  memory2.Unlock(0, 0);
+
+  // Usage time should be correct for |memory2| instance.
+  EXPECT_EQ(memory2.last_known_usage(), Time::FromDoubleT(3));
+
+  // However, usage time has not changed as far as |memory1| instance knows.
+  EXPECT_EQ(memory1.last_known_usage(), Time::FromDoubleT(2));
+
+  // Memory is unlocked, but our usage timestamp is incorrect.
+  rv = memory1.Purge(Time::FromDoubleT(4));
+  EXPECT_FALSE(rv);
+
+  // The failed purge attempt should have updated usage time to the correct
+  // value.
+  EXPECT_EQ(memory1.last_known_usage(), Time::FromDoubleT(3));
+
+  // Purge memory through |memory2| instance. The last usage time should be
+  // set to 0 as a result of this.
+  rv = memory2.Purge(Time::FromDoubleT(5));
+  EXPECT_TRUE(rv);
+  EXPECT_TRUE(memory2.last_known_usage().is_null());
+
+  // This should fail as memory has already been purged and |memory1|'s usage
+  // time is incorrect as a result.
+  rv = memory1.Purge(Time::FromDoubleT(6));
+  EXPECT_FALSE(rv);
+
+  // The failed purge attempt should have updated usage time to the correct
+  // value.
+  EXPECT_TRUE(memory1.last_known_usage().is_null());
+
+  // Purge should succeed now that usage time is correct.
+  rv = memory1.Purge(Time::FromDoubleT(7));
+  EXPECT_TRUE(rv);
+}
+
+TEST(DiscardableSharedMemoryTest, LockShouldAlwaysFailAfterSuccessfulPurge) {
+  const uint32_t kDataSize = 1024;
+
+  TestDiscardableSharedMemory memory1;
+  bool rv = memory1.CreateAndMap(kDataSize);
+  ASSERT_TRUE(rv);
+
+  UnsafeSharedMemoryRegion shared_region = memory1.DuplicateRegion();
+  ASSERT_TRUE(shared_region.IsValid());
+
+  TestDiscardableSharedMemory memory2(std::move(shared_region));
+  rv = memory2.Map(kDataSize);
+  ASSERT_TRUE(rv);
+
+  memory2.SetNow(Time::FromDoubleT(1));
+  memory2.Unlock(0, 0);
+
+  rv = memory2.Purge(Time::FromDoubleT(2));
+  EXPECT_TRUE(rv);
+
+  // Lock should fail as memory has been purged.
+  DiscardableSharedMemory::LockResult lock_rv = memory2.Lock(0, 0);
+  EXPECT_EQ(DiscardableSharedMemory::FAILED, lock_rv);
+}
+
+#if defined(OS_ANDROID)
+TEST(DiscardableSharedMemoryTest, LockShouldFailIfPlatformLockPagesFails) {
+  const uint32_t kDataSize = 1024;
+
+  DiscardableSharedMemory memory1;
+  bool rv1 = memory1.CreateAndMap(kDataSize);
+  ASSERT_TRUE(rv1);
+
+  base::UnsafeSharedMemoryRegion region = memory1.DuplicateRegion();
+  int fd = region.GetPlatformHandle();
+  DiscardableSharedMemory memory2(std::move(region));
+  bool rv2 = memory2.Map(kDataSize);
+  ASSERT_TRUE(rv2);
+
+  // Unlock() the first page of memory, so we can test Lock()ing it.
+  memory2.Unlock(0, base::GetPageSize());
+  // To cause ashmem_pin_region() to fail, we arrange for it to be called with
+  // an invalid file-descriptor, which requires a valid-looking fd (i.e. we
+  // can't just Close() |memory|), but one on which the operation is invalid.
+  // We can overwrite the |memory| fd with a handle to a different file using
+  // dup2(), which has the nice properties that |memory| still has a valid fd
+  // that it can close, etc without errors, but on which ashmem_pin_region()
+  // will fail.
+  base::ScopedFD null(open("/dev/null", O_RDONLY));
+  ASSERT_EQ(fd, dup2(null.get(), fd));
+
+  // Now re-Lock()ing the first page should fail.
+  DiscardableSharedMemory::LockResult lock_rv =
+      memory2.Lock(0, base::GetPageSize());
+  EXPECT_EQ(DiscardableSharedMemory::FAILED, lock_rv);
+}
+#endif  // defined(OS_ANDROID)
+
+TEST(DiscardableSharedMemoryTest, LockAndUnlockRange) {
+  const uint32_t kDataSize = 32;
+
+  uint32_t data_size_in_bytes = kDataSize * base::GetPageSize();
+
+  TestDiscardableSharedMemory memory1;
+  bool rv = memory1.CreateAndMap(data_size_in_bytes);
+  ASSERT_TRUE(rv);
+
+  UnsafeSharedMemoryRegion shared_region = memory1.DuplicateRegion();
+  ASSERT_TRUE(shared_region.IsValid());
+
+  TestDiscardableSharedMemory memory2(std::move(shared_region));
+  rv = memory2.Map(data_size_in_bytes);
+  ASSERT_TRUE(rv);
+
+  // Unlock first page.
+  memory2.SetNow(Time::FromDoubleT(1));
+  memory2.Unlock(0, base::GetPageSize());
+
+  rv = memory1.Purge(Time::FromDoubleT(2));
+  EXPECT_FALSE(rv);
+
+  // Lock first page again.
+  memory2.SetNow(Time::FromDoubleT(3));
+  DiscardableSharedMemory::LockResult lock_rv =
+      memory2.Lock(0, base::GetPageSize());
+  EXPECT_NE(DiscardableSharedMemory::FAILED, lock_rv);
+
+  // Unlock first page.
+  memory2.SetNow(Time::FromDoubleT(4));
+  memory2.Unlock(0, base::GetPageSize());
+
+  rv = memory1.Purge(Time::FromDoubleT(5));
+  EXPECT_FALSE(rv);
+
+  // Unlock second page.
+  memory2.SetNow(Time::FromDoubleT(6));
+  memory2.Unlock(base::GetPageSize(), base::GetPageSize());
+
+  rv = memory1.Purge(Time::FromDoubleT(7));
+  EXPECT_FALSE(rv);
+
+  // Unlock anything onwards.
+  memory2.SetNow(Time::FromDoubleT(8));
+  memory2.Unlock(2 * base::GetPageSize(), 0);
+
+  // Memory is unlocked, but our usage timestamp is incorrect.
+  rv = memory1.Purge(Time::FromDoubleT(9));
+  EXPECT_FALSE(rv);
+
+  // The failed purge attempt should have updated usage time to the correct
+  // value.
+  EXPECT_EQ(Time::FromDoubleT(8), memory1.last_known_usage());
+
+  // Purge should now succeed.
+  rv = memory1.Purge(Time::FromDoubleT(10));
+  EXPECT_TRUE(rv);
+}
+
+TEST(DiscardableSharedMemoryTest, MappedSize) {
+  const uint32_t kDataSize = 1024;
+
+  TestDiscardableSharedMemory memory;
+  bool rv = memory.CreateAndMap(kDataSize);
+  ASSERT_TRUE(rv);
+
+  EXPECT_LE(kDataSize, memory.mapped_size());
+
+  // Mapped size should be 0 after memory segment has been unmapped.
+  rv = memory.Unmap();
+  EXPECT_TRUE(rv);
+  EXPECT_EQ(0u, memory.mapped_size());
+}
+
+TEST(DiscardableSharedMemoryTest, Close) {
+  const uint32_t kDataSize = 1024;
+
+  TestDiscardableSharedMemory memory;
+  bool rv = memory.CreateAndMap(kDataSize);
+  ASSERT_TRUE(rv);
+
+  // Mapped size should be unchanged after memory segment has been closed.
+  memory.Close();
+  EXPECT_LE(kDataSize, memory.mapped_size());
+
+  // Memory is initially locked. Unlock it.
+  memory.SetNow(Time::FromDoubleT(1));
+  memory.Unlock(0, 0);
+
+  // Lock and unlock memory.
+  DiscardableSharedMemory::LockResult lock_rv = memory.Lock(0, 0);
+  EXPECT_EQ(DiscardableSharedMemory::SUCCESS, lock_rv);
+  memory.SetNow(Time::FromDoubleT(2));
+  memory.Unlock(0, 0);
+}
+
+TEST(DiscardableSharedMemoryTest, ZeroSize) {
+  TestDiscardableSharedMemory memory;
+  bool rv = memory.CreateAndMap(0);
+  ASSERT_TRUE(rv);
+
+  EXPECT_LE(0u, memory.mapped_size());
+
+  // Memory is initially locked. Unlock it.
+  memory.SetNow(Time::FromDoubleT(1));
+  memory.Unlock(0, 0);
+
+  // Lock and unlock memory.
+  DiscardableSharedMemory::LockResult lock_rv = memory.Lock(0, 0);
+  EXPECT_NE(DiscardableSharedMemory::FAILED, lock_rv);
+  memory.SetNow(Time::FromDoubleT(2));
+  memory.Unlock(0, 0);
+}
+
+// This test checks that zero-filled pages are returned after purging a segment
+// when DISCARDABLE_SHARED_MEMORY_ZERO_FILL_ON_DEMAND_PAGES_AFTER_PURGE is
+// defined and MADV_REMOVE is supported.
+#if defined(DISCARDABLE_SHARED_MEMORY_ZERO_FILL_ON_DEMAND_PAGES_AFTER_PURGE)
+TEST(DiscardableSharedMemoryTest, ZeroFilledPagesAfterPurge) {
+  const uint32_t kDataSize = 1024;
+
+  TestDiscardableSharedMemory memory1;
+  bool rv = memory1.CreateAndMap(kDataSize);
+  ASSERT_TRUE(rv);
+
+  UnsafeSharedMemoryRegion shared_region = memory1.DuplicateRegion();
+  ASSERT_TRUE(shared_region.IsValid());
+
+  TestDiscardableSharedMemory memory2(std::move(shared_region));
+  rv = memory2.Map(kDataSize);
+  ASSERT_TRUE(rv);
+
+  // Initialize all memory to '0xaa'.
+  memset(memory2.memory(), 0xaa, kDataSize);
+
+  // Unlock memory.
+  memory2.SetNow(Time::FromDoubleT(1));
+  memory2.Unlock(0, 0);
+  EXPECT_FALSE(memory1.IsMemoryLocked());
+
+  // Memory is unlocked, but our usage timestamp is incorrect.
+  rv = memory1.Purge(Time::FromDoubleT(2));
+  EXPECT_FALSE(rv);
+  rv = memory1.Purge(Time::FromDoubleT(3));
+  EXPECT_TRUE(rv);
+
+  // Check that reading memory after it has been purged is returning
+  // zero-filled pages.
+  uint8_t expected_data[kDataSize] = {};
+  EXPECT_EQ(memcmp(memory2.memory(), expected_data, kDataSize), 0);
+}
+#endif
+
+TEST(DiscardableSharedMemoryTest, TracingOwnershipEdges) {
+  const uint32_t kDataSize = 1024;
+  TestDiscardableSharedMemory memory1;
+  bool rv = memory1.CreateAndMap(kDataSize);
+  ASSERT_TRUE(rv);
+
+  base::trace_event::MemoryDumpArgs args = {
+      base::trace_event::MemoryDumpLevelOfDetail::DETAILED};
+  trace_event::ProcessMemoryDump pmd(args);
+  trace_event::MemoryAllocatorDump* client_dump =
+      pmd.CreateAllocatorDump("discardable_manager/map1");
+  const bool is_owned = false;
+  memory1.CreateSharedMemoryOwnershipEdge(client_dump, &pmd, is_owned);
+  const auto* shm_dump = pmd.GetAllocatorDump(
+      SharedMemoryTracker::GetDumpNameForTracing(memory1.mapped_id()));
+  EXPECT_TRUE(shm_dump);
+  EXPECT_EQ(shm_dump->GetSizeInternal(), client_dump->GetSizeInternal());
+  const auto edges = pmd.allocator_dumps_edges();
+  EXPECT_EQ(2u, edges.size());
+  EXPECT_NE(edges.end(), edges.find(shm_dump->guid()));
+  EXPECT_NE(edges.end(), edges.find(client_dump->guid()));
+  // TODO(ssid): test for weak global dump once the
+  // CreateWeakSharedMemoryOwnershipEdge() is fixed, crbug.com/661257.
+}
+
+}  // namespace base
diff --git a/base/memory/memory_coordinator_client.cc b/base/memory/memory_coordinator_client.cc
new file mode 100644
index 0000000..7fa6232
--- /dev/null
+++ b/base/memory/memory_coordinator_client.cc
@@ -0,0 +1,27 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/memory/memory_coordinator_client.h"
+
+#include "base/logging.h"
+
+namespace base {
+
+const char* MemoryStateToString(MemoryState state) {
+  switch (state) {
+    case MemoryState::UNKNOWN:
+      return "unknown";
+    case MemoryState::NORMAL:
+      return "normal";
+    case MemoryState::THROTTLED:
+      return "throttled";
+    case MemoryState::SUSPENDED:
+      return "suspended";
+    default:
+      NOTREACHED();
+  }
+  return "";
+}
+
+}  // namespace base
diff --git a/base/memory/memory_coordinator_client.h b/base/memory/memory_coordinator_client.h
new file mode 100644
index 0000000..804f0a6
--- /dev/null
+++ b/base/memory/memory_coordinator_client.h
@@ -0,0 +1,79 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_MEMORY_MEMORY_COORDINATOR_CLIENT_H_
+#define BASE_MEMORY_MEMORY_COORDINATOR_CLIENT_H_
+
+#include "base/base_export.h"
+
+namespace base {
+
+// OVERVIEW:
+//
+// MemoryCoordinatorClient is an interface which a component can implement to
+// adjust "future allocation" and "existing allocation". For "future allocation"
+// it provides a callback to observe memory state changes, and for "existing
+// allocation" it provides a callback to purge memory.
+//
+// Unlike MemoryPressureListener, memory state changes are stateful. State
+// transitions are throttled to avoid thrashing; the exact throttling period is
+// platform dependent, but will be at least 5-10 seconds. When a state change
+// notification is dispatched, clients are expected to update their allocation
+// policies (e.g. setting cache limit) that persist for the duration of the
+// memory state. Note that clients aren't expected to free up memory on memory
+// state changes. Clients should wait for a separate purge request to free up
+// memory. Purging requests will be throttled as well.
+
+// MemoryState is an indicator that processes can use to guide their memory
+// allocation policies. For example, a process that receives the throttled
+// state can use that as as signal to decrease memory cache limits.
+// NOTE: This enum is used to back an UMA histogram, and therefore should be
+// treated as append-only.
+enum class MemoryState : int {
+  // The state is unknown.
+  UNKNOWN = -1,
+  // No memory constraints.
+  NORMAL = 0,
+  // Running and interactive but memory allocation should be throttled.
+  // Clients should set lower budget for any memory that is used as an
+  // optimization but that is not necessary for the process to run.
+  // (e.g. caches)
+  THROTTLED = 1,
+  // Still resident in memory but core processing logic has been suspended.
+  // In most cases, OnPurgeMemory() will be called before entering this state.
+  SUSPENDED = 2,
+};
+
+const int kMemoryStateMax = static_cast<int>(MemoryState::SUSPENDED) + 1;
+
+// Returns a string representation of MemoryState.
+BASE_EXPORT const char* MemoryStateToString(MemoryState state);
+
+// This is an interface for components which can respond to memory status
+// changes. An initial state is NORMAL. See MemoryCoordinatorClientRegistry for
+// threading guarantees and ownership management.
+class BASE_EXPORT MemoryCoordinatorClient {
+ public:
+  // Called when memory state has changed. Any transition can occur except for
+  // UNKNOWN. General guidelines are:
+  //  * NORMAL:    Restore the default settings for memory allocation/usage if
+  //               it has changed.
+  //  * THROTTLED: Use smaller limits for future memory allocations. You don't
+  //               need to take any action on existing allocations.
+  //  * SUSPENDED: Use much smaller limits for future memory allocations. You
+  //               don't need to take any action on existing allocations.
+  virtual void OnMemoryStateChange(MemoryState state) {}
+
+  // Called to purge memory.
+  // This callback should free up any memory that is used as an optimization, or
+  // any memory whose contents can be reproduced.
+  virtual void OnPurgeMemory() {}
+
+ protected:
+  virtual ~MemoryCoordinatorClient() = default;
+};
+
+}  // namespace base
+
+#endif  // BASE_MEMORY_MEMORY_COORDINATOR_CLIENT_H_
diff --git a/base/memory/memory_coordinator_client_registry.cc b/base/memory/memory_coordinator_client_registry.cc
new file mode 100644
index 0000000..45b4a7f
--- /dev/null
+++ b/base/memory/memory_coordinator_client_registry.cc
@@ -0,0 +1,41 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/memory/memory_coordinator_client_registry.h"
+
+namespace base {
+
+// static
+MemoryCoordinatorClientRegistry*
+MemoryCoordinatorClientRegistry::GetInstance() {
+  return Singleton<
+      MemoryCoordinatorClientRegistry,
+      LeakySingletonTraits<MemoryCoordinatorClientRegistry>>::get();
+}
+
+MemoryCoordinatorClientRegistry::MemoryCoordinatorClientRegistry()
+    : clients_(new ClientList) {}
+
+MemoryCoordinatorClientRegistry::~MemoryCoordinatorClientRegistry() = default;
+
+void MemoryCoordinatorClientRegistry::Register(
+    MemoryCoordinatorClient* client) {
+  clients_->AddObserver(client);
+}
+
+void MemoryCoordinatorClientRegistry::Unregister(
+    MemoryCoordinatorClient* client) {
+  clients_->RemoveObserver(client);
+}
+
+void MemoryCoordinatorClientRegistry::Notify(MemoryState state) {
+  clients_->Notify(FROM_HERE,
+                   &base::MemoryCoordinatorClient::OnMemoryStateChange, state);
+}
+
+void MemoryCoordinatorClientRegistry::PurgeMemory() {
+  clients_->Notify(FROM_HERE, &base::MemoryCoordinatorClient::OnPurgeMemory);
+}
+
+}  // namespace base
diff --git a/base/memory/memory_coordinator_client_registry.h b/base/memory/memory_coordinator_client_registry.h
new file mode 100644
index 0000000..e2c81b7
--- /dev/null
+++ b/base/memory/memory_coordinator_client_registry.h
@@ -0,0 +1,56 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_MEMORY_MEMORY_CLIENT_REGISTRY_H_
+#define BASE_MEMORY_MEMORY_CLIENT_REGISTRY_H_
+
+#include "base/base_export.h"
+#include "base/memory/memory_coordinator_client.h"
+#include "base/memory/singleton.h"
+#include "base/observer_list_threadsafe.h"
+
+namespace base {
+
+// MemoryCoordinatorClientRegistry is the registry of MemoryCoordinatorClients.
+// This class manages clients and provides a way to notify memory state changes
+// to clients, but this isn't responsible to determine how/when to change
+// memory states.
+//
+// Threading guarantees:
+// This class uses ObserverListThreadsafe internally, which means that
+//  * Registering/unregistering callbacks are thread-safe.
+//  * Callbacks are invoked on the same thread on which they are registered.
+// See base/observer_list_threadsafe.h for reference.
+//
+// Ownership management:
+// This class doesn't take the ownership of clients. Clients must be
+// unregistered before they are destroyed.
+class BASE_EXPORT MemoryCoordinatorClientRegistry {
+ public:
+  static MemoryCoordinatorClientRegistry* GetInstance();
+
+  ~MemoryCoordinatorClientRegistry();
+
+  // Registers/unregisters a client. Does not take ownership of client.
+  void Register(MemoryCoordinatorClient* client);
+  void Unregister(MemoryCoordinatorClient* client);
+
+  // Notify clients of a memory state change.
+  void Notify(MemoryState state);
+
+  // Requests purging memory.
+  void PurgeMemory();
+
+ private:
+  friend struct DefaultSingletonTraits<MemoryCoordinatorClientRegistry>;
+
+  MemoryCoordinatorClientRegistry();
+
+  using ClientList = ObserverListThreadSafe<MemoryCoordinatorClient>;
+  scoped_refptr<ClientList> clients_;
+};
+
+}  // namespace base
+
+#endif  // BASE_MEMORY_MEMORY_CLIENT_REGISTRY_H_
diff --git a/base/memory/memory_coordinator_client_registry_unittest.cc b/base/memory/memory_coordinator_client_registry_unittest.cc
new file mode 100644
index 0000000..37ed767
--- /dev/null
+++ b/base/memory/memory_coordinator_client_registry_unittest.cc
@@ -0,0 +1,58 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/memory/memory_coordinator_client_registry.h"
+
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+namespace {
+
+class TestMemoryCoordinatorClient : public MemoryCoordinatorClient {
+ public:
+  void OnMemoryStateChange(MemoryState state) override { state_ = state; }
+
+  void OnPurgeMemory() override { ++purge_count_; }
+
+  MemoryState state() const { return state_; }
+  size_t purge_count() const { return purge_count_; }
+
+ private:
+  MemoryState state_ = MemoryState::UNKNOWN;
+  size_t purge_count_ = 0;
+};
+
+void RunUntilIdle() {
+  base::RunLoop loop;
+  loop.RunUntilIdle();
+}
+
+TEST(MemoryCoordinatorClientRegistryTest, NotifyStateChange) {
+  MessageLoop loop;
+  auto* registry = MemoryCoordinatorClientRegistry::GetInstance();
+  TestMemoryCoordinatorClient client;
+  registry->Register(&client);
+  registry->Notify(MemoryState::THROTTLED);
+  RunUntilIdle();
+  ASSERT_EQ(MemoryState::THROTTLED, client.state());
+  registry->Unregister(&client);
+}
+
+TEST(MemoryCoordinatorClientRegistryTest, PurgeMemory) {
+  MessageLoop loop;
+  auto* registry = MemoryCoordinatorClientRegistry::GetInstance();
+  TestMemoryCoordinatorClient client;
+  registry->Register(&client);
+  registry->PurgeMemory();
+  RunUntilIdle();
+  ASSERT_EQ(1u, client.purge_count());
+  registry->Unregister(&client);
+}
+
+}  // namespace
+
+}  // namespace base
diff --git a/base/memory/memory_coordinator_proxy.cc b/base/memory/memory_coordinator_proxy.cc
new file mode 100644
index 0000000..4e22fe0
--- /dev/null
+++ b/base/memory/memory_coordinator_proxy.cc
@@ -0,0 +1,37 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/memory/memory_coordinator_proxy.h"
+
+namespace base {
+
+namespace {
+
+MemoryCoordinator* g_memory_coordinator = nullptr;
+
+}  // namespace
+
+MemoryCoordinatorProxy::MemoryCoordinatorProxy() = default;
+
+MemoryCoordinatorProxy::~MemoryCoordinatorProxy() = default;
+
+// static
+MemoryCoordinatorProxy* MemoryCoordinatorProxy::GetInstance() {
+  return Singleton<base::MemoryCoordinatorProxy>::get();
+}
+
+// static
+void MemoryCoordinatorProxy::SetMemoryCoordinator(
+    MemoryCoordinator* coordinator) {
+  DCHECK(!g_memory_coordinator || !coordinator);
+  g_memory_coordinator = coordinator;
+}
+
+MemoryState MemoryCoordinatorProxy::GetCurrentMemoryState() const {
+  if (!g_memory_coordinator)
+    return MemoryState::NORMAL;
+  return g_memory_coordinator->GetCurrentMemoryState();
+}
+
+}  // namespace base
diff --git a/base/memory/memory_coordinator_proxy.h b/base/memory/memory_coordinator_proxy.h
new file mode 100644
index 0000000..b6e7b3f
--- /dev/null
+++ b/base/memory/memory_coordinator_proxy.h
@@ -0,0 +1,49 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_MEMORY_MEMORY_COORDINATOR_PROXY_H_
+#define BASE_MEMORY_MEMORY_COORDINATOR_PROXY_H_
+
+#include "base/base_export.h"
+#include "base/callback.h"
+#include "base/memory/memory_coordinator_client.h"
+#include "base/memory/singleton.h"
+
+namespace base {
+
+// The MemoryCoordinator interface. See comments in MemoryCoordinatorProxy for
+// method descriptions.
+class BASE_EXPORT MemoryCoordinator {
+ public:
+  virtual ~MemoryCoordinator() = default;
+
+  virtual MemoryState GetCurrentMemoryState() const = 0;
+};
+
+// The proxy of MemoryCoordinator to be accessed from components that are not
+// in content/browser e.g. net.
+class BASE_EXPORT MemoryCoordinatorProxy {
+ public:
+  static MemoryCoordinatorProxy* GetInstance();
+
+  // Sets an implementation of MemoryCoordinator. MemoryCoordinatorProxy doesn't
+  // take the ownership of |coordinator|. It must outlive this proxy.
+  // This should be called before any components starts using this proxy.
+  static void SetMemoryCoordinator(MemoryCoordinator* coordinator);
+
+  // Returns the current memory state.
+  MemoryState GetCurrentMemoryState() const;
+
+ private:
+  friend struct base::DefaultSingletonTraits<MemoryCoordinatorProxy>;
+
+  MemoryCoordinatorProxy();
+  virtual ~MemoryCoordinatorProxy();
+
+  DISALLOW_COPY_AND_ASSIGN(MemoryCoordinatorProxy);
+};
+
+}  // namespace base
+
+#endif  // BASE_MEMORY_MEMORY_COORDINATOR_PROXY_H_
diff --git a/base/memory/memory_pressure_listener.cc b/base/memory/memory_pressure_listener.cc
new file mode 100644
index 0000000..669fb17
--- /dev/null
+++ b/base/memory/memory_pressure_listener.cc
@@ -0,0 +1,129 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/memory/memory_pressure_listener.h"
+
+#include "base/observer_list_threadsafe.h"
+#include "base/trace_event/trace_event.h"
+
+namespace base {
+
+namespace {
+
+// This class is thread safe and internally synchronized.
+class MemoryPressureObserver {
+ public:
+  // There is at most one MemoryPressureObserver and it is never deleted.
+  ~MemoryPressureObserver() = delete;
+
+  void AddObserver(MemoryPressureListener* listener, bool sync) {
+    async_observers_->AddObserver(listener);
+    if (sync) {
+      AutoLock lock(sync_observers_lock_);
+      sync_observers_.AddObserver(listener);
+    }
+  }
+
+  void RemoveObserver(MemoryPressureListener* listener) {
+    async_observers_->RemoveObserver(listener);
+    AutoLock lock(sync_observers_lock_);
+    sync_observers_.RemoveObserver(listener);
+  }
+
+  void Notify(
+      MemoryPressureListener::MemoryPressureLevel memory_pressure_level) {
+    async_observers_->Notify(FROM_HERE, &MemoryPressureListener::Notify,
+                             memory_pressure_level);
+    AutoLock lock(sync_observers_lock_);
+    for (auto& observer : sync_observers_)
+      observer.SyncNotify(memory_pressure_level);
+  }
+
+ private:
+  const scoped_refptr<ObserverListThreadSafe<MemoryPressureListener>>
+      async_observers_ = base::MakeRefCounted<
+          ObserverListThreadSafe<MemoryPressureListener>>();
+  ObserverList<MemoryPressureListener> sync_observers_;
+  Lock sync_observers_lock_;
+};
+
+// Gets the shared MemoryPressureObserver singleton instance.
+MemoryPressureObserver* GetMemoryPressureObserver() {
+  static auto* const observer = new MemoryPressureObserver();
+  return observer;
+}
+
+subtle::Atomic32 g_notifications_suppressed = 0;
+
+}  // namespace
+
+MemoryPressureListener::MemoryPressureListener(
+    const MemoryPressureListener::MemoryPressureCallback& callback)
+    : callback_(callback) {
+  GetMemoryPressureObserver()->AddObserver(this, false);
+}
+
+MemoryPressureListener::MemoryPressureListener(
+    const MemoryPressureListener::MemoryPressureCallback& callback,
+    const MemoryPressureListener::SyncMemoryPressureCallback&
+        sync_memory_pressure_callback)
+    : callback_(callback),
+      sync_memory_pressure_callback_(sync_memory_pressure_callback) {
+  GetMemoryPressureObserver()->AddObserver(this, true);
+}
+
+MemoryPressureListener::~MemoryPressureListener() {
+  GetMemoryPressureObserver()->RemoveObserver(this);
+}
+
+void MemoryPressureListener::Notify(MemoryPressureLevel memory_pressure_level) {
+  callback_.Run(memory_pressure_level);
+}
+
+void MemoryPressureListener::SyncNotify(
+    MemoryPressureLevel memory_pressure_level) {
+  if (!sync_memory_pressure_callback_.is_null()) {
+    sync_memory_pressure_callback_.Run(memory_pressure_level);
+  }
+}
+
+// static
+void MemoryPressureListener::NotifyMemoryPressure(
+    MemoryPressureLevel memory_pressure_level) {
+  DCHECK_NE(memory_pressure_level, MEMORY_PRESSURE_LEVEL_NONE);
+  TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("memory-infra"),
+                       "MemoryPressureListener::NotifyMemoryPressure",
+                       TRACE_EVENT_SCOPE_THREAD, "level",
+                       memory_pressure_level);
+  if (AreNotificationsSuppressed())
+    return;
+  DoNotifyMemoryPressure(memory_pressure_level);
+}
+
+// static
+bool MemoryPressureListener::AreNotificationsSuppressed() {
+  return subtle::Acquire_Load(&g_notifications_suppressed) == 1;
+}
+
+// static
+void MemoryPressureListener::SetNotificationsSuppressed(bool suppress) {
+  subtle::Release_Store(&g_notifications_suppressed, suppress ? 1 : 0);
+}
+
+// static
+void MemoryPressureListener::SimulatePressureNotification(
+    MemoryPressureLevel memory_pressure_level) {
+  // Notify all listeners even if regular pressure notifications are suppressed.
+  DoNotifyMemoryPressure(memory_pressure_level);
+}
+
+// static
+void MemoryPressureListener::DoNotifyMemoryPressure(
+    MemoryPressureLevel memory_pressure_level) {
+  DCHECK_NE(memory_pressure_level, MEMORY_PRESSURE_LEVEL_NONE);
+
+  GetMemoryPressureObserver()->Notify(memory_pressure_level);
+}
+
+}  // namespace base
diff --git a/base/memory/memory_pressure_listener.h b/base/memory/memory_pressure_listener.h
new file mode 100644
index 0000000..7e97010
--- /dev/null
+++ b/base/memory/memory_pressure_listener.h
@@ -0,0 +1,102 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// MemoryPressure provides static APIs for handling memory pressure on
+// platforms that have such signals, such as Android and ChromeOS.
+// The app will try to discard buffers that aren't deemed essential (individual
+// modules will implement their own policy).
+
+#ifndef BASE_MEMORY_MEMORY_PRESSURE_LISTENER_H_
+#define BASE_MEMORY_MEMORY_PRESSURE_LISTENER_H_
+
+#include "base/base_export.h"
+#include "base/callback.h"
+#include "base/macros.h"
+
+namespace base {
+
+// To start listening, create a new instance, passing a callback to a
+// function that takes a MemoryPressureLevel parameter. To stop listening,
+// simply delete the listener object. The implementation guarantees
+// that the callback will always be called on the thread that created
+// the listener.
+// Note that even on the same thread, the callback is not guaranteed to be
+// called synchronously within the system memory pressure broadcast.
+// Please see notes in MemoryPressureLevel enum below: some levels are
+// absolutely critical, and if not enough memory is returned to the system,
+// it'll potentially kill the app, and then later the app will have to be
+// cold-started.
+//
+// Example:
+//
+//    void OnMemoryPressure(MemoryPressureLevel memory_pressure_level) {
+//       ...
+//    }
+//
+//    // Start listening.
+//    MemoryPressureListener* my_listener =
+//        new MemoryPressureListener(base::Bind(&OnMemoryPressure));
+//
+//    ...
+//
+//    // Stop listening.
+//    delete my_listener;
+//
+class BASE_EXPORT MemoryPressureListener {
+ public:
+  // A Java counterpart will be generated for this enum.
+  // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.base
+  enum MemoryPressureLevel {
+    // No problems, there is enough memory to use. This event is not sent via
+    // callback, but the enum is used in other places to find out the current
+    // state of the system.
+    MEMORY_PRESSURE_LEVEL_NONE,
+
+    // Modules are advised to free buffers that are cheap to re-allocate and not
+    // immediately needed.
+    MEMORY_PRESSURE_LEVEL_MODERATE,
+
+    // At this level, modules are advised to free all possible memory.  The
+    // alternative is to be killed by the system, which means all memory will
+    // have to be re-created, plus the cost of a cold start.
+    MEMORY_PRESSURE_LEVEL_CRITICAL,
+  };
+
+  typedef Callback<void(MemoryPressureLevel)> MemoryPressureCallback;
+  typedef Callback<void(MemoryPressureLevel)> SyncMemoryPressureCallback;
+
+  explicit MemoryPressureListener(
+      const MemoryPressureCallback& memory_pressure_callback);
+  MemoryPressureListener(
+      const MemoryPressureCallback& memory_pressure_callback,
+      const SyncMemoryPressureCallback& sync_memory_pressure_callback);
+
+  ~MemoryPressureListener();
+
+  // Intended for use by the platform specific implementation.
+  static void NotifyMemoryPressure(MemoryPressureLevel memory_pressure_level);
+
+  // These methods should not be used anywhere else but in memory measurement
+  // code, where they are intended to maintain stable conditions across
+  // measurements.
+  static bool AreNotificationsSuppressed();
+  static void SetNotificationsSuppressed(bool suppressed);
+  static void SimulatePressureNotification(
+      MemoryPressureLevel memory_pressure_level);
+
+  void Notify(MemoryPressureLevel memory_pressure_level);
+  void SyncNotify(MemoryPressureLevel memory_pressure_level);
+
+ private:
+  static void DoNotifyMemoryPressure(MemoryPressureLevel memory_pressure_level);
+
+  MemoryPressureCallback callback_;
+  SyncMemoryPressureCallback sync_memory_pressure_callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(MemoryPressureListener);
+};
+
+}  // namespace base
+
+#endif  // BASE_MEMORY_MEMORY_PRESSURE_LISTENER_H_
diff --git a/base/memory/memory_pressure_listener_unittest.cc b/base/memory/memory_pressure_listener_unittest.cc
new file mode 100644
index 0000000..87d5f4c
--- /dev/null
+++ b/base/memory/memory_pressure_listener_unittest.cc
@@ -0,0 +1,79 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/memory/memory_pressure_listener.h"
+
+#include "base/bind.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace base {
+
+using MemoryPressureLevel = MemoryPressureListener::MemoryPressureLevel;
+
+class MemoryPressureListenerTest : public testing::Test {
+ public:
+  void SetUp() override {
+    message_loop_.reset(new MessageLoopForUI());
+    listener_.reset(new MemoryPressureListener(
+        Bind(&MemoryPressureListenerTest::OnMemoryPressure, Unretained(this))));
+  }
+
+  void TearDown() override {
+    listener_.reset();
+    message_loop_.reset();
+  }
+
+ protected:
+  void ExpectNotification(
+      void (*notification_function)(MemoryPressureLevel),
+      MemoryPressureLevel level) {
+    EXPECT_CALL(*this, OnMemoryPressure(level)).Times(1);
+    notification_function(level);
+    RunLoop().RunUntilIdle();
+  }
+
+  void ExpectNoNotification(
+      void (*notification_function)(MemoryPressureLevel),
+      MemoryPressureLevel level) {
+    EXPECT_CALL(*this, OnMemoryPressure(testing::_)).Times(0);
+    notification_function(level);
+    RunLoop().RunUntilIdle();
+  }
+
+ private:
+  MOCK_METHOD1(OnMemoryPressure,
+               void(MemoryPressureListener::MemoryPressureLevel));
+
+  std::unique_ptr<MessageLoopForUI> message_loop_;
+  std::unique_ptr<MemoryPressureListener> listener_;
+};
+
+TEST_F(MemoryPressureListenerTest, NotifyMemoryPressure) {
+  // Memory pressure notifications are not suppressed by default.
+  EXPECT_FALSE(MemoryPressureListener::AreNotificationsSuppressed());
+  ExpectNotification(&MemoryPressureListener::NotifyMemoryPressure,
+                     MemoryPressureLevel::MEMORY_PRESSURE_LEVEL_MODERATE);
+  ExpectNotification(&MemoryPressureListener::SimulatePressureNotification,
+                     MemoryPressureLevel::MEMORY_PRESSURE_LEVEL_MODERATE);
+
+  // Enable suppressing memory pressure notifications.
+  MemoryPressureListener::SetNotificationsSuppressed(true);
+  EXPECT_TRUE(MemoryPressureListener::AreNotificationsSuppressed());
+  ExpectNoNotification(&MemoryPressureListener::NotifyMemoryPressure,
+                       MemoryPressureLevel::MEMORY_PRESSURE_LEVEL_MODERATE);
+  ExpectNotification(&MemoryPressureListener::SimulatePressureNotification,
+                     MemoryPressureLevel::MEMORY_PRESSURE_LEVEL_MODERATE);
+
+  // Disable suppressing memory pressure notifications.
+  MemoryPressureListener::SetNotificationsSuppressed(false);
+  EXPECT_FALSE(MemoryPressureListener::AreNotificationsSuppressed());
+  ExpectNotification(&MemoryPressureListener::NotifyMemoryPressure,
+                     MemoryPressureLevel::MEMORY_PRESSURE_LEVEL_CRITICAL);
+  ExpectNotification(&MemoryPressureListener::SimulatePressureNotification,
+                     MemoryPressureLevel::MEMORY_PRESSURE_LEVEL_CRITICAL);
+}
+
+}  // namespace base
diff --git a/base/memory/memory_pressure_monitor.cc b/base/memory/memory_pressure_monitor.cc
new file mode 100644
index 0000000..ed350b8
--- /dev/null
+++ b/base/memory/memory_pressure_monitor.cc
@@ -0,0 +1,71 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/memory/memory_pressure_monitor.h"
+
+#include "base/logging.h"
+#include "base/metrics/histogram_macros.h"
+
+namespace base {
+namespace {
+
+MemoryPressureMonitor* g_monitor = nullptr;
+
+// Enumeration of UMA memory pressure levels. This needs to be kept in sync with
+// histograms.xml and the memory pressure levels defined in
+// MemoryPressureListener.
+enum MemoryPressureLevelUMA {
+  UMA_MEMORY_PRESSURE_LEVEL_NONE = 0,
+  UMA_MEMORY_PRESSURE_LEVEL_MODERATE = 1,
+  UMA_MEMORY_PRESSURE_LEVEL_CRITICAL = 2,
+  // This must be the last value in the enum.
+  UMA_MEMORY_PRESSURE_LEVEL_COUNT,
+};
+
+// Converts a memory pressure level to an UMA enumeration value.
+MemoryPressureLevelUMA MemoryPressureLevelToUmaEnumValue(
+    base::MemoryPressureListener::MemoryPressureLevel level) {
+  switch (level) {
+    case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE:
+      return UMA_MEMORY_PRESSURE_LEVEL_NONE;
+    case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE:
+      return UMA_MEMORY_PRESSURE_LEVEL_MODERATE;
+    case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL:
+      return UMA_MEMORY_PRESSURE_LEVEL_CRITICAL;
+  }
+  NOTREACHED();
+  return UMA_MEMORY_PRESSURE_LEVEL_NONE;
+}
+
+}  // namespace
+
+MemoryPressureMonitor::MemoryPressureMonitor() {
+  DCHECK(!g_monitor);
+  g_monitor = this;
+}
+
+MemoryPressureMonitor::~MemoryPressureMonitor() {
+  DCHECK(g_monitor);
+  g_monitor = nullptr;
+}
+
+// static
+MemoryPressureMonitor* MemoryPressureMonitor::Get() {
+  return g_monitor;
+}
+void MemoryPressureMonitor::RecordMemoryPressure(
+    base::MemoryPressureListener::MemoryPressureLevel level,
+    int ticks) {
+  // Use the more primitive STATIC_HISTOGRAM_POINTER_BLOCK macro because the
+  // simple UMA_HISTOGRAM macros don't expose 'AddCount' functionality.
+  STATIC_HISTOGRAM_POINTER_BLOCK(
+      "Memory.PressureLevel",
+      AddCount(MemoryPressureLevelToUmaEnumValue(level), ticks),
+      base::LinearHistogram::FactoryGet(
+          "Memory.PressureLevel", 1, UMA_MEMORY_PRESSURE_LEVEL_COUNT,
+          UMA_MEMORY_PRESSURE_LEVEL_COUNT + 1,
+          base::HistogramBase::kUmaTargetedHistogramFlag));
+}
+
+}  // namespace base
diff --git a/base/memory/memory_pressure_monitor.h b/base/memory/memory_pressure_monitor.h
new file mode 100644
index 0000000..e48244b
--- /dev/null
+++ b/base/memory/memory_pressure_monitor.h
@@ -0,0 +1,53 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_MEMORY_MEMORY_PRESSURE_MONITOR_H_
+#define BASE_MEMORY_MEMORY_PRESSURE_MONITOR_H_
+
+#include "base/base_export.h"
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/memory_pressure_listener.h"
+
+namespace base {
+
+// TODO(chrisha): Make this a concrete class with per-OS implementations rather
+// than an abstract base class.
+
+// Declares the interface for a MemoryPressureMonitor. There are multiple
+// OS specific implementations of this class. An instance of the memory
+// pressure observer is created at the process level, tracks memory usage, and
+// pushes memory state change notifications to the static function
+// base::MemoryPressureListener::NotifyMemoryPressure. This is turn notifies
+// all MemoryPressureListener instances via a callback.
+class BASE_EXPORT MemoryPressureMonitor {
+ public:
+  using MemoryPressureLevel = base::MemoryPressureListener::MemoryPressureLevel;
+  using DispatchCallback = base::Callback<void(MemoryPressureLevel level)>;
+
+  virtual ~MemoryPressureMonitor();
+
+  // Return the singleton MemoryPressureMonitor.
+  static MemoryPressureMonitor* Get();
+
+  // Record memory pressure UMA statistic. A tick is 5 seconds.
+  static void RecordMemoryPressure(MemoryPressureLevel level, int ticks);
+
+  // Returns the currently observed memory pressure.
+  virtual MemoryPressureLevel GetCurrentPressureLevel() = 0;
+
+  // Sets a notification callback. The default callback invokes
+  // base::MemoryPressureListener::NotifyMemoryPressure.
+  virtual void SetDispatchCallback(const DispatchCallback& callback) = 0;
+
+ protected:
+  MemoryPressureMonitor();
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MemoryPressureMonitor);
+};
+
+}  // namespace base
+
+#endif  // BASE_MEMORY_MEMORY_PRESSURE_MONITOR_H_
diff --git a/base/memory/memory_pressure_monitor_chromeos.cc b/base/memory/memory_pressure_monitor_chromeos.cc
new file mode 100644
index 0000000..b4e4b94
--- /dev/null
+++ b/base/memory/memory_pressure_monitor_chromeos.cc
@@ -0,0 +1,288 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/memory/memory_pressure_monitor_chromeos.h"
+
+#include <fcntl.h>
+#include <sys/select.h>
+
+#include "base/metrics/histogram_macros.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/process/process_metrics.h"
+#include "base/single_thread_task_runner.h"
+#include "base/sys_info.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/time/time.h"
+
+namespace base {
+namespace chromeos {
+
+namespace {
+
+// The time between memory pressure checks. While under critical pressure, this
+// is also the timer to repeat cleanup attempts.
+const int kMemoryPressureIntervalMs = 1000;
+
+// The time which should pass between two moderate memory pressure calls.
+const int kModerateMemoryPressureCooldownMs = 10000;
+
+// Number of event polls before the next moderate pressure event can be sent.
+const int kModerateMemoryPressureCooldown =
+    kModerateMemoryPressureCooldownMs / kMemoryPressureIntervalMs;
+
+// Threshold constants to emit pressure events.
+const int kNormalMemoryPressureModerateThresholdPercent = 60;
+const int kNormalMemoryPressureCriticalThresholdPercent = 95;
+const int kAggressiveMemoryPressureModerateThresholdPercent = 35;
+const int kAggressiveMemoryPressureCriticalThresholdPercent = 70;
+
+// The possible state for memory pressure level. The values should be in line
+// with values in MemoryPressureListener::MemoryPressureLevel and should be
+// updated if more memory pressure levels are introduced.
+enum MemoryPressureLevelUMA {
+  MEMORY_PRESSURE_LEVEL_NONE = 0,
+  MEMORY_PRESSURE_LEVEL_MODERATE,
+  MEMORY_PRESSURE_LEVEL_CRITICAL,
+  NUM_MEMORY_PRESSURE_LEVELS
+};
+
+// This is the file that will exist if low memory notification is available
+// on the device.  Whenever it becomes readable, it signals a low memory
+// condition.
+const char kLowMemFile[] = "/dev/chromeos-low-mem";
+
+// Converts a |MemoryPressureThreshold| value into a used memory percentage for
+// the moderate pressure event.
+int GetModerateMemoryThresholdInPercent(
+    MemoryPressureMonitor::MemoryPressureThresholds thresholds) {
+  return thresholds == MemoryPressureMonitor::
+                           THRESHOLD_AGGRESSIVE_CACHE_DISCARD ||
+         thresholds == MemoryPressureMonitor::THRESHOLD_AGGRESSIVE
+             ? kAggressiveMemoryPressureModerateThresholdPercent
+             : kNormalMemoryPressureModerateThresholdPercent;
+}
+
+// Converts a |MemoryPressureThreshold| value into a used memory percentage for
+// the critical pressure event.
+int GetCriticalMemoryThresholdInPercent(
+    MemoryPressureMonitor::MemoryPressureThresholds thresholds) {
+  return thresholds == MemoryPressureMonitor::
+                           THRESHOLD_AGGRESSIVE_TAB_DISCARD ||
+         thresholds == MemoryPressureMonitor::THRESHOLD_AGGRESSIVE
+             ? kAggressiveMemoryPressureCriticalThresholdPercent
+             : kNormalMemoryPressureCriticalThresholdPercent;
+}
+
+// Converts free percent of memory into a memory pressure value.
+MemoryPressureListener::MemoryPressureLevel GetMemoryPressureLevelFromFillLevel(
+    int actual_fill_level,
+    int moderate_threshold,
+    int critical_threshold) {
+  if (actual_fill_level < moderate_threshold)
+    return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE;
+  return actual_fill_level < critical_threshold
+             ? MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE
+             : MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL;
+}
+
+// This function will be called less than once a second. It will check if
+// the kernel has detected a low memory situation.
+bool IsLowMemoryCondition(int file_descriptor) {
+  fd_set fds;
+  struct timeval tv;
+
+  FD_ZERO(&fds);
+  FD_SET(file_descriptor, &fds);
+
+  tv.tv_sec = 0;
+  tv.tv_usec = 0;
+
+  return HANDLE_EINTR(select(file_descriptor + 1, &fds, NULL, NULL, &tv)) > 0;
+}
+
+}  // namespace
+
+MemoryPressureMonitor::MemoryPressureMonitor(
+    MemoryPressureThresholds thresholds)
+    : current_memory_pressure_level_(
+          MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE),
+      moderate_pressure_repeat_count_(0),
+      seconds_since_reporting_(0),
+      moderate_pressure_threshold_percent_(
+          GetModerateMemoryThresholdInPercent(thresholds)),
+      critical_pressure_threshold_percent_(
+          GetCriticalMemoryThresholdInPercent(thresholds)),
+      low_mem_file_(HANDLE_EINTR(::open(kLowMemFile, O_RDONLY))),
+      dispatch_callback_(
+          base::Bind(&MemoryPressureListener::NotifyMemoryPressure)),
+      weak_ptr_factory_(this) {
+  StartObserving();
+  LOG_IF(ERROR,
+         base::SysInfo::IsRunningOnChromeOS() && !low_mem_file_.is_valid())
+      << "Cannot open kernel listener";
+}
+
+MemoryPressureMonitor::~MemoryPressureMonitor() {
+  StopObserving();
+}
+
+void MemoryPressureMonitor::ScheduleEarlyCheck() {
+  ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, BindOnce(&MemoryPressureMonitor::CheckMemoryPressure,
+                          weak_ptr_factory_.GetWeakPtr()));
+}
+
+MemoryPressureListener::MemoryPressureLevel
+MemoryPressureMonitor::GetCurrentPressureLevel() {
+  return current_memory_pressure_level_;
+}
+
+// static
+MemoryPressureMonitor* MemoryPressureMonitor::Get() {
+  return static_cast<MemoryPressureMonitor*>(
+      base::MemoryPressureMonitor::Get());
+}
+
+void MemoryPressureMonitor::StartObserving() {
+  timer_.Start(FROM_HERE,
+               TimeDelta::FromMilliseconds(kMemoryPressureIntervalMs),
+               Bind(&MemoryPressureMonitor::
+                        CheckMemoryPressureAndRecordStatistics,
+                    weak_ptr_factory_.GetWeakPtr()));
+}
+
+void MemoryPressureMonitor::StopObserving() {
+  // If StartObserving failed, StopObserving will still get called.
+  timer_.Stop();
+}
+
+void MemoryPressureMonitor::CheckMemoryPressureAndRecordStatistics() {
+  CheckMemoryPressure();
+  if (seconds_since_reporting_++ == 5) {
+    seconds_since_reporting_ = 0;
+    RecordMemoryPressure(current_memory_pressure_level_, 1);
+  }
+  // Record UMA histogram statistics for the current memory pressure level.
+  // TODO(lgrey): Remove this once there's a usable history for the
+  // "Memory.PressureLevel" statistic
+  MemoryPressureLevelUMA memory_pressure_level_uma(MEMORY_PRESSURE_LEVEL_NONE);
+  switch (current_memory_pressure_level_) {
+    case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE:
+      memory_pressure_level_uma = MEMORY_PRESSURE_LEVEL_NONE;
+      break;
+    case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE:
+      memory_pressure_level_uma = MEMORY_PRESSURE_LEVEL_MODERATE;
+      break;
+    case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL:
+      memory_pressure_level_uma = MEMORY_PRESSURE_LEVEL_CRITICAL;
+      break;
+  }
+
+  UMA_HISTOGRAM_ENUMERATION("ChromeOS.MemoryPressureLevel",
+                            memory_pressure_level_uma,
+                            NUM_MEMORY_PRESSURE_LEVELS);
+}
+
+void MemoryPressureMonitor::CheckMemoryPressure() {
+  MemoryPressureListener::MemoryPressureLevel old_pressure =
+      current_memory_pressure_level_;
+
+  // If we have the kernel low memory observer, we use it's flag instead of our
+  // own computation (for now). Note that in "simulation mode" it can be null.
+  // TODO(skuhne): We need to add code which makes sure that the kernel and this
+  // computation come to similar results and then remove this override again.
+  // TODO(skuhne): Add some testing framework here to see how close the kernel
+  // and the internal functions are.
+  if (low_mem_file_.is_valid() && IsLowMemoryCondition(low_mem_file_.get())) {
+    current_memory_pressure_level_ =
+        MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL;
+  } else {
+    current_memory_pressure_level_ = GetMemoryPressureLevelFromFillLevel(
+        GetUsedMemoryInPercent(),
+        moderate_pressure_threshold_percent_,
+        critical_pressure_threshold_percent_);
+
+    // When listening to the kernel, we ignore the reported memory pressure
+    // level from our own computation and reduce critical to moderate.
+    if (low_mem_file_.is_valid() &&
+        current_memory_pressure_level_ ==
+        MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL) {
+      current_memory_pressure_level_ =
+          MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE;
+    }
+  }
+
+  // In case there is no memory pressure we do not notify.
+  if (current_memory_pressure_level_ ==
+      MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE) {
+    return;
+  }
+  if (old_pressure == current_memory_pressure_level_) {
+    // If the memory pressure is still at the same level, we notify again for a
+    // critical level. In case of a moderate level repeat however, we only send
+    // a notification after a certain time has passed.
+    if (current_memory_pressure_level_ ==
+        MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE &&
+          ++moderate_pressure_repeat_count_ <
+              kModerateMemoryPressureCooldown) {
+      return;
+    }
+  } else if (current_memory_pressure_level_ ==
+               MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE &&
+             old_pressure ==
+               MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL) {
+    // When we reducing the pressure level from critical to moderate, we
+    // restart the timeout and do not send another notification.
+    moderate_pressure_repeat_count_ = 0;
+    return;
+  }
+  moderate_pressure_repeat_count_ = 0;
+  dispatch_callback_.Run(current_memory_pressure_level_);
+}
+
+// Gets the used ChromeOS memory in percent.
+int MemoryPressureMonitor::GetUsedMemoryInPercent() {
+  base::SystemMemoryInfoKB info;
+  if (!base::GetSystemMemoryInfo(&info)) {
+    VLOG(1) << "Cannot determine the free memory of the system.";
+    return 0;
+  }
+  // TODO(skuhne): Instead of adding the kernel memory pressure calculation
+  // logic here, we should have a kernel mechanism similar to the low memory
+  // notifier in ChromeOS which offers multiple pressure states.
+  // To track this, we have crbug.com/381196.
+
+  // The available memory consists of "real" and virtual (z)ram memory.
+  // Since swappable memory uses a non pre-deterministic compression and
+  // the compression creates its own "dynamic" in the system, it gets
+  // de-emphasized by the |kSwapWeight| factor.
+  const int kSwapWeight = 4;
+
+  // The total memory we have is the "real memory" plus the virtual (z)ram.
+  int total_memory = info.total + info.swap_total / kSwapWeight;
+
+  // The kernel internally uses 50MB.
+  const int kMinFileMemory = 50 * 1024;
+
+  // Most file memory can be easily reclaimed.
+  int file_memory = info.active_file + info.inactive_file;
+  // unless it is dirty or it's a minimal portion which is required.
+  file_memory -= info.dirty + kMinFileMemory;
+
+  // Available memory is the sum of free, swap and easy reclaimable memory.
+  int available_memory =
+      info.free + info.swap_free / kSwapWeight + file_memory;
+
+  DCHECK(available_memory < total_memory);
+  int percentage = ((total_memory - available_memory) * 100) / total_memory;
+  return percentage;
+}
+
+void MemoryPressureMonitor::SetDispatchCallback(
+    const DispatchCallback& callback) {
+  dispatch_callback_ = callback;
+}
+
+}  // namespace chromeos
+}  // namespace base
diff --git a/base/memory/memory_pressure_monitor_chromeos.h b/base/memory/memory_pressure_monitor_chromeos.h
new file mode 100644
index 0000000..563ba85
--- /dev/null
+++ b/base/memory/memory_pressure_monitor_chromeos.h
@@ -0,0 +1,128 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_MEMORY_MEMORY_PRESSURE_MONITOR_CHROMEOS_H_
+#define BASE_MEMORY_MEMORY_PRESSURE_MONITOR_CHROMEOS_H_
+
+#include "base/base_export.h"
+#include "base/files/scoped_file.h"
+#include "base/macros.h"
+#include "base/memory/memory_pressure_listener.h"
+#include "base/memory/memory_pressure_monitor.h"
+#include "base/memory/weak_ptr.h"
+#include "base/timer/timer.h"
+
+namespace base {
+namespace chromeos {
+
+class TestMemoryPressureMonitor;
+
+////////////////////////////////////////////////////////////////////////////////
+// MemoryPressureMonitor
+//
+// A class to handle the observation of our free memory. It notifies the
+// MemoryPressureListener of memory fill level changes, so that it can take
+// action to reduce memory resources accordingly.
+//
+class BASE_EXPORT MemoryPressureMonitor : public base::MemoryPressureMonitor {
+ public:
+  using GetUsedMemoryInPercentCallback = int (*)();
+
+  // There are two memory pressure events:
+  // MODERATE - which will mainly release caches.
+  // CRITICAL - which will discard tabs.
+  // The |MemoryPressureThresholds| enum selects the strategy of firing these
+  // events: A conservative strategy will keep as much content in memory as
+  // possible (causing the system to swap to zram) and an aggressive strategy
+  // will release memory earlier to avoid swapping.
+  enum MemoryPressureThresholds {
+    // Use the system default.
+    THRESHOLD_DEFAULT = 0,
+    // Try to keep as much content in memory as possible.
+    THRESHOLD_CONSERVATIVE = 1,
+    // Discard caches earlier, allowing to keep more tabs in memory.
+    THRESHOLD_AGGRESSIVE_CACHE_DISCARD = 2,
+    // Discard tabs earlier, allowing the system to get faster.
+    THRESHOLD_AGGRESSIVE_TAB_DISCARD = 3,
+    // Discard caches and tabs earlier to allow the system to be faster.
+    THRESHOLD_AGGRESSIVE = 4
+  };
+
+  explicit MemoryPressureMonitor(MemoryPressureThresholds thresholds);
+  ~MemoryPressureMonitor() override;
+
+  // Redo the memory pressure calculation soon and call again if a critical
+  // memory pressure prevails. Note that this call will trigger an asynchronous
+  // action which gives the system time to release memory back into the pool.
+  void ScheduleEarlyCheck();
+
+  // Get the current memory pressure level.
+  MemoryPressureListener::MemoryPressureLevel GetCurrentPressureLevel()
+      override;
+  void SetDispatchCallback(const DispatchCallback& callback) override;
+
+  // Returns a type-casted version of the current memory pressure monitor. A
+  // simple wrapper to base::MemoryPressureMonitor::Get.
+  static MemoryPressureMonitor* Get();
+
+ private:
+  friend TestMemoryPressureMonitor;
+  // Starts observing the memory fill level.
+  // Calls to StartObserving should always be matched with calls to
+  // StopObserving.
+  void StartObserving();
+
+  // Stop observing the memory fill level.
+  // May be safely called if StartObserving has not been called.
+  void StopObserving();
+
+  // The function which gets periodically called to check any changes in the
+  // memory pressure. It will report pressure changes as well as continuous
+  // critical pressure levels.
+  void CheckMemoryPressure();
+
+  // The function periodically checks the memory pressure changes and records
+  // the UMA histogram statistics for the current memory pressure level.
+  void CheckMemoryPressureAndRecordStatistics();
+
+  // Get the memory pressure in percent (virtual for testing).
+  virtual int GetUsedMemoryInPercent();
+
+  // The current memory pressure.
+  base::MemoryPressureListener::MemoryPressureLevel
+      current_memory_pressure_level_;
+
+  // A periodic timer to check for resource pressure changes. This will get
+  // replaced by a kernel triggered event system (see crbug.com/381196).
+  base::RepeatingTimer timer_;
+
+  // To slow down the amount of moderate pressure event calls, this counter
+  // gets used to count the number of events since the last event occured.
+  int moderate_pressure_repeat_count_;
+
+  // The "Memory.PressureLevel" statistic is recorded every
+  // 5 seconds, but the timer to report "ChromeOS.MemoryPressureLevel"
+  // fires every second. This counter is used to allow reporting
+  // "Memory.PressureLevel" correctly without adding another
+  // timer.
+  int seconds_since_reporting_;
+
+  // The thresholds for moderate and critical pressure.
+  const int moderate_pressure_threshold_percent_;
+  const int critical_pressure_threshold_percent_;
+
+  // File descriptor used to detect low memory condition.
+  ScopedFD low_mem_file_;
+
+  DispatchCallback dispatch_callback_;
+
+  base::WeakPtrFactory<MemoryPressureMonitor> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(MemoryPressureMonitor);
+};
+
+}  // namespace chromeos
+}  // namespace base
+
+#endif  // BASE_MEMORY_MEMORY_PRESSURE_MONITOR_CHROMEOS_H_
diff --git a/base/memory/memory_pressure_monitor_chromeos_unittest.cc b/base/memory/memory_pressure_monitor_chromeos_unittest.cc
new file mode 100644
index 0000000..ee00091
--- /dev/null
+++ b/base/memory/memory_pressure_monitor_chromeos_unittest.cc
@@ -0,0 +1,172 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/memory/memory_pressure_monitor_chromeos.h"
+
+#include "base/macros.h"
+#include "base/memory/memory_pressure_listener.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/sys_info.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace chromeos {
+
+namespace {
+
+// True if the memory notifier got called.
+// Do not read/modify value directly.
+bool on_memory_pressure_called = false;
+
+// If the memory notifier got called, this is the memory pressure reported.
+MemoryPressureListener::MemoryPressureLevel on_memory_pressure_level =
+    MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE;
+
+// Processes OnMemoryPressure calls.
+void OnMemoryPressure(MemoryPressureListener::MemoryPressureLevel level) {
+  on_memory_pressure_called = true;
+  on_memory_pressure_level = level;
+}
+
+// Resets the indicator for memory pressure.
+void ResetOnMemoryPressureCalled() {
+  on_memory_pressure_called = false;
+}
+
+// Returns true when OnMemoryPressure was called (and resets it).
+bool WasOnMemoryPressureCalled() {
+  bool b = on_memory_pressure_called;
+  ResetOnMemoryPressureCalled();
+  return b;
+}
+
+}  // namespace
+
+class TestMemoryPressureMonitor : public MemoryPressureMonitor {
+ public:
+  TestMemoryPressureMonitor()
+      : MemoryPressureMonitor(THRESHOLD_DEFAULT),
+        memory_in_percent_override_(0) {
+    // Disable any timers which are going on and set a special memory reporting
+    // function.
+    StopObserving();
+  }
+  ~TestMemoryPressureMonitor() override = default;
+
+  void SetMemoryInPercentOverride(int percent) {
+    memory_in_percent_override_ = percent;
+  }
+
+  void CheckMemoryPressureForTest() {
+    CheckMemoryPressure();
+  }
+
+ private:
+  int GetUsedMemoryInPercent() override {
+    return memory_in_percent_override_;
+  }
+
+  int memory_in_percent_override_;
+  DISALLOW_COPY_AND_ASSIGN(TestMemoryPressureMonitor);
+};
+
+// This test tests the various transition states from memory pressure, looking
+// for the correct behavior on event reposting as well as state updates.
+TEST(ChromeOSMemoryPressureMonitorTest, CheckMemoryPressure) {
+  // crbug.com/844102:
+  if (base::SysInfo::IsRunningOnChromeOS())
+    return;
+
+  base::MessageLoopForUI message_loop;
+  std::unique_ptr<TestMemoryPressureMonitor> monitor(
+      new TestMemoryPressureMonitor);
+  std::unique_ptr<MemoryPressureListener> listener(
+      new MemoryPressureListener(base::Bind(&OnMemoryPressure)));
+  // Checking the memory pressure while 0% are used should not produce any
+  // events.
+  monitor->SetMemoryInPercentOverride(0);
+  ResetOnMemoryPressureCalled();
+
+  monitor->CheckMemoryPressureForTest();
+  RunLoop().RunUntilIdle();
+  EXPECT_FALSE(WasOnMemoryPressureCalled());
+  EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE,
+            monitor->GetCurrentPressureLevel());
+
+  // Setting the memory level to 80% should produce a moderate pressure level.
+  monitor->SetMemoryInPercentOverride(80);
+  monitor->CheckMemoryPressureForTest();
+  RunLoop().RunUntilIdle();
+  EXPECT_TRUE(WasOnMemoryPressureCalled());
+  EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE,
+            monitor->GetCurrentPressureLevel());
+  EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE,
+            on_memory_pressure_level);
+
+  // We need to check that the event gets reposted after a while.
+  int i = 0;
+  for (; i < 100; i++) {
+    monitor->CheckMemoryPressureForTest();
+    RunLoop().RunUntilIdle();
+    EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE,
+              monitor->GetCurrentPressureLevel());
+    if (WasOnMemoryPressureCalled()) {
+      EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE,
+                on_memory_pressure_level);
+      break;
+    }
+  }
+  // Should be more than 5 and less than 100.
+  EXPECT_LE(5, i);
+  EXPECT_GE(99, i);
+
+  // Setting the memory usage to 99% should produce critical levels.
+  monitor->SetMemoryInPercentOverride(99);
+  monitor->CheckMemoryPressureForTest();
+  RunLoop().RunUntilIdle();
+  EXPECT_TRUE(WasOnMemoryPressureCalled());
+  EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL,
+            on_memory_pressure_level);
+  EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL,
+            monitor->GetCurrentPressureLevel());
+
+  // Calling it again should immediately produce a second call.
+  monitor->CheckMemoryPressureForTest();
+  RunLoop().RunUntilIdle();
+  EXPECT_TRUE(WasOnMemoryPressureCalled());
+  EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL,
+            on_memory_pressure_level);
+  EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL,
+            monitor->GetCurrentPressureLevel());
+
+  // When lowering the pressure again we should not get an event, but the
+  // pressure should go back to moderate.
+  monitor->SetMemoryInPercentOverride(80);
+  monitor->CheckMemoryPressureForTest();
+  RunLoop().RunUntilIdle();
+  EXPECT_FALSE(WasOnMemoryPressureCalled());
+  EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE,
+            monitor->GetCurrentPressureLevel());
+
+  // We should need exactly the same amount of calls as before, before the next
+  // call comes in.
+  int j = 0;
+  for (; j < 100; j++) {
+    monitor->CheckMemoryPressureForTest();
+    RunLoop().RunUntilIdle();
+    EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE,
+              monitor->GetCurrentPressureLevel());
+    if (WasOnMemoryPressureCalled()) {
+      EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE,
+                on_memory_pressure_level);
+      break;
+    }
+  }
+  // We should have needed exactly the same amount of checks as before.
+  EXPECT_EQ(j, i);
+}
+
+}  // namespace chromeos
+}  // namespace base
diff --git a/base/memory/memory_pressure_monitor_unittest.cc b/base/memory/memory_pressure_monitor_unittest.cc
new file mode 100644
index 0000000..10d9d24
--- /dev/null
+++ b/base/memory/memory_pressure_monitor_unittest.cc
@@ -0,0 +1,33 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/memory/memory_pressure_monitor.h"
+
+#include "base/macros.h"
+#include "base/memory/memory_pressure_listener.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+TEST(MemoryPressureMonitorTest, RecordMemoryPressure) {
+  base::HistogramTester tester;
+  const char* kHistogram = "Memory.PressureLevel";
+
+  MemoryPressureMonitor::RecordMemoryPressure(
+      MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE, 3);
+  tester.ExpectTotalCount(kHistogram, 3);
+  tester.ExpectBucketCount(kHistogram, 0, 3);
+
+  MemoryPressureMonitor::RecordMemoryPressure(
+      MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE, 2);
+  tester.ExpectTotalCount(kHistogram, 5);
+  tester.ExpectBucketCount(kHistogram, 1, 2);
+
+  MemoryPressureMonitor::RecordMemoryPressure(
+      MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL, 1);
+  tester.ExpectTotalCount(kHistogram, 6);
+  tester.ExpectBucketCount(kHistogram, 2, 1);
+}
+}  // namespace base
diff --git a/base/memory/platform_shared_memory_region_android.cc b/base/memory/platform_shared_memory_region_android.cc
new file mode 100644
index 0000000..6c92b5e
--- /dev/null
+++ b/base/memory/platform_shared_memory_region_android.cc
@@ -0,0 +1,201 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/memory/platform_shared_memory_region.h"
+
+#include <sys/mman.h>
+
+#include "base/memory/shared_memory_tracker.h"
+#include "base/posix/eintr_wrapper.h"
+#include "third_party/ashmem/ashmem.h"
+
+namespace base {
+namespace subtle {
+
+// For Android, we use ashmem to implement SharedMemory. ashmem_create_region
+// will automatically pin the region. We never explicitly call pin/unpin. When
+// all the file descriptors from different processes associated with the region
+// are closed, the memory buffer will go away.
+
+namespace {
+
+static int GetAshmemRegionProtectionMask(int fd) {
+  int prot = ashmem_get_prot_region(fd);
+  if (prot < 0) {
+    DPLOG(ERROR) << "ashmem_get_prot_region failed";
+    return -1;
+  }
+  return prot;
+}
+
+}  // namespace
+
+// static
+PlatformSharedMemoryRegion PlatformSharedMemoryRegion::Take(
+    ScopedFD fd,
+    Mode mode,
+    size_t size,
+    const UnguessableToken& guid) {
+  if (!fd.is_valid())
+    return {};
+
+  if (size == 0)
+    return {};
+
+  if (size > static_cast<size_t>(std::numeric_limits<int>::max()))
+    return {};
+
+  CHECK(CheckPlatformHandlePermissionsCorrespondToMode(fd.get(), mode, size));
+
+  return PlatformSharedMemoryRegion(std::move(fd), mode, size, guid);
+}
+
+int PlatformSharedMemoryRegion::GetPlatformHandle() const {
+  return handle_.get();
+}
+
+bool PlatformSharedMemoryRegion::IsValid() const {
+  return handle_.is_valid();
+}
+
+PlatformSharedMemoryRegion PlatformSharedMemoryRegion::Duplicate() const {
+  if (!IsValid())
+    return {};
+
+  CHECK_NE(mode_, Mode::kWritable)
+      << "Duplicating a writable shared memory region is prohibited";
+
+  ScopedFD duped_fd(HANDLE_EINTR(dup(handle_.get())));
+  if (!duped_fd.is_valid()) {
+    DPLOG(ERROR) << "dup(" << handle_.get() << ") failed";
+    return {};
+  }
+
+  return PlatformSharedMemoryRegion(std::move(duped_fd), mode_, size_, guid_);
+}
+
+bool PlatformSharedMemoryRegion::ConvertToReadOnly() {
+  if (!IsValid())
+    return false;
+
+  CHECK_EQ(mode_, Mode::kWritable)
+      << "Only writable shared memory region can be converted to read-only";
+
+  ScopedFD handle_copy(handle_.release());
+
+  int prot = GetAshmemRegionProtectionMask(handle_copy.get());
+  if (prot < 0)
+    return false;
+
+  prot &= ~PROT_WRITE;
+  int ret = ashmem_set_prot_region(handle_copy.get(), prot);
+  if (ret != 0) {
+    DPLOG(ERROR) << "ashmem_set_prot_region failed";
+    return false;
+  }
+
+  handle_ = std::move(handle_copy);
+  mode_ = Mode::kReadOnly;
+  return true;
+}
+
+bool PlatformSharedMemoryRegion::ConvertToUnsafe() {
+  if (!IsValid())
+    return false;
+
+  CHECK_EQ(mode_, Mode::kWritable)
+      << "Only writable shared memory region can be converted to unsafe";
+
+  mode_ = Mode::kUnsafe;
+  return true;
+}
+
+bool PlatformSharedMemoryRegion::MapAt(off_t offset,
+                                       size_t size,
+                                       void** memory,
+                                       size_t* mapped_size) const {
+  if (!IsValid())
+    return false;
+
+  size_t end_byte;
+  if (!CheckAdd(offset, size).AssignIfValid(&end_byte) || end_byte > size_) {
+    return false;
+  }
+
+  bool write_allowed = mode_ != Mode::kReadOnly;
+  *memory = mmap(nullptr, size, PROT_READ | (write_allowed ? PROT_WRITE : 0),
+                 MAP_SHARED, handle_.get(), offset);
+
+  bool mmap_succeeded = *memory && *memory != reinterpret_cast<void*>(-1);
+  if (!mmap_succeeded) {
+    DPLOG(ERROR) << "mmap " << handle_.get() << " failed";
+    return false;
+  }
+
+  *mapped_size = size;
+  DCHECK_EQ(0U,
+            reinterpret_cast<uintptr_t>(*memory) & (kMapMinimumAlignment - 1));
+  return true;
+}
+
+// static
+PlatformSharedMemoryRegion PlatformSharedMemoryRegion::Create(Mode mode,
+                                                              size_t size) {
+  if (size == 0)
+    return {};
+
+  if (size > static_cast<size_t>(std::numeric_limits<int>::max()))
+    return {};
+
+  CHECK_NE(mode, Mode::kReadOnly) << "Creating a region in read-only mode will "
+                                     "lead to this region being non-modifiable";
+
+  UnguessableToken guid = UnguessableToken::Create();
+
+  ScopedFD fd(ashmem_create_region(
+      SharedMemoryTracker::GetDumpNameForTracing(guid).c_str(), size));
+  if (!fd.is_valid()) {
+    DPLOG(ERROR) << "ashmem_create_region failed";
+    return {};
+  }
+
+  int err = ashmem_set_prot_region(fd.get(), PROT_READ | PROT_WRITE);
+  if (err < 0) {
+    DPLOG(ERROR) << "ashmem_set_prot_region failed";
+    return {};
+  }
+
+  return PlatformSharedMemoryRegion(std::move(fd), mode, size, guid);
+}
+
+bool PlatformSharedMemoryRegion::CheckPlatformHandlePermissionsCorrespondToMode(
+    PlatformHandle handle,
+    Mode mode,
+    size_t size) {
+  int prot = GetAshmemRegionProtectionMask(handle);
+  if (prot < 0)
+    return false;
+
+  bool is_read_only = (prot & PROT_WRITE) == 0;
+  bool expected_read_only = mode == Mode::kReadOnly;
+
+  if (is_read_only != expected_read_only) {
+    DLOG(ERROR) << "Ashmem region has a wrong protection mask: it is"
+                << (is_read_only ? " " : " not ") << "read-only but it should"
+                << (expected_read_only ? " " : " not ") << "be";
+    return false;
+  }
+
+  return true;
+}
+
+PlatformSharedMemoryRegion::PlatformSharedMemoryRegion(
+    ScopedFD fd,
+    Mode mode,
+    size_t size,
+    const UnguessableToken& guid)
+    : handle_(std::move(fd)), mode_(mode), size_(size), guid_(guid) {}
+
+}  // namespace subtle
+}  // namespace base
diff --git a/base/memory/platform_shared_memory_region_mac.cc b/base/memory/platform_shared_memory_region_mac.cc
deleted file mode 100644
index 4a8b440..0000000
--- a/base/memory/platform_shared_memory_region_mac.cc
+++ /dev/null
@@ -1,233 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/memory/platform_shared_memory_region.h"
-
-#include <mach/mach_vm.h>
-
-#include "base/mac/mach_logging.h"
-#include "base/mac/scoped_mach_vm.h"
-#include "base/numerics/checked_math.h"
-#include "build/build_config.h"
-
-#if defined(OS_IOS)
-#error "MacOS only - iOS uses platform_shared_memory_region_posix.cc"
-#endif
-
-namespace base {
-namespace subtle {
-
-// static
-PlatformSharedMemoryRegion PlatformSharedMemoryRegion::Take(
-    mac::ScopedMachSendRight handle,
-    Mode mode,
-    size_t size,
-    const UnguessableToken& guid) {
-  if (!handle.is_valid())
-    return {};
-
-  if (size == 0)
-    return {};
-
-  if (size > static_cast<size_t>(std::numeric_limits<int>::max()))
-    return {};
-
-  CHECK(
-      CheckPlatformHandlePermissionsCorrespondToMode(handle.get(), mode, size));
-
-  return PlatformSharedMemoryRegion(std::move(handle), mode, size, guid);
-}
-
-mach_port_t PlatformSharedMemoryRegion::GetPlatformHandle() const {
-  return handle_.get();
-}
-
-bool PlatformSharedMemoryRegion::IsValid() const {
-  return handle_.is_valid();
-}
-
-PlatformSharedMemoryRegion PlatformSharedMemoryRegion::Duplicate() const {
-  if (!IsValid())
-    return {};
-
-  CHECK_NE(mode_, Mode::kWritable)
-      << "Duplicating a writable shared memory region is prohibited";
-
-  // Increment the ref count.
-  kern_return_t kr = mach_port_mod_refs(mach_task_self(), handle_.get(),
-                                        MACH_PORT_RIGHT_SEND, 1);
-  if (kr != KERN_SUCCESS) {
-    MACH_DLOG(ERROR, kr) << "mach_port_mod_refs";
-    return {};
-  }
-
-  return PlatformSharedMemoryRegion(mac::ScopedMachSendRight(handle_.get()),
-                                    mode_, size_, guid_);
-}
-
-bool PlatformSharedMemoryRegion::ConvertToReadOnly() {
-  return ConvertToReadOnly(nullptr);
-}
-
-bool PlatformSharedMemoryRegion::ConvertToReadOnly(void* mapped_addr) {
-  if (!IsValid())
-    return false;
-
-  CHECK_EQ(mode_, Mode::kWritable)
-      << "Only writable shared memory region can be converted to read-only";
-
-  mac::ScopedMachSendRight handle_copy(handle_.release());
-
-  void* temp_addr = mapped_addr;
-  mac::ScopedMachVM scoped_memory;
-  if (!temp_addr) {
-    // Intentionally lower current prot and max prot to |VM_PROT_READ|.
-    kern_return_t kr = mach_vm_map(
-        mach_task_self(), reinterpret_cast<mach_vm_address_t*>(&temp_addr),
-        size_, 0, VM_FLAGS_ANYWHERE, handle_copy.get(), 0, FALSE, VM_PROT_READ,
-        VM_PROT_READ, VM_INHERIT_NONE);
-    if (kr != KERN_SUCCESS) {
-      MACH_DLOG(ERROR, kr) << "mach_vm_map";
-      return false;
-    }
-    scoped_memory.reset(reinterpret_cast<vm_address_t>(temp_addr),
-                        mach_vm_round_page(size_));
-  }
-
-  // Make new memory object.
-  memory_object_size_t allocation_size = size_;
-  mac::ScopedMachSendRight named_right;
-  kern_return_t kr = mach_make_memory_entry_64(
-      mach_task_self(), &allocation_size,
-      reinterpret_cast<memory_object_offset_t>(temp_addr), VM_PROT_READ,
-      named_right.receive(), MACH_PORT_NULL);
-  if (kr != KERN_SUCCESS) {
-    MACH_DLOG(ERROR, kr) << "mach_make_memory_entry_64";
-    return false;
-  }
-  DCHECK_GE(allocation_size, size_);
-
-  handle_ = std::move(named_right);
-  mode_ = Mode::kReadOnly;
-  return true;
-}
-
-bool PlatformSharedMemoryRegion::ConvertToUnsafe() {
-  if (!IsValid())
-    return false;
-
-  CHECK_EQ(mode_, Mode::kWritable)
-      << "Only writable shared memory region can be converted to unsafe";
-
-  mode_ = Mode::kUnsafe;
-  return true;
-}
-
-bool PlatformSharedMemoryRegion::MapAt(off_t offset,
-                                       size_t size,
-                                       void** memory,
-                                       size_t* mapped_size) const {
-  if (!IsValid())
-    return false;
-
-  size_t end_byte;
-  if (!CheckAdd(offset, size).AssignIfValid(&end_byte) || end_byte > size_) {
-    return false;
-  }
-
-  bool write_allowed = mode_ != Mode::kReadOnly;
-  vm_prot_t vm_prot_write = write_allowed ? VM_PROT_WRITE : 0;
-  kern_return_t kr = mach_vm_map(
-      mach_task_self(),
-      reinterpret_cast<mach_vm_address_t*>(memory),  // Output parameter
-      size,
-      0,  // Alignment mask
-      VM_FLAGS_ANYWHERE, handle_.get(), offset,
-      FALSE,                         // Copy
-      VM_PROT_READ | vm_prot_write,  // Current protection
-      VM_PROT_READ | vm_prot_write,  // Maximum protection
-      VM_INHERIT_NONE);
-  if (kr != KERN_SUCCESS) {
-    MACH_DLOG(ERROR, kr) << "mach_vm_map";
-    return false;
-  }
-
-  *mapped_size = size;
-  DCHECK_EQ(0U,
-            reinterpret_cast<uintptr_t>(*memory) & (kMapMinimumAlignment - 1));
-  return true;
-}
-
-// static
-PlatformSharedMemoryRegion PlatformSharedMemoryRegion::Create(Mode mode,
-                                                              size_t size) {
-  if (size == 0)
-    return {};
-
-  if (size > static_cast<size_t>(std::numeric_limits<int>::max()))
-    return {};
-
-  CHECK_NE(mode, Mode::kReadOnly) << "Creating a region in read-only mode will "
-                                     "lead to this region being non-modifiable";
-
-  mach_vm_size_t vm_size = size;
-  mac::ScopedMachSendRight named_right;
-  kern_return_t kr = mach_make_memory_entry_64(
-      mach_task_self(), &vm_size,
-      0,  // Address.
-      MAP_MEM_NAMED_CREATE | VM_PROT_READ | VM_PROT_WRITE,
-      named_right.receive(),
-      MACH_PORT_NULL);  // Parent handle.
-  if (kr != KERN_SUCCESS) {
-    MACH_DLOG(ERROR, kr) << "mach_make_memory_entry_64";
-    return {};
-  }
-  DCHECK_GE(vm_size, size);
-
-  return PlatformSharedMemoryRegion(std::move(named_right), mode, size,
-                                    UnguessableToken::Create());
-}
-
-// static
-bool PlatformSharedMemoryRegion::CheckPlatformHandlePermissionsCorrespondToMode(
-    PlatformHandle handle,
-    Mode mode,
-    size_t size) {
-  mach_vm_address_t temp_addr = 0;
-  kern_return_t kr =
-      mach_vm_map(mach_task_self(), &temp_addr, size, 0, VM_FLAGS_ANYWHERE,
-                  handle, 0, FALSE, VM_PROT_READ | VM_PROT_WRITE,
-                  VM_PROT_READ | VM_PROT_WRITE, VM_INHERIT_NONE);
-  if (kr == KERN_SUCCESS) {
-    kern_return_t kr_deallocate =
-        mach_vm_deallocate(mach_task_self(), temp_addr, size);
-    MACH_DLOG_IF(ERROR, kr_deallocate != KERN_SUCCESS, kr_deallocate)
-        << "mach_vm_deallocate";
-  } else if (kr != KERN_INVALID_RIGHT) {
-    MACH_DLOG(ERROR, kr) << "mach_vm_map";
-    return false;
-  }
-
-  bool is_read_only = kr == KERN_INVALID_RIGHT;
-  bool expected_read_only = mode == Mode::kReadOnly;
-
-  if (is_read_only != expected_read_only) {
-    DLOG(ERROR) << "VM region has a wrong protection mask: it is"
-                << (is_read_only ? " " : " not ") << "read-only but it should"
-                << (expected_read_only ? " " : " not ") << "be";
-    return false;
-  }
-
-  return true;
-}
-
-PlatformSharedMemoryRegion::PlatformSharedMemoryRegion(
-    mac::ScopedMachSendRight handle,
-    Mode mode,
-    size_t size,
-    const UnguessableToken& guid)
-    : handle_(std::move(handle)), mode_(mode), size_(size), guid_(guid) {}
-
-}  // namespace subtle
-}  // namespace base
diff --git a/base/memory/ptr_util_unittest.cc b/base/memory/ptr_util_unittest.cc
new file mode 100644
index 0000000..3fa40d8
--- /dev/null
+++ b/base/memory/ptr_util_unittest.cc
@@ -0,0 +1,40 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/memory/ptr_util.h"
+
+#include <stddef.h>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+namespace {
+
+class DeleteCounter {
+ public:
+  DeleteCounter() { ++count_; }
+  ~DeleteCounter() { --count_; }
+
+  static size_t count() { return count_; }
+
+ private:
+  static size_t count_;
+};
+
+size_t DeleteCounter::count_ = 0;
+
+}  // namespace
+
+TEST(PtrUtilTest, WrapUnique) {
+  EXPECT_EQ(0u, DeleteCounter::count());
+  DeleteCounter* counter = new DeleteCounter;
+  EXPECT_EQ(1u, DeleteCounter::count());
+  std::unique_ptr<DeleteCounter> owned_counter = WrapUnique(counter);
+  EXPECT_EQ(1u, DeleteCounter::count());
+  owned_counter.reset();
+  EXPECT_EQ(0u, DeleteCounter::count());
+}
+
+}  // namespace base
diff --git a/base/memory/ref_counted_unittest.nc b/base/memory/ref_counted_unittest.nc
new file mode 100644
index 0000000..b8c371f
--- /dev/null
+++ b/base/memory/ref_counted_unittest.nc
@@ -0,0 +1,28 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/memory/ref_counted.h"
+
+namespace base {
+
+class InitialRefCountIsZero : public base::RefCounted<InitialRefCountIsZero> {
+ public:
+  InitialRefCountIsZero() {}
+ private:
+  friend class base::RefCounted<InitialRefCountIsZero>;
+  ~InitialRefCountIsZero() {}
+};
+
+// TODO(hans): Remove .* and update the static_assert expectations once we roll
+// past Clang r313315. https://crbug.com/765692.
+
+#if defined(NCTEST_ADOPT_REF_TO_ZERO_START)  // [r"fatal error: static_assert failed .*\"Use AdoptRef only for the reference count starts from one\.\""]
+
+void WontCompile() {
+  AdoptRef(new InitialRefCountIsZero());
+}
+
+#endif
+
+}  // namespace base
diff --git a/base/memory/shared_memory_tracker.cc b/base/memory/shared_memory_tracker.cc
new file mode 100644
index 0000000..5ca7c84
--- /dev/null
+++ b/base/memory/shared_memory_tracker.cc
@@ -0,0 +1,147 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/memory/shared_memory_tracker.h"
+
+#include "base/memory/shared_memory.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/trace_event/memory_allocator_dump_guid.h"
+#include "base/trace_event/memory_dump_manager.h"
+#include "base/trace_event/process_memory_dump.h"
+
+namespace base {
+
+const char SharedMemoryTracker::kDumpRootName[] = "shared_memory";
+
+// static
+SharedMemoryTracker* SharedMemoryTracker::GetInstance() {
+  static SharedMemoryTracker* instance = new SharedMemoryTracker;
+  return instance;
+}
+
+// static
+std::string SharedMemoryTracker::GetDumpNameForTracing(
+    const UnguessableToken& id) {
+  DCHECK(!id.is_empty());
+  return std::string(kDumpRootName) + "/" + id.ToString();
+}
+
+// static
+trace_event::MemoryAllocatorDumpGuid
+SharedMemoryTracker::GetGlobalDumpIdForTracing(const UnguessableToken& id) {
+  std::string dump_name = GetDumpNameForTracing(id);
+  return trace_event::MemoryAllocatorDumpGuid(dump_name);
+}
+
+// static
+const trace_event::MemoryAllocatorDump*
+SharedMemoryTracker::GetOrCreateSharedMemoryDump(
+    const SharedMemory* shared_memory,
+    trace_event::ProcessMemoryDump* pmd) {
+  return GetOrCreateSharedMemoryDumpInternal(shared_memory->memory(),
+                                             shared_memory->mapped_size(),
+                                             shared_memory->mapped_id(), pmd);
+}
+
+const trace_event::MemoryAllocatorDump*
+SharedMemoryTracker::GetOrCreateSharedMemoryDump(
+    const SharedMemoryMapping& shared_memory,
+    trace_event::ProcessMemoryDump* pmd) {
+  return GetOrCreateSharedMemoryDumpInternal(shared_memory.raw_memory_ptr(),
+                                             shared_memory.mapped_size(),
+                                             shared_memory.guid(), pmd);
+}
+
+void SharedMemoryTracker::IncrementMemoryUsage(
+    const SharedMemory& shared_memory) {
+  AutoLock hold(usages_lock_);
+  DCHECK(usages_.find(shared_memory.memory()) == usages_.end());
+  usages_.emplace(shared_memory.memory(), UsageInfo(shared_memory.mapped_size(),
+                                                    shared_memory.mapped_id()));
+}
+
+void SharedMemoryTracker::IncrementMemoryUsage(
+    const SharedMemoryMapping& mapping) {
+  AutoLock hold(usages_lock_);
+  DCHECK(usages_.find(mapping.raw_memory_ptr()) == usages_.end());
+  usages_.emplace(mapping.raw_memory_ptr(),
+                  UsageInfo(mapping.mapped_size(), mapping.guid()));
+}
+
+void SharedMemoryTracker::DecrementMemoryUsage(
+    const SharedMemory& shared_memory) {
+  AutoLock hold(usages_lock_);
+  DCHECK(usages_.find(shared_memory.memory()) != usages_.end());
+  usages_.erase(shared_memory.memory());
+}
+
+void SharedMemoryTracker::DecrementMemoryUsage(
+    const SharedMemoryMapping& mapping) {
+  AutoLock hold(usages_lock_);
+  DCHECK(usages_.find(mapping.raw_memory_ptr()) != usages_.end());
+  usages_.erase(mapping.raw_memory_ptr());
+}
+
+SharedMemoryTracker::SharedMemoryTracker() {
+  trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
+      this, "SharedMemoryTracker", nullptr);
+}
+
+SharedMemoryTracker::~SharedMemoryTracker() = default;
+
+bool SharedMemoryTracker::OnMemoryDump(const trace_event::MemoryDumpArgs& args,
+                                       trace_event::ProcessMemoryDump* pmd) {
+  AutoLock hold(usages_lock_);
+  for (const auto& usage : usages_) {
+    const trace_event::MemoryAllocatorDump* dump =
+        GetOrCreateSharedMemoryDumpInternal(
+            usage.first, usage.second.mapped_size, usage.second.mapped_id, pmd);
+    DCHECK(dump);
+  }
+  return true;
+}
+
+// static
+const trace_event::MemoryAllocatorDump*
+SharedMemoryTracker::GetOrCreateSharedMemoryDumpInternal(
+    void* mapped_memory,
+    size_t mapped_size,
+    const UnguessableToken& mapped_id,
+    trace_event::ProcessMemoryDump* pmd) {
+  const std::string dump_name = GetDumpNameForTracing(mapped_id);
+  trace_event::MemoryAllocatorDump* local_dump =
+      pmd->GetAllocatorDump(dump_name);
+  if (local_dump)
+    return local_dump;
+
+  size_t virtual_size = mapped_size;
+  // If resident size is not available, a virtual size is used as fallback.
+  size_t size = virtual_size;
+#if defined(COUNT_RESIDENT_BYTES_SUPPORTED)
+  base::Optional<size_t> resident_size =
+      trace_event::ProcessMemoryDump::CountResidentBytesInSharedMemory(
+          mapped_memory, mapped_size);
+  if (resident_size.has_value())
+    size = resident_size.value();
+#endif
+
+  local_dump = pmd->CreateAllocatorDump(dump_name);
+  local_dump->AddScalar(trace_event::MemoryAllocatorDump::kNameSize,
+                        trace_event::MemoryAllocatorDump::kUnitsBytes, size);
+  local_dump->AddScalar("virtual_size",
+                        trace_event::MemoryAllocatorDump::kUnitsBytes,
+                        virtual_size);
+  auto global_dump_guid = GetGlobalDumpIdForTracing(mapped_id);
+  trace_event::MemoryAllocatorDump* global_dump =
+      pmd->CreateSharedGlobalAllocatorDump(global_dump_guid);
+  global_dump->AddScalar(trace_event::MemoryAllocatorDump::kNameSize,
+                         trace_event::MemoryAllocatorDump::kUnitsBytes, size);
+
+  // The edges will be overriden by the clients with correct importance.
+  pmd->AddOverridableOwnershipEdge(local_dump->guid(), global_dump->guid(),
+                                   0 /* importance */);
+  return local_dump;
+}
+
+}  // namespace
diff --git a/base/memory/shared_memory_tracker.h b/base/memory/shared_memory_tracker.h
new file mode 100644
index 0000000..499b172
--- /dev/null
+++ b/base/memory/shared_memory_tracker.h
@@ -0,0 +1,90 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_MEMORY_SHARED_MEMORY_TRACKER_H_
+#define BASE_MEMORY_SHARED_MEMORY_TRACKER_H_
+
+#include <map>
+#include <string>
+
+#include "base/memory/shared_memory.h"
+#include "base/memory/shared_memory_mapping.h"
+#include "base/synchronization/lock.h"
+#include "base/trace_event/memory_dump_provider.h"
+
+namespace base {
+
+namespace trace_event {
+class MemoryAllocatorDump;
+class MemoryAllocatorDumpGuid;
+class ProcessMemoryDump;
+}
+
+// SharedMemoryTracker tracks shared memory usage.
+class BASE_EXPORT SharedMemoryTracker : public trace_event::MemoryDumpProvider {
+ public:
+  // Returns a singleton instance.
+  static SharedMemoryTracker* GetInstance();
+
+  static std::string GetDumpNameForTracing(const UnguessableToken& id);
+
+  static trace_event::MemoryAllocatorDumpGuid GetGlobalDumpIdForTracing(
+      const UnguessableToken& id);
+
+  // Gets or creates if non-existant, a memory dump for the |shared_memory|
+  // inside the given |pmd|. Also adds the necessary edges for the dump when
+  // creating the dump.
+  static const trace_event::MemoryAllocatorDump* GetOrCreateSharedMemoryDump(
+      const SharedMemory* shared_memory,
+      trace_event::ProcessMemoryDump* pmd);
+  // We're in the middle of a refactor https://crbug.com/795291. Eventually, the
+  // first call will go away.
+  static const trace_event::MemoryAllocatorDump* GetOrCreateSharedMemoryDump(
+      const SharedMemoryMapping& shared_memory,
+      trace_event::ProcessMemoryDump* pmd);
+
+  // Records shared memory usage on valid mapping.
+  void IncrementMemoryUsage(const SharedMemory& shared_memory);
+  void IncrementMemoryUsage(const SharedMemoryMapping& mapping);
+
+  // Records shared memory usage on unmapping.
+  void DecrementMemoryUsage(const SharedMemory& shared_memory);
+  void DecrementMemoryUsage(const SharedMemoryMapping& mapping);
+
+  // Root dump name for all shared memory dumps.
+  static const char kDumpRootName[];
+
+ private:
+  SharedMemoryTracker();
+  ~SharedMemoryTracker() override;
+
+  // trace_event::MemoryDumpProvider implementation.
+  bool OnMemoryDump(const trace_event::MemoryDumpArgs& args,
+                    trace_event::ProcessMemoryDump* pmd) override;
+
+  static const trace_event::MemoryAllocatorDump*
+  GetOrCreateSharedMemoryDumpInternal(void* mapped_memory,
+                                      size_t mapped_size,
+                                      const UnguessableToken& mapped_id,
+                                      trace_event::ProcessMemoryDump* pmd);
+
+  // Information associated with each mapped address.
+  struct UsageInfo {
+    UsageInfo(size_t size, const UnguessableToken& id)
+        : mapped_size(size), mapped_id(id) {}
+
+    size_t mapped_size;
+    UnguessableToken mapped_id;
+  };
+
+  // Used to lock when |usages_| is modified or read.
+  Lock usages_lock_;
+  std::map<void*, UsageInfo> usages_;
+
+  DISALLOW_COPY_AND_ASSIGN(SharedMemoryTracker);
+};
+
+}  // namespace base
+
+#endif  // BASE_MEMORY_SHARED_MEMORY_TRACKER_H_
diff --git a/base/message_loop/message_pump_android.cc b/base/message_loop/message_pump_android.cc
new file mode 100644
index 0000000..3fd5567
--- /dev/null
+++ b/base/message_loop/message_pump_android.cc
@@ -0,0 +1,313 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/message_loop/message_pump_android.h"
+
+#include <android/looper.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <jni.h>
+#include <sys/eventfd.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <utility>
+
+#include "base/android/jni_android.h"
+#include "base/android/scoped_java_ref.h"
+#include "base/callback_helpers.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/run_loop.h"
+
+// Android stripped sys/timerfd.h out of their platform headers, so we have to
+// use syscall to make use of timerfd. Once the min API level is 20, we can
+// directly use timerfd.h.
+#ifndef __NR_timerfd_create
+#error "Unable to find syscall for __NR_timerfd_create"
+#endif
+
+#ifndef TFD_TIMER_ABSTIME
+#define TFD_TIMER_ABSTIME (1 << 0)
+#endif
+
+using base::android::JavaParamRef;
+using base::android::ScopedJavaLocalRef;
+
+namespace base {
+
+namespace {
+
+// See sys/timerfd.h
+int timerfd_create(int clockid, int flags) {
+  return syscall(__NR_timerfd_create, clockid, flags);
+}
+
+// See sys/timerfd.h
+int timerfd_settime(int ufc,
+                    int flags,
+                    const struct itimerspec* utmr,
+                    struct itimerspec* otmr) {
+  return syscall(__NR_timerfd_settime, ufc, flags, utmr, otmr);
+}
+
+int NonDelayedLooperCallback(int fd, int events, void* data) {
+  if (events & ALOOPER_EVENT_HANGUP)
+    return 0;
+
+  DCHECK(events & ALOOPER_EVENT_INPUT);
+  MessagePumpForUI* pump = reinterpret_cast<MessagePumpForUI*>(data);
+  pump->OnNonDelayedLooperCallback();
+  return 1;  // continue listening for events
+}
+
+int DelayedLooperCallback(int fd, int events, void* data) {
+  if (events & ALOOPER_EVENT_HANGUP)
+    return 0;
+
+  DCHECK(events & ALOOPER_EVENT_INPUT);
+  MessagePumpForUI* pump = reinterpret_cast<MessagePumpForUI*>(data);
+  pump->OnDelayedLooperCallback();
+  return 1;  // continue listening for events
+}
+
+}  // namespace
+
+MessagePumpForUI::MessagePumpForUI() {
+  // The Android native ALooper uses epoll to poll our file descriptors and wake
+  // us up. We use a simple level-triggered eventfd to signal that non-delayed
+  // work is available, and a timerfd to signal when delayed work is ready to
+  // be run.
+  non_delayed_fd_ = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
+  CHECK_NE(non_delayed_fd_, -1);
+  DCHECK_EQ(TimeTicks::GetClock(), TimeTicks::Clock::LINUX_CLOCK_MONOTONIC);
+
+  // We can't create the timerfd with TFD_NONBLOCK | TFD_CLOEXEC as we can't
+  // include timerfd.h. See comments above on __NR_timerfd_create. It looks like
+  // they're just aliases to O_NONBLOCK and O_CLOEXEC anyways, so this should be
+  // fine.
+  delayed_fd_ = timerfd_create(CLOCK_MONOTONIC, O_NONBLOCK | O_CLOEXEC);
+  CHECK_NE(delayed_fd_, -1);
+
+  looper_ = ALooper_prepare(0);
+  DCHECK(looper_);
+  // Add a reference to the looper so it isn't deleted on us.
+  ALooper_acquire(looper_);
+  ALooper_addFd(looper_, non_delayed_fd_, 0, ALOOPER_EVENT_INPUT,
+                &NonDelayedLooperCallback, reinterpret_cast<void*>(this));
+  ALooper_addFd(looper_, delayed_fd_, 0, ALOOPER_EVENT_INPUT,
+                &DelayedLooperCallback, reinterpret_cast<void*>(this));
+}
+
+MessagePumpForUI::~MessagePumpForUI() {
+  DCHECK_EQ(ALooper_forThread(), looper_);
+  ALooper_removeFd(looper_, non_delayed_fd_);
+  ALooper_removeFd(looper_, delayed_fd_);
+  ALooper_release(looper_);
+  looper_ = nullptr;
+
+  close(non_delayed_fd_);
+  close(delayed_fd_);
+}
+
+void MessagePumpForUI::OnDelayedLooperCallback() {
+  if (ShouldQuit())
+    return;
+
+  // Clear the fd.
+  uint64_t value;
+  int ret = read(delayed_fd_, &value, sizeof(value));
+  DCHECK_GE(ret, 0);
+  delayed_scheduled_time_ = base::TimeTicks();
+
+  base::TimeTicks next_delayed_work_time;
+  delegate_->DoDelayedWork(&next_delayed_work_time);
+  if (!next_delayed_work_time.is_null()) {
+    ScheduleDelayedWork(next_delayed_work_time);
+  }
+  if (ShouldQuit())
+    return;
+  // We may be idle now, so pump the loop to find out.
+  ScheduleWork();
+}
+
+void MessagePumpForUI::OnNonDelayedLooperCallback() {
+  base::TimeTicks next_delayed_work_time;
+  bool did_any_work = false;
+
+  // Runs all native tasks scheduled to run, scheduling delayed work if
+  // necessary.
+  while (true) {
+    bool did_work_this_loop = false;
+    if (ShouldQuit())
+      return;
+    did_work_this_loop = delegate_->DoWork();
+    if (ShouldQuit())
+      return;
+
+    did_work_this_loop |= delegate_->DoDelayedWork(&next_delayed_work_time);
+
+    did_any_work |= did_work_this_loop;
+
+    // If we didn't do any work, we're out of native tasks to run, and we should
+    // return control to the looper to run Java tasks.
+    if (!did_work_this_loop)
+      break;
+  }
+  // If we did any work, return control to the looper to run java tasks before
+  // we call DoIdleWork(). We haven't cleared the fd yet, so we'll get woken up
+  // again soon to check for idle-ness.
+  if (did_any_work)
+    return;
+  if (ShouldQuit())
+    return;
+
+  // Read the file descriptor, resetting its contents to 0 and reading back the
+  // stored value.
+  // See http://man7.org/linux/man-pages/man2/eventfd.2.html
+  uint64_t value = 0;
+  int ret = read(non_delayed_fd_, &value, sizeof(value));
+  DCHECK_GE(ret, 0);
+
+  // If we read a value > 1, it means we lost the race to clear the fd before a
+  // new task was posted. This is okay, we can just re-schedule work.
+  if (value > 1) {
+    ScheduleWork();
+  } else {
+    // At this point, the java looper might not be idle - it's impossible to
+    // know pre-Android-M, so we may end up doing Idle work while java tasks are
+    // still queued up. Note that this won't cause us to fail to run java tasks
+    // using QuitWhenIdle, as the JavaHandlerThread will finish running all
+    // currently scheduled tasks before it quits. Also note that we can't just
+    // add an idle callback to the java looper, as that will fire even if native
+    // tasks are still queued up.
+    DoIdleWork();
+    if (!next_delayed_work_time.is_null()) {
+      ScheduleDelayedWork(next_delayed_work_time);
+    }
+  }
+}
+
+void MessagePumpForUI::DoIdleWork() {
+  if (delegate_->DoIdleWork()) {
+    // If DoIdleWork() resulted in any work, we're not idle yet. We need to pump
+    // the loop here because we may in fact be idle after doing idle work
+    // without any new tasks being queued.
+    ScheduleWork();
+  }
+}
+
+void MessagePumpForUI::Run(Delegate* delegate) {
+  DCHECK(IsTestImplementation());
+  // This function is only called in tests. We manually pump the native looper
+  // which won't run any java tasks.
+  quit_ = false;
+
+  SetDelegate(delegate);
+
+  // Pump the loop once in case we're starting off idle as ALooper_pollOnce will
+  // never return in that case.
+  ScheduleWork();
+  while (true) {
+    // Waits for either the delayed, or non-delayed fds to be signalled, calling
+    // either OnDelayedLooperCallback, or OnNonDelayedLooperCallback,
+    // respectively. This uses Android's Looper implementation, which is based
+    // off of epoll.
+    ALooper_pollOnce(-1, nullptr, nullptr, nullptr);
+    if (quit_)
+      break;
+  }
+}
+
+void MessagePumpForUI::Attach(Delegate* delegate) {
+  DCHECK(!quit_);
+
+  // Since the Looper is controlled by the UI thread or JavaHandlerThread, we
+  // can't use Run() like we do on other platforms or we would prevent Java
+  // tasks from running. Instead we create and initialize a run loop here, then
+  // return control back to the Looper.
+
+  SetDelegate(delegate);
+  run_loop_ = std::make_unique<RunLoop>();
+  // Since the RunLoop was just created above, BeforeRun should be guaranteed to
+  // return true (it only returns false if the RunLoop has been Quit already).
+  if (!run_loop_->BeforeRun())
+    NOTREACHED();
+}
+
+void MessagePumpForUI::Quit() {
+  if (quit_)
+    return;
+
+  quit_ = true;
+
+  int64_t value;
+  // Clear any pending timer.
+  read(delayed_fd_, &value, sizeof(value));
+  // Clear the eventfd.
+  read(non_delayed_fd_, &value, sizeof(value));
+
+  if (run_loop_) {
+    run_loop_->AfterRun();
+    run_loop_ = nullptr;
+  }
+  if (on_quit_callback_) {
+    std::move(on_quit_callback_).Run();
+  }
+}
+
+void MessagePumpForUI::ScheduleWork() {
+  if (ShouldQuit())
+    return;
+
+  // Write (add) 1 to the eventfd. This tells the Looper to wake up and call our
+  // callback, allowing us to run tasks. This also allows us to detect, when we
+  // clear the fd, whether additional work was scheduled after we finished
+  // performing work, but before we cleared the fd, as we'll read back >=2
+  // instead of 1 in that case.
+  // See the eventfd man pages
+  // (http://man7.org/linux/man-pages/man2/eventfd.2.html) for details on how
+  // the read and write APIs for this file descriptor work, specifically without
+  // EFD_SEMAPHORE.
+  uint64_t value = 1;
+  int ret = write(non_delayed_fd_, &value, sizeof(value));
+  DCHECK_GE(ret, 0);
+}
+
+void MessagePumpForUI::ScheduleDelayedWork(const TimeTicks& delayed_work_time) {
+  if (ShouldQuit())
+    return;
+
+  if (!delayed_scheduled_time_.is_null() &&
+      delayed_work_time >= delayed_scheduled_time_) {
+    return;
+  }
+
+  DCHECK(!delayed_work_time.is_null());
+  delayed_scheduled_time_ = delayed_work_time;
+  int64_t nanos = delayed_work_time.since_origin().InNanoseconds();
+  struct itimerspec ts;
+  ts.it_interval.tv_sec = 0;  // Don't repeat.
+  ts.it_interval.tv_nsec = 0;
+  ts.it_value.tv_sec = nanos / TimeTicks::kNanosecondsPerSecond;
+  ts.it_value.tv_nsec = nanos % TimeTicks::kNanosecondsPerSecond;
+
+  int ret = timerfd_settime(delayed_fd_, TFD_TIMER_ABSTIME, &ts, nullptr);
+  DCHECK_GE(ret, 0);
+}
+
+void MessagePumpForUI::QuitWhenIdle(base::OnceClosure callback) {
+  DCHECK(!on_quit_callback_);
+  DCHECK(run_loop_);
+  on_quit_callback_ = std::move(callback);
+  run_loop_->QuitWhenIdle();
+  // Pump the loop in case we're already idle.
+  ScheduleWork();
+}
+
+bool MessagePumpForUI::IsTestImplementation() const {
+  return false;
+}
+
+}  // namespace base
diff --git a/base/message_loop/message_pump_android.h b/base/message_loop/message_pump_android.h
new file mode 100644
index 0000000..d7e0f50
--- /dev/null
+++ b/base/message_loop/message_pump_android.h
@@ -0,0 +1,102 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_MESSAGE_LOOP_MESSAGE_PUMP_ANDROID_H_
+#define BASE_MESSAGE_LOOP_MESSAGE_PUMP_ANDROID_H_
+
+#include <jni.h>
+#include <memory>
+
+#include "base/android/scoped_java_ref.h"
+#include "base/base_export.h"
+#include "base/callback.h"
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/message_loop/message_pump.h"
+#include "base/time/time.h"
+
+struct ALooper;
+
+namespace base {
+
+class RunLoop;
+
+// This class implements a MessagePump needed for TYPE_UI MessageLoops on
+// OS_ANDROID platform.
+class BASE_EXPORT MessagePumpForUI : public MessagePump {
+ public:
+  MessagePumpForUI();
+  ~MessagePumpForUI() override;
+
+  void Run(Delegate* delegate) override;
+  void Quit() override;
+  void ScheduleWork() override;
+  void ScheduleDelayedWork(const TimeTicks& delayed_work_time) override;
+
+  // Attaches |delegate| to this native MessagePump. |delegate| will from then
+  // on be invoked by the native loop to process application tasks.
+  virtual void Attach(Delegate* delegate);
+
+  // We call Abort when there is a pending JNI exception, meaning that the
+  // current thread will crash when we return to Java.
+  // We can't call any JNI-methods before returning to Java as we would then
+  // cause a native crash (instead of the original Java crash).
+  void Abort() { should_abort_ = true; }
+  bool IsAborted() { return should_abort_; }
+  bool ShouldQuit() const { return should_abort_ || quit_; }
+
+  // Tells the RunLoop to quit when idle, calling the callback when it's safe
+  // for the Thread to stop.
+  void QuitWhenIdle(base::OnceClosure callback);
+
+  // These functions are only public so that the looper callbacks can call them,
+  // and should not be called from outside this class.
+  void OnDelayedLooperCallback();
+  void OnNonDelayedLooperCallback();
+
+ protected:
+  void SetDelegate(Delegate* delegate) { delegate_ = delegate; }
+  virtual bool IsTestImplementation() const;
+
+ private:
+  void DoIdleWork();
+
+  // Unlike other platforms, we don't control the message loop as it's
+  // controlled by the Android Looper, so we can't run a RunLoop to keep the
+  // Thread this pump belongs to alive. However, threads are expected to have an
+  // active run loop, so we manage a RunLoop internally here, starting/stopping
+  // it as necessary.
+  std::unique_ptr<RunLoop> run_loop_;
+
+  // See Abort().
+  bool should_abort_ = false;
+
+  // Whether this message pump is quitting, or has quit.
+  bool quit_ = false;
+
+  // The MessageLoop::Delegate for this pump.
+  Delegate* delegate_ = nullptr;
+
+  // The time at which we are currently scheduled to wake up and perform a
+  // delayed task.
+  base::TimeTicks delayed_scheduled_time_;
+
+  // If set, a callback to fire when the message pump is quit.
+  base::OnceClosure on_quit_callback_;
+
+  // The file descriptor used to signal that non-delayed work is available.
+  int non_delayed_fd_;
+
+  // The file descriptor used to signal that delayed work is available.
+  int delayed_fd_;
+
+  // The Android Looper for this thread.
+  ALooper* looper_ = nullptr;
+
+  DISALLOW_COPY_AND_ASSIGN(MessagePumpForUI);
+};
+
+}  // namespace base
+
+#endif  // BASE_MESSAGE_LOOP_MESSAGE_PUMP_ANDROID_H_
diff --git a/base/message_loop/message_pump_libevent_unittest.cc b/base/message_loop/message_pump_libevent_unittest.cc
new file mode 100644
index 0000000..55eb0b4
--- /dev/null
+++ b/base/message_loop/message_pump_libevent_unittest.cc
@@ -0,0 +1,263 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/message_loop/message_pump_libevent.h"
+
+#include <unistd.h>
+
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/files/file_util.h"
+#include "base/memory/ptr_util.h"
+#include "base/message_loop/message_loop.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/run_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/synchronization/waitable_event_watcher.h"
+#include "base/test/gtest_util.h"
+#include "base/third_party/libevent/event.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "base/threading/thread.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+class MessagePumpLibeventTest : public testing::Test {
+ protected:
+  MessagePumpLibeventTest()
+      : ui_loop_(new MessageLoop(MessageLoop::TYPE_UI)),
+        io_thread_("MessagePumpLibeventTestIOThread") {}
+  ~MessagePumpLibeventTest() override = default;
+
+  void SetUp() override {
+    Thread::Options options(MessageLoop::TYPE_IO, 0);
+    ASSERT_TRUE(io_thread_.StartWithOptions(options));
+    ASSERT_EQ(MessageLoop::TYPE_IO, io_thread_.message_loop()->type());
+    int ret = pipe(pipefds_);
+    ASSERT_EQ(0, ret);
+  }
+
+  void TearDown() override {
+    if (IGNORE_EINTR(close(pipefds_[0])) < 0)
+      PLOG(ERROR) << "close";
+    if (IGNORE_EINTR(close(pipefds_[1])) < 0)
+      PLOG(ERROR) << "close";
+  }
+
+  void WaitUntilIoThreadStarted() {
+    ASSERT_TRUE(io_thread_.WaitUntilThreadStarted());
+  }
+
+  scoped_refptr<SingleThreadTaskRunner> io_runner() const {
+    return io_thread_.task_runner();
+  }
+
+  void OnLibeventNotification(
+      MessagePumpLibevent* pump,
+      MessagePumpLibevent::FdWatchController* controller) {
+    pump->OnLibeventNotification(0, EV_WRITE | EV_READ, controller);
+  }
+
+  int pipefds_[2];
+  std::unique_ptr<MessageLoop> ui_loop_;
+
+ private:
+  Thread io_thread_;
+};
+
+namespace {
+
+// Concrete implementation of MessagePumpLibevent::FdWatcher that does
+// nothing useful.
+class StupidWatcher : public MessagePumpLibevent::FdWatcher {
+ public:
+  ~StupidWatcher() override = default;
+
+  // base:MessagePumpLibevent::FdWatcher interface
+  void OnFileCanReadWithoutBlocking(int fd) override {}
+  void OnFileCanWriteWithoutBlocking(int fd) override {}
+};
+
+TEST_F(MessagePumpLibeventTest, QuitOutsideOfRun) {
+  std::unique_ptr<MessagePumpLibevent> pump(new MessagePumpLibevent);
+  ASSERT_DCHECK_DEATH(pump->Quit());
+}
+
+class BaseWatcher : public MessagePumpLibevent::FdWatcher {
+ public:
+  explicit BaseWatcher(MessagePumpLibevent::FdWatchController* controller)
+      : controller_(controller) {
+    DCHECK(controller_);
+  }
+  ~BaseWatcher() override = default;
+
+  // base:MessagePumpLibevent::FdWatcher interface
+  void OnFileCanReadWithoutBlocking(int /* fd */) override { NOTREACHED(); }
+
+  void OnFileCanWriteWithoutBlocking(int /* fd */) override { NOTREACHED(); }
+
+ protected:
+  MessagePumpLibevent::FdWatchController* controller_;
+};
+
+class DeleteWatcher : public BaseWatcher {
+ public:
+  explicit DeleteWatcher(MessagePumpLibevent::FdWatchController* controller)
+      : BaseWatcher(controller) {}
+
+  ~DeleteWatcher() override { DCHECK(!controller_); }
+
+  void OnFileCanWriteWithoutBlocking(int /* fd */) override {
+    DCHECK(controller_);
+    delete controller_;
+    controller_ = nullptr;
+  }
+};
+
+TEST_F(MessagePumpLibeventTest, DeleteWatcher) {
+  std::unique_ptr<MessagePumpLibevent> pump(new MessagePumpLibevent);
+  MessagePumpLibevent::FdWatchController* watcher =
+      new MessagePumpLibevent::FdWatchController(FROM_HERE);
+  DeleteWatcher delegate(watcher);
+  pump->WatchFileDescriptor(pipefds_[1],
+      false, MessagePumpLibevent::WATCH_READ_WRITE, watcher, &delegate);
+
+  // Spoof a libevent notification.
+  OnLibeventNotification(pump.get(), watcher);
+}
+
+class StopWatcher : public BaseWatcher {
+ public:
+  explicit StopWatcher(MessagePumpLibevent::FdWatchController* controller)
+      : BaseWatcher(controller) {}
+
+  ~StopWatcher() override = default;
+
+  void OnFileCanWriteWithoutBlocking(int /* fd */) override {
+    controller_->StopWatchingFileDescriptor();
+  }
+};
+
+TEST_F(MessagePumpLibeventTest, StopWatcher) {
+  std::unique_ptr<MessagePumpLibevent> pump(new MessagePumpLibevent);
+  MessagePumpLibevent::FdWatchController watcher(FROM_HERE);
+  StopWatcher delegate(&watcher);
+  pump->WatchFileDescriptor(pipefds_[1],
+      false, MessagePumpLibevent::WATCH_READ_WRITE, &watcher, &delegate);
+
+  // Spoof a libevent notification.
+  OnLibeventNotification(pump.get(), &watcher);
+}
+
+void QuitMessageLoopAndStart(const Closure& quit_closure) {
+  quit_closure.Run();
+
+  RunLoop runloop(RunLoop::Type::kNestableTasksAllowed);
+  ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, runloop.QuitClosure());
+  runloop.Run();
+}
+
+class NestedPumpWatcher : public MessagePumpLibevent::FdWatcher {
+ public:
+  NestedPumpWatcher() = default;
+  ~NestedPumpWatcher() override = default;
+
+  void OnFileCanReadWithoutBlocking(int /* fd */) override {
+    RunLoop runloop;
+    ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE, BindOnce(&QuitMessageLoopAndStart, runloop.QuitClosure()));
+    runloop.Run();
+  }
+
+  void OnFileCanWriteWithoutBlocking(int /* fd */) override {}
+};
+
+TEST_F(MessagePumpLibeventTest, NestedPumpWatcher) {
+  std::unique_ptr<MessagePumpLibevent> pump(new MessagePumpLibevent);
+  MessagePumpLibevent::FdWatchController watcher(FROM_HERE);
+  NestedPumpWatcher delegate;
+  pump->WatchFileDescriptor(pipefds_[1],
+      false, MessagePumpLibevent::WATCH_READ, &watcher, &delegate);
+
+  // Spoof a libevent notification.
+  OnLibeventNotification(pump.get(), &watcher);
+}
+
+void FatalClosure() {
+  FAIL() << "Reached fatal closure.";
+}
+
+class QuitWatcher : public BaseWatcher {
+ public:
+  QuitWatcher(MessagePumpLibevent::FdWatchController* controller,
+              base::Closure quit_closure)
+      : BaseWatcher(controller), quit_closure_(std::move(quit_closure)) {}
+
+  void OnFileCanReadWithoutBlocking(int /* fd */) override {
+    // Post a fatal closure to the MessageLoop before we quit it.
+    ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, BindOnce(&FatalClosure));
+
+    quit_closure_.Run();
+  }
+
+ private:
+  base::Closure quit_closure_;
+};
+
+void WriteFDWrapper(const int fd,
+                    const char* buf,
+                    int size,
+                    WaitableEvent* event) {
+  ASSERT_TRUE(WriteFileDescriptor(fd, buf, size));
+}
+
+// Tests that MessagePumpLibevent quits immediately when it is quit from
+// libevent's event_base_loop().
+TEST_F(MessagePumpLibeventTest, QuitWatcher) {
+  // Delete the old MessageLoop so that we can manage our own one here.
+  ui_loop_.reset();
+
+  MessagePumpLibevent* pump = new MessagePumpLibevent;  // owned by |loop|.
+  MessageLoop loop(WrapUnique(pump));
+  RunLoop run_loop;
+  MessagePumpLibevent::FdWatchController controller(FROM_HERE);
+  QuitWatcher delegate(&controller, run_loop.QuitClosure());
+  WaitableEvent event(WaitableEvent::ResetPolicy::AUTOMATIC,
+                      WaitableEvent::InitialState::NOT_SIGNALED);
+  std::unique_ptr<WaitableEventWatcher> watcher(new WaitableEventWatcher);
+
+  // Tell the pump to watch the pipe.
+  pump->WatchFileDescriptor(pipefds_[0], false, MessagePumpLibevent::WATCH_READ,
+                            &controller, &delegate);
+
+  // Make the IO thread wait for |event| before writing to pipefds[1].
+  const char buf = 0;
+  WaitableEventWatcher::EventCallback write_fd_task =
+      BindOnce(&WriteFDWrapper, pipefds_[1], &buf, 1);
+  io_runner()->PostTask(
+      FROM_HERE, BindOnce(IgnoreResult(&WaitableEventWatcher::StartWatching),
+                          Unretained(watcher.get()), &event,
+                          std::move(write_fd_task), io_runner()));
+
+  // Queue |event| to signal on |loop|.
+  loop.task_runner()->PostTask(
+      FROM_HERE, BindOnce(&WaitableEvent::Signal, Unretained(&event)));
+
+  // Now run the MessageLoop.
+  run_loop.Run();
+
+  // StartWatching can move |watcher| to IO thread. Release on IO thread.
+  io_runner()->PostTask(FROM_HERE, BindOnce(&WaitableEventWatcher::StopWatching,
+                                            Owned(watcher.release())));
+}
+
+}  // namespace
+
+}  // namespace base
diff --git a/base/message_loop/message_pump_perftest.cc b/base/message_loop/message_pump_perftest.cc
new file mode 100644
index 0000000..71ed491
--- /dev/null
+++ b/base/message_loop/message_pump_perftest.cc
@@ -0,0 +1,243 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/format_macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/message_loop/message_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "base/strings/stringprintf.h"
+#include "base/synchronization/condition_variable.h"
+#include "base/synchronization/lock.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread.h"
+#include "base/time/time.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/perf/perf_test.h"
+
+#if defined(OS_ANDROID)
+#include "base/android/java_handler_thread.h"
+#endif
+
+namespace base {
+
+class ScheduleWorkTest : public testing::Test {
+ public:
+  ScheduleWorkTest() : counter_(0) {}
+
+  void SetUp() override {
+    if (base::ThreadTicks::IsSupported())
+      base::ThreadTicks::WaitUntilInitialized();
+  }
+
+  void Increment(uint64_t amount) { counter_ += amount; }
+
+  void Schedule(int index) {
+    base::TimeTicks start = base::TimeTicks::Now();
+    base::ThreadTicks thread_start;
+    if (ThreadTicks::IsSupported())
+      thread_start = base::ThreadTicks::Now();
+    base::TimeDelta minimum = base::TimeDelta::Max();
+    base::TimeDelta maximum = base::TimeDelta();
+    base::TimeTicks now, lastnow = start;
+    uint64_t schedule_calls = 0u;
+    do {
+      for (size_t i = 0; i < kBatchSize; ++i) {
+        target_message_loop()->ScheduleWork();
+        schedule_calls++;
+      }
+      now = base::TimeTicks::Now();
+      base::TimeDelta laptime = now - lastnow;
+      lastnow = now;
+      minimum = std::min(minimum, laptime);
+      maximum = std::max(maximum, laptime);
+    } while (now - start < base::TimeDelta::FromSeconds(kTargetTimeSec));
+
+    scheduling_times_[index] = now - start;
+    if (ThreadTicks::IsSupported())
+      scheduling_thread_times_[index] =
+          base::ThreadTicks::Now() - thread_start;
+    min_batch_times_[index] = minimum;
+    max_batch_times_[index] = maximum;
+    target_message_loop()->task_runner()->PostTask(
+        FROM_HERE, base::BindOnce(&ScheduleWorkTest::Increment,
+                                  base::Unretained(this), schedule_calls));
+  }
+
+  void ScheduleWork(MessageLoop::Type target_type, int num_scheduling_threads) {
+#if defined(OS_ANDROID)
+    if (target_type == MessageLoop::TYPE_JAVA) {
+      java_thread_.reset(new android::JavaHandlerThread("target"));
+      java_thread_->Start();
+    } else
+#endif
+    {
+      target_.reset(new Thread("target"));
+      target_->StartWithOptions(Thread::Options(target_type, 0u));
+
+      // Without this, it's possible for the scheduling threads to start and run
+      // before the target thread. In this case, the scheduling threads will
+      // call target_message_loop()->ScheduleWork(), which dereferences the
+      // loop's message pump, which is only created after the target thread has
+      // finished starting.
+      target_->WaitUntilThreadStarted();
+    }
+
+    std::vector<std::unique_ptr<Thread>> scheduling_threads;
+    scheduling_times_.reset(new base::TimeDelta[num_scheduling_threads]);
+    scheduling_thread_times_.reset(new base::TimeDelta[num_scheduling_threads]);
+    min_batch_times_.reset(new base::TimeDelta[num_scheduling_threads]);
+    max_batch_times_.reset(new base::TimeDelta[num_scheduling_threads]);
+
+    for (int i = 0; i < num_scheduling_threads; ++i) {
+      scheduling_threads.push_back(std::make_unique<Thread>("posting thread"));
+      scheduling_threads[i]->Start();
+    }
+
+    for (int i = 0; i < num_scheduling_threads; ++i) {
+      scheduling_threads[i]->task_runner()->PostTask(
+          FROM_HERE, base::BindOnce(&ScheduleWorkTest::Schedule,
+                                    base::Unretained(this), i));
+    }
+
+    for (int i = 0; i < num_scheduling_threads; ++i) {
+      scheduling_threads[i]->Stop();
+    }
+#if defined(OS_ANDROID)
+    if (target_type == MessageLoop::TYPE_JAVA) {
+      java_thread_->Stop();
+      java_thread_.reset();
+    } else
+#endif
+    {
+      target_->Stop();
+      target_.reset();
+    }
+    base::TimeDelta total_time;
+    base::TimeDelta total_thread_time;
+    base::TimeDelta min_batch_time = base::TimeDelta::Max();
+    base::TimeDelta max_batch_time = base::TimeDelta();
+    for (int i = 0; i < num_scheduling_threads; ++i) {
+      total_time += scheduling_times_[i];
+      total_thread_time += scheduling_thread_times_[i];
+      min_batch_time = std::min(min_batch_time, min_batch_times_[i]);
+      max_batch_time = std::max(max_batch_time, max_batch_times_[i]);
+    }
+    std::string trace = StringPrintf(
+        "%d_threads_scheduling_to_%s_pump",
+        num_scheduling_threads,
+        target_type == MessageLoop::TYPE_IO
+            ? "io"
+            : (target_type == MessageLoop::TYPE_UI ? "ui" : "default"));
+    perf_test::PrintResult(
+        "task",
+        "",
+        trace,
+        total_time.InMicroseconds() / static_cast<double>(counter_),
+        "us/task",
+        true);
+    perf_test::PrintResult(
+        "task",
+        "_min_batch_time",
+        trace,
+        min_batch_time.InMicroseconds() / static_cast<double>(kBatchSize),
+        "us/task",
+        false);
+    perf_test::PrintResult(
+        "task",
+        "_max_batch_time",
+        trace,
+        max_batch_time.InMicroseconds() / static_cast<double>(kBatchSize),
+        "us/task",
+        false);
+    if (ThreadTicks::IsSupported()) {
+      perf_test::PrintResult(
+          "task",
+          "_thread_time",
+          trace,
+          total_thread_time.InMicroseconds() / static_cast<double>(counter_),
+          "us/task",
+          true);
+    }
+  }
+
+  MessageLoop* target_message_loop() {
+#if defined(OS_ANDROID)
+    if (java_thread_)
+      return java_thread_->message_loop();
+#endif
+    return target_->message_loop();
+  }
+
+ private:
+  std::unique_ptr<Thread> target_;
+#if defined(OS_ANDROID)
+  std::unique_ptr<android::JavaHandlerThread> java_thread_;
+#endif
+  std::unique_ptr<base::TimeDelta[]> scheduling_times_;
+  std::unique_ptr<base::TimeDelta[]> scheduling_thread_times_;
+  std::unique_ptr<base::TimeDelta[]> min_batch_times_;
+  std::unique_ptr<base::TimeDelta[]> max_batch_times_;
+  uint64_t counter_;
+
+  static const size_t kTargetTimeSec = 5;
+  static const size_t kBatchSize = 1000;
+};
+
+TEST_F(ScheduleWorkTest, ThreadTimeToIOFromOneThread) {
+  ScheduleWork(MessageLoop::TYPE_IO, 1);
+}
+
+TEST_F(ScheduleWorkTest, ThreadTimeToIOFromTwoThreads) {
+  ScheduleWork(MessageLoop::TYPE_IO, 2);
+}
+
+TEST_F(ScheduleWorkTest, ThreadTimeToIOFromFourThreads) {
+  ScheduleWork(MessageLoop::TYPE_IO, 4);
+}
+
+TEST_F(ScheduleWorkTest, ThreadTimeToUIFromOneThread) {
+  ScheduleWork(MessageLoop::TYPE_UI, 1);
+}
+
+TEST_F(ScheduleWorkTest, ThreadTimeToUIFromTwoThreads) {
+  ScheduleWork(MessageLoop::TYPE_UI, 2);
+}
+
+TEST_F(ScheduleWorkTest, ThreadTimeToUIFromFourThreads) {
+  ScheduleWork(MessageLoop::TYPE_UI, 4);
+}
+
+TEST_F(ScheduleWorkTest, ThreadTimeToDefaultFromOneThread) {
+  ScheduleWork(MessageLoop::TYPE_DEFAULT, 1);
+}
+
+TEST_F(ScheduleWorkTest, ThreadTimeToDefaultFromTwoThreads) {
+  ScheduleWork(MessageLoop::TYPE_DEFAULT, 2);
+}
+
+TEST_F(ScheduleWorkTest, ThreadTimeToDefaultFromFourThreads) {
+  ScheduleWork(MessageLoop::TYPE_DEFAULT, 4);
+}
+
+#if defined(OS_ANDROID)
+TEST_F(ScheduleWorkTest, ThreadTimeToJavaFromOneThread) {
+  ScheduleWork(MessageLoop::TYPE_JAVA, 1);
+}
+
+TEST_F(ScheduleWorkTest, ThreadTimeToJavaFromTwoThreads) {
+  ScheduleWork(MessageLoop::TYPE_JAVA, 2);
+}
+
+TEST_F(ScheduleWorkTest, ThreadTimeToJavaFromFourThreads) {
+  ScheduleWork(MessageLoop::TYPE_JAVA, 4);
+}
+#endif
+
+}  // namespace base
diff --git a/base/metrics/field_trial_params_unittest.cc b/base/metrics/field_trial_params_unittest.cc
new file mode 100644
index 0000000..d310c0d
--- /dev/null
+++ b/base/metrics/field_trial_params_unittest.cc
@@ -0,0 +1,458 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/metrics/field_trial_params.h"
+
+#include "base/feature_list.h"
+#include "base/macros.h"
+#include "base/metrics/field_trial.h"
+#include "base/metrics/field_trial_param_associator.h"
+#include "base/test/scoped_feature_list.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+namespace {
+
+// Call FieldTrialList::FactoryGetFieldTrial() with a future expiry date.
+scoped_refptr<FieldTrial> CreateFieldTrial(
+    const std::string& trial_name,
+    int total_probability,
+    const std::string& default_group_name,
+    int* default_group_number) {
+  return FieldTrialList::FactoryGetFieldTrial(
+      trial_name, total_probability, default_group_name,
+      FieldTrialList::kNoExpirationYear, 1, 1, FieldTrial::SESSION_RANDOMIZED,
+      default_group_number);
+}
+
+}  // namespace
+
+class FieldTrialParamsTest : public ::testing::Test {
+ public:
+  FieldTrialParamsTest() : field_trial_list_(nullptr) {}
+
+  ~FieldTrialParamsTest() override {
+    // Ensure that the maps are cleared between tests, since they are stored as
+    // process singletons.
+    FieldTrialParamAssociator::GetInstance()->ClearAllParamsForTesting();
+  }
+
+  void CreateFeatureWithTrial(const Feature& feature,
+                              FeatureList::OverrideState override_state,
+                              FieldTrial* trial) {
+    std::unique_ptr<FeatureList> feature_list(new FeatureList);
+    feature_list->RegisterFieldTrialOverride(feature.name, override_state,
+                                             trial);
+    scoped_feature_list_.InitWithFeatureList(std::move(feature_list));
+  }
+
+ private:
+  FieldTrialList field_trial_list_;
+  test::ScopedFeatureList scoped_feature_list_;
+
+  DISALLOW_COPY_AND_ASSIGN(FieldTrialParamsTest);
+};
+
+TEST_F(FieldTrialParamsTest, AssociateFieldTrialParams) {
+  const std::string kTrialName = "AssociateFieldTrialParams";
+
+  {
+    std::map<std::string, std::string> params;
+    params["a"] = "10";
+    params["b"] = "test";
+    ASSERT_TRUE(AssociateFieldTrialParams(kTrialName, "A", params));
+  }
+  {
+    std::map<std::string, std::string> params;
+    params["a"] = "5";
+    ASSERT_TRUE(AssociateFieldTrialParams(kTrialName, "B", params));
+  }
+
+  FieldTrialList::CreateFieldTrial(kTrialName, "B");
+  EXPECT_EQ("5", GetFieldTrialParamValue(kTrialName, "a"));
+  EXPECT_EQ(std::string(), GetFieldTrialParamValue(kTrialName, "b"));
+  EXPECT_EQ(std::string(), GetFieldTrialParamValue(kTrialName, "x"));
+
+  std::map<std::string, std::string> params;
+  EXPECT_TRUE(GetFieldTrialParams(kTrialName, &params));
+  EXPECT_EQ(1U, params.size());
+  EXPECT_EQ("5", params["a"]);
+}
+
+TEST_F(FieldTrialParamsTest, AssociateFieldTrialParams_Fail) {
+  const std::string kTrialName = "AssociateFieldTrialParams_Fail";
+  const std::string kGroupName = "A";
+
+  std::map<std::string, std::string> params;
+  params["a"] = "10";
+  ASSERT_TRUE(AssociateFieldTrialParams(kTrialName, kGroupName, params));
+  params["a"] = "1";
+  params["b"] = "2";
+  ASSERT_FALSE(AssociateFieldTrialParams(kTrialName, kGroupName, params));
+
+  FieldTrialList::CreateFieldTrial(kTrialName, kGroupName);
+  EXPECT_EQ("10", GetFieldTrialParamValue(kTrialName, "a"));
+  EXPECT_EQ(std::string(), GetFieldTrialParamValue(kTrialName, "b"));
+}
+
+TEST_F(FieldTrialParamsTest, AssociateFieldTrialParams_TrialActiveFail) {
+  const std::string kTrialName = "AssociateFieldTrialParams_TrialActiveFail";
+  FieldTrialList::CreateFieldTrial(kTrialName, "A");
+  ASSERT_EQ("A", FieldTrialList::FindFullName(kTrialName));
+
+  std::map<std::string, std::string> params;
+  params["a"] = "10";
+  EXPECT_FALSE(AssociateFieldTrialParams(kTrialName, "B", params));
+  EXPECT_FALSE(AssociateFieldTrialParams(kTrialName, "A", params));
+}
+
+TEST_F(FieldTrialParamsTest, AssociateFieldTrialParams_DoesntActivateTrial) {
+  const std::string kTrialName =
+      "AssociateFieldTrialParams_DoesntActivateTrial";
+
+  ASSERT_FALSE(FieldTrialList::IsTrialActive(kTrialName));
+  scoped_refptr<FieldTrial> trial(
+      CreateFieldTrial(kTrialName, 100, "A", nullptr));
+  ASSERT_FALSE(FieldTrialList::IsTrialActive(kTrialName));
+
+  std::map<std::string, std::string> params;
+  params["a"] = "10";
+  EXPECT_TRUE(AssociateFieldTrialParams(kTrialName, "A", params));
+  ASSERT_FALSE(FieldTrialList::IsTrialActive(kTrialName));
+}
+
+TEST_F(FieldTrialParamsTest, GetFieldTrialParams_NoTrial) {
+  const std::string kTrialName = "GetFieldTrialParams_NoParams";
+
+  std::map<std::string, std::string> params;
+  EXPECT_FALSE(GetFieldTrialParams(kTrialName, &params));
+  EXPECT_EQ(std::string(), GetFieldTrialParamValue(kTrialName, "x"));
+  EXPECT_EQ(std::string(), GetFieldTrialParamValue(kTrialName, "y"));
+}
+
+TEST_F(FieldTrialParamsTest, GetFieldTrialParams_NoParams) {
+  const std::string kTrialName = "GetFieldTrialParams_NoParams";
+
+  FieldTrialList::CreateFieldTrial(kTrialName, "A");
+
+  std::map<std::string, std::string> params;
+  EXPECT_FALSE(GetFieldTrialParams(kTrialName, &params));
+  EXPECT_EQ(std::string(), GetFieldTrialParamValue(kTrialName, "x"));
+  EXPECT_EQ(std::string(), GetFieldTrialParamValue(kTrialName, "y"));
+}
+
+TEST_F(FieldTrialParamsTest, GetFieldTrialParams_ActivatesTrial) {
+  const std::string kTrialName = "GetFieldTrialParams_ActivatesTrial";
+
+  ASSERT_FALSE(FieldTrialList::IsTrialActive(kTrialName));
+  scoped_refptr<FieldTrial> trial(
+      CreateFieldTrial(kTrialName, 100, "A", nullptr));
+  ASSERT_FALSE(FieldTrialList::IsTrialActive(kTrialName));
+
+  std::map<std::string, std::string> params;
+  EXPECT_FALSE(GetFieldTrialParams(kTrialName, &params));
+  ASSERT_TRUE(FieldTrialList::IsTrialActive(kTrialName));
+}
+
+TEST_F(FieldTrialParamsTest, GetFieldTrialParamValue_ActivatesTrial) {
+  const std::string kTrialName = "GetFieldTrialParamValue_ActivatesTrial";
+
+  ASSERT_FALSE(FieldTrialList::IsTrialActive(kTrialName));
+  scoped_refptr<FieldTrial> trial(
+      CreateFieldTrial(kTrialName, 100, "A", nullptr));
+  ASSERT_FALSE(FieldTrialList::IsTrialActive(kTrialName));
+
+  std::map<std::string, std::string> params;
+  EXPECT_EQ(std::string(), GetFieldTrialParamValue(kTrialName, "x"));
+  ASSERT_TRUE(FieldTrialList::IsTrialActive(kTrialName));
+}
+
+TEST_F(FieldTrialParamsTest, GetFieldTrialParamsByFeature) {
+  const std::string kTrialName = "GetFieldTrialParamsByFeature";
+  const Feature kFeature{"TestFeature", FEATURE_DISABLED_BY_DEFAULT};
+
+  std::map<std::string, std::string> params;
+  params["x"] = "1";
+  AssociateFieldTrialParams(kTrialName, "A", params);
+  scoped_refptr<FieldTrial> trial(
+      CreateFieldTrial(kTrialName, 100, "A", nullptr));
+
+  CreateFeatureWithTrial(kFeature, FeatureList::OVERRIDE_ENABLE_FEATURE,
+                         trial.get());
+
+  std::map<std::string, std::string> actualParams;
+  EXPECT_TRUE(GetFieldTrialParamsByFeature(kFeature, &actualParams));
+  EXPECT_EQ(params, actualParams);
+}
+
+TEST_F(FieldTrialParamsTest, GetFieldTrialParamValueByFeature) {
+  const std::string kTrialName = "GetFieldTrialParamsByFeature";
+  const Feature kFeature{"TestFeature", FEATURE_DISABLED_BY_DEFAULT};
+
+  std::map<std::string, std::string> params;
+  params["x"] = "1";
+  AssociateFieldTrialParams(kTrialName, "A", params);
+  scoped_refptr<FieldTrial> trial(
+      CreateFieldTrial(kTrialName, 100, "A", nullptr));
+
+  CreateFeatureWithTrial(kFeature, FeatureList::OVERRIDE_ENABLE_FEATURE,
+                         trial.get());
+
+  std::map<std::string, std::string> actualParams;
+  EXPECT_EQ(params["x"], GetFieldTrialParamValueByFeature(kFeature, "x"));
+}
+
+TEST_F(FieldTrialParamsTest, GetFieldTrialParamsByFeature_Disable) {
+  const std::string kTrialName = "GetFieldTrialParamsByFeature";
+  const Feature kFeature{"TestFeature", FEATURE_DISABLED_BY_DEFAULT};
+
+  std::map<std::string, std::string> params;
+  params["x"] = "1";
+  AssociateFieldTrialParams(kTrialName, "A", params);
+  scoped_refptr<FieldTrial> trial(
+      CreateFieldTrial(kTrialName, 100, "A", nullptr));
+
+  CreateFeatureWithTrial(kFeature, FeatureList::OVERRIDE_DISABLE_FEATURE,
+                         trial.get());
+
+  std::map<std::string, std::string> actualParams;
+  EXPECT_FALSE(GetFieldTrialParamsByFeature(kFeature, &actualParams));
+}
+
+TEST_F(FieldTrialParamsTest, GetFieldTrialParamValueByFeature_Disable) {
+  const std::string kTrialName = "GetFieldTrialParamsByFeature";
+  const Feature kFeature{"TestFeature", FEATURE_DISABLED_BY_DEFAULT};
+
+  std::map<std::string, std::string> params;
+  params["x"] = "1";
+  AssociateFieldTrialParams(kTrialName, "A", params);
+  scoped_refptr<FieldTrial> trial(
+      CreateFieldTrial(kTrialName, 100, "A", nullptr));
+
+  CreateFeatureWithTrial(kFeature, FeatureList::OVERRIDE_DISABLE_FEATURE,
+                         trial.get());
+
+  std::map<std::string, std::string> actualParams;
+  EXPECT_EQ(std::string(), GetFieldTrialParamValueByFeature(kFeature, "x"));
+}
+
+TEST_F(FieldTrialParamsTest, FeatureParamString) {
+  const std::string kTrialName = "GetFieldTrialParamsByFeature";
+
+  static const Feature kFeature{"TestFeature", FEATURE_DISABLED_BY_DEFAULT};
+  static const FeatureParam<std::string> a{&kFeature, "a", "default"};
+  static const FeatureParam<std::string> b{&kFeature, "b", ""};
+  static const FeatureParam<std::string> c{&kFeature, "c", "default"};
+  static const FeatureParam<std::string> d{&kFeature, "d", ""};
+  static const FeatureParam<std::string> e{&kFeature, "e", "default"};
+  static const FeatureParam<std::string> f{&kFeature, "f", ""};
+
+  std::map<std::string, std::string> params;
+  params["a"] = "";
+  params["b"] = "non-default";
+  params["c"] = "non-default";
+  params["d"] = "";
+  // "e" is not registered
+  // "f" is not registered
+  AssociateFieldTrialParams(kTrialName, "A", params);
+  scoped_refptr<FieldTrial> trial(
+      CreateFieldTrial(kTrialName, 100, "A", nullptr));
+
+  CreateFeatureWithTrial(kFeature, FeatureList::OVERRIDE_ENABLE_FEATURE,
+                         trial.get());
+
+  EXPECT_EQ("default", a.Get());  // empty
+  EXPECT_EQ("non-default", b.Get());
+  EXPECT_EQ("non-default", c.Get());
+  EXPECT_EQ("", d.Get());         // empty
+  EXPECT_EQ("default", e.Get());  // not registered
+  EXPECT_EQ("", f.Get());         // not registered
+}
+
+TEST_F(FieldTrialParamsTest, FeatureParamInt) {
+  const std::string kTrialName = "GetFieldTrialParamsByFeature";
+
+  static const Feature kFeature{"TestFeature", FEATURE_DISABLED_BY_DEFAULT};
+  static const FeatureParam<int> a{&kFeature, "a", 0};
+  static const FeatureParam<int> b{&kFeature, "b", 0};
+  static const FeatureParam<int> c{&kFeature, "c", 0};
+  static const FeatureParam<int> d{&kFeature, "d", 0};
+  static const FeatureParam<int> e{&kFeature, "e", 0};
+
+  std::map<std::string, std::string> params;
+  params["a"] = "1";
+  params["b"] = "1.5";
+  params["c"] = "foo";
+  params["d"] = "";
+  // "e" is not registered
+  AssociateFieldTrialParams(kTrialName, "A", params);
+  scoped_refptr<FieldTrial> trial(
+      CreateFieldTrial(kTrialName, 100, "A", nullptr));
+
+  CreateFeatureWithTrial(kFeature, FeatureList::OVERRIDE_ENABLE_FEATURE,
+                         trial.get());
+
+  EXPECT_EQ(1, GetFieldTrialParamByFeatureAsInt(kFeature, "a", 0));
+  EXPECT_EQ(0, GetFieldTrialParamByFeatureAsInt(kFeature, "b", 0));  // invalid
+  EXPECT_EQ(0, GetFieldTrialParamByFeatureAsInt(kFeature, "c", 0));  // invalid
+  EXPECT_EQ(0, GetFieldTrialParamByFeatureAsInt(kFeature, "d", 0));  // empty
+  EXPECT_EQ(0, GetFieldTrialParamByFeatureAsInt(kFeature, "e", 0));  // empty
+
+  EXPECT_EQ(1, a.Get());
+  EXPECT_EQ(0, b.Get());  // invalid
+  EXPECT_EQ(0, c.Get());  // invalid
+  EXPECT_EQ(0, d.Get());  // empty
+  EXPECT_EQ(0, e.Get());  // empty
+}
+
+TEST_F(FieldTrialParamsTest, FeatureParamDouble) {
+  const std::string kTrialName = "GetFieldTrialParamsByFeature";
+
+  static const Feature kFeature{"TestFeature", FEATURE_DISABLED_BY_DEFAULT};
+  static const FeatureParam<double> a{&kFeature, "a", 0.0};
+  static const FeatureParam<double> b{&kFeature, "b", 0.0};
+  static const FeatureParam<double> c{&kFeature, "c", 0.0};
+  static const FeatureParam<double> d{&kFeature, "d", 0.0};
+  static const FeatureParam<double> e{&kFeature, "e", 0.0};
+  static const FeatureParam<double> f{&kFeature, "f", 0.0};
+
+  std::map<std::string, std::string> params;
+  params["a"] = "1";
+  params["b"] = "1.5";
+  params["c"] = "1.0e-10";
+  params["d"] = "foo";
+  params["e"] = "";
+  // "f" is not registered
+  AssociateFieldTrialParams(kTrialName, "A", params);
+  scoped_refptr<FieldTrial> trial(
+      CreateFieldTrial(kTrialName, 100, "A", nullptr));
+
+  CreateFeatureWithTrial(kFeature, FeatureList::OVERRIDE_ENABLE_FEATURE,
+                         trial.get());
+
+  EXPECT_EQ(1, GetFieldTrialParamByFeatureAsDouble(kFeature, "a", 0));
+  EXPECT_EQ(1.5, GetFieldTrialParamByFeatureAsDouble(kFeature, "b", 0));
+  EXPECT_EQ(1.0e-10, GetFieldTrialParamByFeatureAsDouble(kFeature, "c", 0));
+  EXPECT_EQ(0,
+            GetFieldTrialParamByFeatureAsDouble(kFeature, "d", 0));  // invalid
+  EXPECT_EQ(0, GetFieldTrialParamByFeatureAsDouble(kFeature, "e", 0));  // empty
+  EXPECT_EQ(0, GetFieldTrialParamByFeatureAsDouble(kFeature, "f", 0));  // empty
+
+  EXPECT_EQ(1, a.Get());
+  EXPECT_EQ(1.5, b.Get());
+  EXPECT_EQ(1.0e-10, c.Get());
+  EXPECT_EQ(0, d.Get());  // invalid
+  EXPECT_EQ(0, e.Get());  // empty
+  EXPECT_EQ(0, f.Get());  // empty
+}
+
+TEST_F(FieldTrialParamsTest, FeatureParamBool) {
+  const std::string kTrialName = "GetFieldTrialParamsByFeature";
+
+  static const Feature kFeature{"TestFeature", FEATURE_DISABLED_BY_DEFAULT};
+  static const FeatureParam<bool> a{&kFeature, "a", false};
+  static const FeatureParam<bool> b{&kFeature, "b", true};
+  static const FeatureParam<bool> c{&kFeature, "c", false};
+  static const FeatureParam<bool> d{&kFeature, "d", true};
+  static const FeatureParam<bool> e{&kFeature, "e", true};
+  static const FeatureParam<bool> f{&kFeature, "f", true};
+
+  std::map<std::string, std::string> params;
+  params["a"] = "true";
+  params["b"] = "false";
+  params["c"] = "1";
+  params["d"] = "False";
+  params["e"] = "";
+  // "f" is not registered
+  AssociateFieldTrialParams(kTrialName, "A", params);
+  scoped_refptr<FieldTrial> trial(
+      CreateFieldTrial(kTrialName, 100, "A", nullptr));
+
+  CreateFeatureWithTrial(kFeature, FeatureList::OVERRIDE_ENABLE_FEATURE,
+                         trial.get());
+
+  EXPECT_TRUE(a.Get());
+  EXPECT_FALSE(b.Get());
+  EXPECT_FALSE(c.Get());  // invalid
+  EXPECT_TRUE(d.Get());   // invalid
+  EXPECT_TRUE(e.Get());   // empty
+  EXPECT_TRUE(f.Get());   // empty
+}
+
+enum Hand { ROCK, PAPER, SCISSORS };
+
+TEST_F(FieldTrialParamsTest, FeatureParamEnum) {
+  const std::string kTrialName = "GetFieldTrialParamsByFeature";
+
+  static const FeatureParam<Hand>::Option hands[] = {
+      {ROCK, "rock"}, {PAPER, "paper"}, {SCISSORS, "scissors"}};
+  static const Feature kFeature{"TestFeature", FEATURE_DISABLED_BY_DEFAULT};
+  static const FeatureParam<Hand> a{&kFeature, "a", ROCK, &hands};
+  static const FeatureParam<Hand> b{&kFeature, "b", ROCK, &hands};
+  static const FeatureParam<Hand> c{&kFeature, "c", ROCK, &hands};
+  static const FeatureParam<Hand> d{&kFeature, "d", ROCK, &hands};
+  static const FeatureParam<Hand> e{&kFeature, "e", PAPER, &hands};
+  static const FeatureParam<Hand> f{&kFeature, "f", SCISSORS, &hands};
+
+  std::map<std::string, std::string> params;
+  params["a"] = "rock";
+  params["b"] = "paper";
+  params["c"] = "scissors";
+  params["d"] = "lizard";
+  params["e"] = "";
+  // "f" is not registered
+  AssociateFieldTrialParams(kTrialName, "A", params);
+  scoped_refptr<FieldTrial> trial(
+      CreateFieldTrial(kTrialName, 100, "A", nullptr));
+
+  CreateFeatureWithTrial(kFeature, FeatureList::OVERRIDE_ENABLE_FEATURE,
+                         trial.get());
+
+  EXPECT_EQ(ROCK, a.Get());
+  EXPECT_EQ(PAPER, b.Get());
+  EXPECT_EQ(SCISSORS, c.Get());
+  EXPECT_EQ(ROCK, d.Get());      // invalid
+  EXPECT_EQ(PAPER, e.Get());     // invalid/empty
+  EXPECT_EQ(SCISSORS, f.Get());  // not registered
+}
+
+enum class UI { ONE_D, TWO_D, THREE_D };
+
+TEST_F(FieldTrialParamsTest, FeatureParamEnumClass) {
+  const std::string kTrialName = "GetFieldTrialParamsByFeature";
+
+  static const FeatureParam<UI>::Option uis[] = {
+      {UI::ONE_D, "1d"}, {UI::TWO_D, "2d"}, {UI::THREE_D, "3d"}};
+  static const Feature kFeature{"TestFeature", FEATURE_DISABLED_BY_DEFAULT};
+  static const FeatureParam<UI> a{&kFeature, "a", UI::ONE_D, &uis};
+  static const FeatureParam<UI> b{&kFeature, "b", UI::ONE_D, &uis};
+  static const FeatureParam<UI> c{&kFeature, "c", UI::ONE_D, &uis};
+  static const FeatureParam<UI> d{&kFeature, "d", UI::ONE_D, &uis};
+  static const FeatureParam<UI> e{&kFeature, "e", UI::TWO_D, &uis};
+  static const FeatureParam<UI> f{&kFeature, "f", UI::THREE_D, &uis};
+
+  std::map<std::string, std::string> params;
+  params["a"] = "1d";
+  params["b"] = "2d";
+  params["c"] = "3d";
+  params["d"] = "4d";
+  params["e"] = "";
+  // "f" is not registered
+  AssociateFieldTrialParams(kTrialName, "A", params);
+  scoped_refptr<FieldTrial> trial(
+      CreateFieldTrial(kTrialName, 100, "A", nullptr));
+
+  CreateFeatureWithTrial(kFeature, FeatureList::OVERRIDE_ENABLE_FEATURE,
+                         trial.get());
+
+  EXPECT_EQ(UI::ONE_D, a.Get());
+  EXPECT_EQ(UI::TWO_D, b.Get());
+  EXPECT_EQ(UI::THREE_D, c.Get());
+  EXPECT_EQ(UI::ONE_D, d.Get());    // invalid
+  EXPECT_EQ(UI::TWO_D, e.Get());    // invalid/empty
+  EXPECT_EQ(UI::THREE_D, f.Get());  // not registered
+}
+
+}  // namespace base
diff --git a/base/metrics/histogram_functions_unittest.cc b/base/metrics/histogram_functions_unittest.cc
new file mode 100644
index 0000000..32f4394
--- /dev/null
+++ b/base/metrics/histogram_functions_unittest.cc
@@ -0,0 +1,127 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/metrics/histogram_functions.h"
+
+#include "base/metrics/histogram_macros.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "base/time/time.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+enum UmaHistogramTestingEnum {
+  UMA_HISTOGRAM_TESTING_ENUM_FIRST,
+  UMA_HISTOGRAM_TESTING_ENUM_SECOND,
+  UMA_HISTOGRAM_TESTING_ENUM_THIRD
+};
+
+TEST(HistogramFunctionsTest, ExactLinear) {
+  std::string histogram("Testing.UMA.HistogramExactLinear");
+  HistogramTester tester;
+  UmaHistogramExactLinear(histogram, 10, 100);
+  tester.ExpectUniqueSample(histogram, 10, 1);
+  UmaHistogramExactLinear(histogram, 20, 100);
+  UmaHistogramExactLinear(histogram, 10, 100);
+  tester.ExpectBucketCount(histogram, 10, 2);
+  tester.ExpectBucketCount(histogram, 20, 1);
+  tester.ExpectTotalCount(histogram, 3);
+  // Test linear buckets overflow.
+  UmaHistogramExactLinear(histogram, 200, 100);
+  tester.ExpectBucketCount(histogram, 101, 1);
+  tester.ExpectTotalCount(histogram, 4);
+  // Test linear buckets underflow.
+  UmaHistogramExactLinear(histogram, 0, 100);
+  tester.ExpectBucketCount(histogram, 0, 1);
+  tester.ExpectTotalCount(histogram, 5);
+}
+
+TEST(HistogramFunctionsTest, Enumeration) {
+  std::string histogram("Testing.UMA.HistogramEnumeration");
+  HistogramTester tester;
+  UmaHistogramEnumeration(histogram, UMA_HISTOGRAM_TESTING_ENUM_FIRST,
+                          UMA_HISTOGRAM_TESTING_ENUM_THIRD);
+  tester.ExpectUniqueSample(histogram, UMA_HISTOGRAM_TESTING_ENUM_FIRST, 1);
+
+  // Verify the overflow & underflow bucket exists.
+  UMA_HISTOGRAM_ENUMERATION(
+      histogram, static_cast<int>(UMA_HISTOGRAM_TESTING_ENUM_THIRD) + 10,
+      static_cast<int>(UMA_HISTOGRAM_TESTING_ENUM_THIRD));
+  tester.ExpectBucketCount(
+      histogram, static_cast<int>(UMA_HISTOGRAM_TESTING_ENUM_THIRD) + 1, 1);
+  tester.ExpectTotalCount(histogram, 2);
+}
+
+TEST(HistogramFunctionsTest, Boolean) {
+  std::string histogram("Testing.UMA.HistogramBoolean");
+  HistogramTester tester;
+  UmaHistogramBoolean(histogram, true);
+  tester.ExpectUniqueSample(histogram, 1, 1);
+  UmaHistogramBoolean(histogram, false);
+  tester.ExpectBucketCount(histogram, 0, 1);
+  tester.ExpectTotalCount(histogram, 2);
+}
+
+TEST(HistogramFunctionsTest, Percentage) {
+  std::string histogram("Testing.UMA.HistogramPercentage");
+  HistogramTester tester;
+  UmaHistogramPercentage(histogram, 50);
+  tester.ExpectUniqueSample(histogram, 50, 1);
+  // Test overflows.
+  UmaHistogramPercentage(histogram, 110);
+  tester.ExpectBucketCount(histogram, 101, 1);
+  tester.ExpectTotalCount(histogram, 2);
+}
+
+TEST(HistogramFunctionsTest, Counts) {
+  std::string histogram("Testing.UMA.HistogramCount.Custom");
+  HistogramTester tester;
+  UmaHistogramCustomCounts(histogram, 10, 1, 100, 10);
+  tester.ExpectUniqueSample(histogram, 10, 1);
+  UmaHistogramCustomCounts(histogram, 20, 1, 100, 10);
+  UmaHistogramCustomCounts(histogram, 20, 1, 100, 10);
+  UmaHistogramCustomCounts(histogram, 20, 1, 100, 10);
+  tester.ExpectBucketCount(histogram, 20, 3);
+  tester.ExpectTotalCount(histogram, 4);
+  UmaHistogramCustomCounts(histogram, 110, 1, 100, 10);
+  tester.ExpectBucketCount(histogram, 101, 1);
+  tester.ExpectTotalCount(histogram, 5);
+}
+
+TEST(HistogramFunctionsTest, Times) {
+  std::string histogram("Testing.UMA.HistogramTimes");
+  HistogramTester tester;
+  UmaHistogramTimes(histogram, TimeDelta::FromSeconds(1));
+  tester.ExpectTimeBucketCount(histogram, TimeDelta::FromSeconds(1), 1);
+  tester.ExpectTotalCount(histogram, 1);
+  UmaHistogramTimes(histogram, TimeDelta::FromSeconds(9));
+  tester.ExpectTimeBucketCount(histogram, TimeDelta::FromSeconds(9), 1);
+  tester.ExpectTotalCount(histogram, 2);
+  UmaHistogramTimes(histogram, TimeDelta::FromSeconds(10));  // Overflows
+  tester.ExpectTimeBucketCount(histogram, TimeDelta::FromSeconds(10), 1);
+  UmaHistogramTimes(histogram, TimeDelta::FromSeconds(20));  // Overflows.
+  // Check the value by picking any overflow time.
+  tester.ExpectTimeBucketCount(histogram, TimeDelta::FromSeconds(11), 2);
+  tester.ExpectTotalCount(histogram, 4);
+}
+
+TEST(HistogramFunctionsTest, Sparse_SupportsLargeRange) {
+  std::string histogram("Testing.UMA.HistogramSparse");
+  HistogramTester tester;
+  UmaHistogramSparse(histogram, 0);
+  UmaHistogramSparse(histogram, 123456789);
+  UmaHistogramSparse(histogram, 123456789);
+  EXPECT_THAT(tester.GetAllSamples(histogram),
+              testing::ElementsAre(Bucket(0, 1), Bucket(123456789, 2)));
+}
+
+TEST(HistogramFunctionsTest, Sparse_SupportsNegativeValues) {
+  std::string histogram("Testing.UMA.HistogramSparse");
+  HistogramTester tester;
+  UmaHistogramSparse(histogram, -1);
+  tester.ExpectUniqueSample(histogram, -1, 1);
+}
+
+}  // namespace base.
diff --git a/base/metrics/histogram_unittest.nc b/base/metrics/histogram_unittest.nc
new file mode 100644
index 0000000..c9c2657
--- /dev/null
+++ b/base/metrics/histogram_unittest.nc
@@ -0,0 +1,90 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This is a "No Compile Test" suite.
+// http://dev.chromium.org/developers/testing/no-compile-tests
+
+#include "base/metrics/histogram_functions.h"
+#include "base/metrics/histogram_macros.h"
+
+namespace base {
+
+#if defined(NCTEST_DIFFERENT_ENUM)  // [r"\|sample\| and \|boundary\| shouldn't be of different enums"]
+
+void WontCompile() {
+  enum TypeA { A };
+  enum TypeB { B };
+  UMA_HISTOGRAM_ENUMERATION("", A, B);
+}
+
+#elif defined(NCTEST_DIFFERENT_ENUM_CLASS)  // [r"\|sample\| and \|boundary\| shouldn't be of different enums"]
+
+void WontCompile() {
+  enum class TypeA { A };
+  enum class TypeB { B };
+  UMA_HISTOGRAM_ENUMERATION("", TypeA::A, TypeB::B);
+}
+
+#elif defined(NCTEST_DIFFERENT_ENUM_MIXED)  // [r"\|sample\| and \|boundary\| shouldn't be of different enums"]
+
+void WontCompile() {
+  enum class TypeA { A };
+  enum TypeB { B };
+  UMA_HISTOGRAM_ENUMERATION("", TypeA::A, B);
+}
+
+#elif defined(NCTEST_NEGATIVE_ENUM_MAX)  // [r'static_assert failed "\|boundary\| is out of range of HistogramBase::Sample"']
+
+void WontCompile() {
+  // Buckets for enumeration start from 0, so a boundary < 0 is illegal.
+  enum class TypeA { A = -1 };
+  UMA_HISTOGRAM_ENUMERATION("", TypeA::A, TypeA::A);
+}
+
+#elif defined(NCTEST_ENUM_MAX_OUT_OF_RANGE)  // [r'static_assert failed "\|boundary\| is out of range of HistogramBase::Sample"']
+
+void WontCompile() {
+  // HistogramBase::Sample is an int and can't hold larger values.
+  enum class TypeA : uint32_t { A = 0xffffffff };
+  UMA_HISTOGRAM_ENUMERATION("", TypeA::A, TypeA::A);
+}
+
+#elif defined(NCTEST_SAMPLE_NOT_ENUM)  // [r'static_assert failed "Unexpected: \|boundary\| is enum, but \|sample\| is not."']
+
+void WontCompile() {
+  enum TypeA { A };
+  UMA_HISTOGRAM_ENUMERATION("", 0, TypeA::A);
+}
+
+#elif defined(NCTEST_FUNCTION_INT)  // [r"Non enum passed to UmaHistogramEnumeration"]
+
+void WontCompile() {
+  UmaHistogramEnumeration("", 1, 2);
+}
+
+#elif defined(NCTEST_FUNCTION_DIFFERENT_ENUM)  // [r"no matching function for call to 'UmaHistogramEnumeration'"]
+
+void WontCompile() {
+  enum TypeA { A };
+  enum TypeB { B };
+  UmaHistogramEnumeration("", A, B);
+}
+
+#elif defined(NCTEST_FUNCTION_FIRST_NOT_ENUM)  // [r"no matching function for call to 'UmaHistogramEnumeration'"]
+
+void WontCompile() {
+  enum TypeB { B };
+  UmaHistogramEnumeration("", 1, B);
+}
+
+#elif defined(NCTEST_FUNCTION_SECOND_NOT_ENUM)  // [r"no matching function for call to 'UmaHistogramEnumeration'"]
+
+void WontCompile() {
+  enum TypeA { A };
+  UmaHistogramEnumeration("", A, 2);
+}
+
+#endif
+
+}  // namespace base
diff --git a/base/native_library_unittest.cc b/base/native_library_unittest.cc
new file mode 100644
index 0000000..2bfb9ec
--- /dev/null
+++ b/base/native_library_unittest.cc
@@ -0,0 +1,166 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "base/native_library.h"
+#include "base/path_service.h"
+#include "base/test/native_library_test_utils.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+const FilePath::CharType kDummyLibraryPath[] =
+    FILE_PATH_LITERAL("dummy_library");
+
+TEST(NativeLibraryTest, LoadFailure) {
+  NativeLibraryLoadError error;
+  EXPECT_FALSE(LoadNativeLibrary(FilePath(kDummyLibraryPath), &error));
+  EXPECT_FALSE(error.ToString().empty());
+}
+
+// |error| is optional and can be null.
+TEST(NativeLibraryTest, LoadFailureWithNullError) {
+  EXPECT_FALSE(LoadNativeLibrary(FilePath(kDummyLibraryPath), nullptr));
+}
+
+TEST(NativeLibraryTest, GetNativeLibraryName) {
+  const char kExpectedName[] =
+#if defined(OS_WIN)
+      "mylib.dll";
+#elif defined(OS_IOS)
+      "mylib";
+#elif defined(OS_MACOSX)
+      "libmylib.dylib";
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+      "libmylib.so";
+#endif
+  EXPECT_EQ(kExpectedName, GetNativeLibraryName("mylib"));
+}
+
+TEST(NativeLibraryTest, GetLoadableModuleName) {
+  const char kExpectedName[] =
+#if defined(OS_WIN)
+      "mylib.dll";
+#elif defined(OS_IOS)
+      "mylib";
+#elif defined(OS_MACOSX)
+      "mylib.so";
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+      "libmylib.so";
+#endif
+  EXPECT_EQ(kExpectedName, GetLoadableModuleName("mylib"));
+}
+
+// We don't support dynamic loading on iOS, and ASAN will complain about our
+// intentional ODR violation because of |g_native_library_exported_value| being
+// defined globally both here and in the shared library.
+#if !defined(OS_IOS) && !defined(ADDRESS_SANITIZER)
+
+const char kTestLibraryName[] =
+#if defined(OS_WIN)
+    "test_shared_library.dll";
+#elif defined(OS_MACOSX)
+    "libtest_shared_library.dylib";
+#elif defined(OS_ANDROID) && defined(COMPONENT_BUILD)
+    "libtest_shared_library.cr.so";
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+    "libtest_shared_library.so";
+#endif
+
+class TestLibrary {
+ public:
+  TestLibrary() : TestLibrary(NativeLibraryOptions()) {}
+
+  explicit TestLibrary(const NativeLibraryOptions& options)
+    : library_(nullptr) {
+    base::FilePath exe_path;
+
+#if !defined(OS_FUCHSIA)
+    // Libraries do not sit alongside the executable in Fuchsia. NativeLibrary
+    // is aware of this and is able to resolve library paths correctly.
+    CHECK(base::PathService::Get(base::DIR_EXE, &exe_path));
+#endif
+
+    library_ = LoadNativeLibraryWithOptions(
+        exe_path.AppendASCII(kTestLibraryName), options, nullptr);
+    CHECK(library_);
+  }
+
+  ~TestLibrary() {
+    UnloadNativeLibrary(library_);
+  }
+
+  template <typename ReturnType, typename... Args>
+  ReturnType Call(const char* function_name, Args... args) {
+    return reinterpret_cast<ReturnType(*)(Args...)>(
+        GetFunctionPointerFromNativeLibrary(library_, function_name))(args...);
+  }
+
+ private:
+  NativeLibrary library_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestLibrary);
+};
+
+// NativeLibraaryTest.LoadLibrary is failing on M tablets only.
+// crbug/641309
+#if !defined(OS_ANDROID)
+
+// Verifies that we can load a native library and resolve its exported symbols.
+TEST(NativeLibraryTest, LoadLibrary) {
+  TestLibrary library;
+  EXPECT_EQ(5, library.Call<int>("GetSimpleTestValue"));
+}
+
+#endif  // !defined(OS_ANDROID)
+
+// Android dlopen() requires further investigation, as it might vary across
+// versions with respect to symbol resolution scope.
+// TSan and MSan error out on RTLD_DEEPBIND, https://crbug.com/705255
+#if !defined(OS_ANDROID) && !defined(THREAD_SANITIZER) && \
+    !defined(MEMORY_SANITIZER)
+
+// Verifies that the |prefer_own_symbols| option satisfies its guarantee that
+// a loaded library will always prefer local symbol resolution before
+// considering global symbols.
+TEST(NativeLibraryTest, LoadLibraryPreferOwnSymbols) {
+  NativeLibraryOptions options;
+  options.prefer_own_symbols = true;
+  TestLibrary library(options);
+
+  // Verify that this binary and the DSO use different storage for
+  // |g_native_library_exported_value|.
+  g_native_library_exported_value = 1;
+  library.Call<void>("SetExportedValue", 2);
+  EXPECT_EQ(1, g_native_library_exported_value);
+  g_native_library_exported_value = 3;
+  EXPECT_EQ(2, library.Call<int>("GetExportedValue"));
+
+  // Both this binary and the library link against the
+  // native_library_test_utils source library, which in turn exports the
+  // NativeLibraryTestIncrement() function whose return value depends on some
+  // static internal state.
+  //
+  // The DSO's GetIncrementValue() forwards to that function inside the DSO.
+  //
+  // Here we verify that direct calls to NativeLibraryTestIncrement() in this
+  // binary return a sequence of values independent from the sequence returned
+  // by GetIncrementValue(), ensuring that the DSO is calling its own local
+  // definition of NativeLibraryTestIncrement().
+  EXPECT_EQ(1, library.Call<int>("GetIncrementValue"));
+  EXPECT_EQ(1, NativeLibraryTestIncrement());
+  EXPECT_EQ(2, library.Call<int>("GetIncrementValue"));
+  EXPECT_EQ(3, library.Call<int>("GetIncrementValue"));
+  EXPECT_EQ(4, library.Call<int>("NativeLibraryTestIncrement"));
+  EXPECT_EQ(2, NativeLibraryTestIncrement());
+  EXPECT_EQ(3, NativeLibraryTestIncrement());
+}
+
+#endif  // !defined(OS_ANDROID)
+
+#endif  // !defined(OS_IOS) && !defined(ADDRESS_SANITIZER)
+
+}  // namespace base
diff --git a/base/nix/mime_util_xdg.cc b/base/nix/mime_util_xdg.cc
new file mode 100644
index 0000000..6b5b11d
--- /dev/null
+++ b/base/nix/mime_util_xdg.cc
@@ -0,0 +1,33 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/nix/mime_util_xdg.h"
+
+#include "base/files/file_path.h"
+#include "base/lazy_instance.h"
+#include "base/synchronization/lock.h"
+#include "base/third_party/xdg_mime/xdgmime.h"
+#include "base/threading/thread_restrictions.h"
+
+namespace base {
+namespace nix {
+
+namespace {
+
+// None of the XDG stuff is thread-safe, so serialize all access under
+// this lock.
+LazyInstance<Lock>::Leaky g_mime_util_xdg_lock = LAZY_INSTANCE_INITIALIZER;
+
+}  // namespace
+
+std::string GetFileMimeType(const FilePath& filepath) {
+  if (filepath.empty())
+    return std::string();
+  AssertBlockingAllowed();
+  AutoLock scoped_lock(g_mime_util_xdg_lock.Get());
+  return xdg_mime_get_mime_type_from_file_name(filepath.value().c_str());
+}
+
+}  // namespace nix
+}  // namespace base
diff --git a/base/nix/mime_util_xdg.h b/base/nix/mime_util_xdg.h
new file mode 100644
index 0000000..e0f264a
--- /dev/null
+++ b/base/nix/mime_util_xdg.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_NIX_MIME_UTIL_XDG_H_
+#define BASE_NIX_MIME_UTIL_XDG_H_
+
+#include <string>
+
+#include "base/base_export.h"
+#include "build/build_config.h"
+
+namespace base {
+
+class FilePath;
+
+namespace nix {
+
+// Gets the mime type for a file at |filepath|.
+//
+// The mime type is calculated based only on the file name of |filepath|.  In
+// particular |filepath| will not be touched on disk and |filepath| doesn't even
+// have to exist.  This means that the function does not work for directories
+// (i.e. |filepath| is assumed to be a path to a file).
+//
+// Note that this function might need to read from disk the mime-types data
+// provided by the OS.  Therefore this function should not be called from
+// threads that disallow IO via base::ThreadRestrictions::SetIOAllowed(false).
+//
+// If the mime type is unknown, this will return application/octet-stream.
+BASE_EXPORT std::string GetFileMimeType(const FilePath& filepath);
+
+}  // namespace nix
+}  // namespace base
+
+#endif  // BASE_NIX_MIME_UTIL_XDG_H_
diff --git a/base/nix/xdg_util.cc b/base/nix/xdg_util.cc
new file mode 100644
index 0000000..9ff4d88
--- /dev/null
+++ b/base/nix/xdg_util.cc
@@ -0,0 +1,152 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/nix/xdg_util.h"
+
+#include <string>
+
+#include "base/base_paths.h"
+#include "base/environment.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/path_service.h"
+#include "base/strings/string_util.h"
+#include "base/third_party/xdg_user_dirs/xdg_user_dir_lookup.h"
+
+namespace {
+
+// The KDE session version environment variable introduced in KDE 4.
+const char kKDESessionEnvVar[] = "KDE_SESSION_VERSION";
+
+}  // namespace
+
+namespace base {
+namespace nix {
+
+const char kDotConfigDir[] = ".config";
+const char kXdgConfigHomeEnvVar[] = "XDG_CONFIG_HOME";
+
+FilePath GetXDGDirectory(Environment* env, const char* env_name,
+                         const char* fallback_dir) {
+  FilePath path;
+  std::string env_value;
+  if (env->GetVar(env_name, &env_value) && !env_value.empty()) {
+    path = FilePath(env_value);
+  } else {
+    PathService::Get(DIR_HOME, &path);
+    path = path.Append(fallback_dir);
+  }
+  return path.StripTrailingSeparators();
+}
+
+FilePath GetXDGUserDirectory(const char* dir_name, const char* fallback_dir) {
+  FilePath path;
+  char* xdg_dir = xdg_user_dir_lookup(dir_name);
+  if (xdg_dir) {
+    path = FilePath(xdg_dir);
+    free(xdg_dir);
+  } else {
+    PathService::Get(DIR_HOME, &path);
+    path = path.Append(fallback_dir);
+  }
+  return path.StripTrailingSeparators();
+}
+
+DesktopEnvironment GetDesktopEnvironment(Environment* env) {
+  // XDG_CURRENT_DESKTOP is the newest standard circa 2012.
+  std::string xdg_current_desktop;
+  if (env->GetVar("XDG_CURRENT_DESKTOP", &xdg_current_desktop)) {
+    // Not all desktop environments set this env var as of this writing.
+    if (base::StartsWith(xdg_current_desktop, "Unity",
+                         base::CompareCase::SENSITIVE)) {
+      // gnome-fallback sessions set XDG_CURRENT_DESKTOP to Unity
+      // DESKTOP_SESSION can be gnome-fallback or gnome-fallback-compiz
+      std::string desktop_session;
+      if (env->GetVar("DESKTOP_SESSION", &desktop_session) &&
+          desktop_session.find("gnome-fallback") != std::string::npos) {
+        return DESKTOP_ENVIRONMENT_GNOME;
+      }
+      return DESKTOP_ENVIRONMENT_UNITY;
+    }
+    if (xdg_current_desktop == "GNOME")
+      return DESKTOP_ENVIRONMENT_GNOME;
+    if (xdg_current_desktop == "X-Cinnamon")
+      return DESKTOP_ENVIRONMENT_CINNAMON;
+    if (xdg_current_desktop == "KDE") {
+      std::string kde_session;
+      if (env->GetVar(kKDESessionEnvVar, &kde_session)) {
+        if (kde_session == "5") {
+          return DESKTOP_ENVIRONMENT_KDE5;
+        }
+      }
+      return DESKTOP_ENVIRONMENT_KDE4;
+    }
+    if (xdg_current_desktop == "Pantheon")
+      return DESKTOP_ENVIRONMENT_PANTHEON;
+    if (xdg_current_desktop == "XFCE")
+      return DESKTOP_ENVIRONMENT_XFCE;
+  }
+
+  // DESKTOP_SESSION was what everyone used in 2010.
+  std::string desktop_session;
+  if (env->GetVar("DESKTOP_SESSION", &desktop_session)) {
+    if (desktop_session == "gnome" || desktop_session == "mate")
+      return DESKTOP_ENVIRONMENT_GNOME;
+    if (desktop_session == "kde4" || desktop_session == "kde-plasma")
+      return DESKTOP_ENVIRONMENT_KDE4;
+    if (desktop_session == "kde") {
+      // This may mean KDE4 on newer systems, so we have to check.
+      if (env->HasVar(kKDESessionEnvVar))
+        return DESKTOP_ENVIRONMENT_KDE4;
+      return DESKTOP_ENVIRONMENT_KDE3;
+    }
+    if (desktop_session.find("xfce") != std::string::npos ||
+        desktop_session == "xubuntu") {
+      return DESKTOP_ENVIRONMENT_XFCE;
+    }
+  }
+
+  // Fall back on some older environment variables.
+  // Useful particularly in the DESKTOP_SESSION=default case.
+  if (env->HasVar("GNOME_DESKTOP_SESSION_ID"))
+    return DESKTOP_ENVIRONMENT_GNOME;
+  if (env->HasVar("KDE_FULL_SESSION")) {
+    if (env->HasVar(kKDESessionEnvVar))
+      return DESKTOP_ENVIRONMENT_KDE4;
+    return DESKTOP_ENVIRONMENT_KDE3;
+  }
+
+  return DESKTOP_ENVIRONMENT_OTHER;
+}
+
+const char* GetDesktopEnvironmentName(DesktopEnvironment env) {
+  switch (env) {
+    case DESKTOP_ENVIRONMENT_OTHER:
+      return nullptr;
+    case DESKTOP_ENVIRONMENT_CINNAMON:
+      return "CINNAMON";
+    case DESKTOP_ENVIRONMENT_GNOME:
+      return "GNOME";
+    case DESKTOP_ENVIRONMENT_KDE3:
+      return "KDE3";
+    case DESKTOP_ENVIRONMENT_KDE4:
+      return "KDE4";
+    case DESKTOP_ENVIRONMENT_KDE5:
+      return "KDE5";
+    case DESKTOP_ENVIRONMENT_PANTHEON:
+      return "PANTHEON";
+    case DESKTOP_ENVIRONMENT_UNITY:
+      return "UNITY";
+    case DESKTOP_ENVIRONMENT_XFCE:
+      return "XFCE";
+  }
+  return nullptr;
+}
+
+const char* GetDesktopEnvironmentName(Environment* env) {
+  return GetDesktopEnvironmentName(GetDesktopEnvironment(env));
+}
+
+}  // namespace nix
+}  // namespace base
diff --git a/base/nix/xdg_util.h b/base/nix/xdg_util.h
new file mode 100644
index 0000000..65f7d15
--- /dev/null
+++ b/base/nix/xdg_util.h
@@ -0,0 +1,77 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_NIX_XDG_UTIL_H_
+#define BASE_NIX_XDG_UTIL_H_
+
+// XDG refers to http://en.wikipedia.org/wiki/Freedesktop.org .
+// This file contains utilities found across free desktop environments.
+//
+// TODO(brettw) this file should be in app/x11, but is currently used by
+// net. We should have a net API to allow the embedder to specify the behavior
+// that it uses XDG for, and then move this file.
+
+#include "base/base_export.h"
+
+#ifdef nix
+#error asdf
+#endif
+
+namespace base {
+
+class Environment;
+class FilePath;
+
+namespace nix {
+
+// The default XDG config directory name.
+BASE_EXPORT extern const char kDotConfigDir[];
+
+// The XDG config directory environment variable.
+BASE_EXPORT extern const char kXdgConfigHomeEnvVar[];
+
+// Utility function for getting XDG directories.
+// |env_name| is the name of an environment variable that we want to use to get
+// a directory path. |fallback_dir| is the directory relative to $HOME that we
+// use if |env_name| cannot be found or is empty. |fallback_dir| may be NULL.
+// Examples of |env_name| are XDG_CONFIG_HOME and XDG_DATA_HOME.
+BASE_EXPORT FilePath GetXDGDirectory(Environment* env, const char* env_name,
+                                     const char* fallback_dir);
+
+// Wrapper around xdg_user_dir_lookup() from src/base/third_party/xdg-user-dirs
+// This looks up "well known" user directories like the desktop and music
+// folder. Examples of |dir_name| are DESKTOP and MUSIC.
+BASE_EXPORT FilePath GetXDGUserDirectory(const char* dir_name,
+                                         const char* fallback_dir);
+
+enum DesktopEnvironment {
+  DESKTOP_ENVIRONMENT_OTHER,
+  DESKTOP_ENVIRONMENT_CINNAMON,
+  DESKTOP_ENVIRONMENT_GNOME,
+  // KDE3, KDE4 and KDE5 are sufficiently different that we count
+  // them as different desktop environments here.
+  DESKTOP_ENVIRONMENT_KDE3,
+  DESKTOP_ENVIRONMENT_KDE4,
+  DESKTOP_ENVIRONMENT_KDE5,
+  DESKTOP_ENVIRONMENT_PANTHEON,
+  DESKTOP_ENVIRONMENT_UNITY,
+  DESKTOP_ENVIRONMENT_XFCE,
+};
+
+// Return an entry from the DesktopEnvironment enum with a best guess
+// of which desktop environment we're using.  We use this to know when
+// to attempt to use preferences from the desktop environment --
+// proxy settings, password manager, etc.
+BASE_EXPORT DesktopEnvironment GetDesktopEnvironment(Environment* env);
+
+// Return a string representation of the given desktop environment.
+// May return NULL in the case of DESKTOP_ENVIRONMENT_OTHER.
+BASE_EXPORT const char* GetDesktopEnvironmentName(DesktopEnvironment env);
+// Convenience wrapper that calls GetDesktopEnvironment() first.
+BASE_EXPORT const char* GetDesktopEnvironmentName(Environment* env);
+
+}  // namespace nix
+}  // namespace base
+
+#endif  // BASE_NIX_XDG_UTIL_H_
diff --git a/base/nix/xdg_util_unittest.cc b/base/nix/xdg_util_unittest.cc
new file mode 100644
index 0000000..e195303
--- /dev/null
+++ b/base/nix/xdg_util_unittest.cc
@@ -0,0 +1,181 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/nix/xdg_util.h"
+
+#include "base/environment.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::_;
+using ::testing::Eq;
+using ::testing::Return;
+using ::testing::SetArgPointee;
+
+namespace base {
+namespace nix {
+
+namespace {
+
+class MockEnvironment : public Environment {
+ public:
+  MOCK_METHOD2(GetVar, bool(StringPiece, std::string* result));
+  MOCK_METHOD2(SetVar, bool(StringPiece, const std::string& new_value));
+  MOCK_METHOD1(UnSetVar, bool(StringPiece));
+};
+
+// Needs to be const char* to make gmock happy.
+const char* const kDesktopGnome = "gnome";
+const char* const kDesktopGnomeFallback = "gnome-fallback";
+const char* const kDesktopMATE = "mate";
+const char* const kDesktopKDE4 = "kde4";
+const char* const kDesktopKDE = "kde";
+const char* const kDesktopXFCE = "xfce";
+const char* const kXdgDesktopCinnamon = "X-Cinnamon";
+const char* const kXdgDesktopGNOME = "GNOME";
+const char* const kXdgDesktopKDE = "KDE";
+const char* const kXdgDesktopPantheon = "Pantheon";
+const char* const kXdgDesktopUnity = "Unity";
+const char* const kXdgDesktopUnity7 = "Unity:Unity7";
+const char* const kXdgDesktopUnity8 = "Unity:Unity8";
+const char* const kKDESessionKDE5 = "5";
+
+const char kDesktopSession[] = "DESKTOP_SESSION";
+const char kKDESession[] = "KDE_SESSION_VERSION";
+const char kXdgDesktop[] = "XDG_CURRENT_DESKTOP";
+
+}  // namespace
+
+TEST(XDGUtilTest, GetDesktopEnvironmentGnome) {
+  MockEnvironment getter;
+  EXPECT_CALL(getter, GetVar(_, _)).WillRepeatedly(Return(false));
+  EXPECT_CALL(getter, GetVar(Eq(kDesktopSession), _))
+      .WillOnce(DoAll(SetArgPointee<1>(kDesktopGnome), Return(true)));
+
+  EXPECT_EQ(DESKTOP_ENVIRONMENT_GNOME, GetDesktopEnvironment(&getter));
+}
+
+TEST(XDGUtilTest, GetDesktopEnvironmentMATE) {
+  MockEnvironment getter;
+  EXPECT_CALL(getter, GetVar(_, _)).WillRepeatedly(Return(false));
+  EXPECT_CALL(getter, GetVar(Eq(kDesktopSession), _))
+      .WillOnce(DoAll(SetArgPointee<1>(kDesktopMATE), Return(true)));
+
+  EXPECT_EQ(DESKTOP_ENVIRONMENT_GNOME, GetDesktopEnvironment(&getter));
+}
+
+TEST(XDGUtilTest, GetDesktopEnvironmentKDE4) {
+  MockEnvironment getter;
+  EXPECT_CALL(getter, GetVar(_, _)).WillRepeatedly(Return(false));
+  EXPECT_CALL(getter, GetVar(Eq(kDesktopSession), _))
+      .WillOnce(DoAll(SetArgPointee<1>(kDesktopKDE4), Return(true)));
+
+  EXPECT_EQ(DESKTOP_ENVIRONMENT_KDE4, GetDesktopEnvironment(&getter));
+}
+
+TEST(XDGUtilTest, GetDesktopEnvironmentKDE3) {
+  MockEnvironment getter;
+  EXPECT_CALL(getter, GetVar(_, _)).WillRepeatedly(Return(false));
+  EXPECT_CALL(getter, GetVar(Eq(kDesktopSession), _))
+      .WillOnce(DoAll(SetArgPointee<1>(kDesktopKDE), Return(true)));
+
+  EXPECT_EQ(DESKTOP_ENVIRONMENT_KDE3, GetDesktopEnvironment(&getter));
+}
+
+TEST(XDGUtilTest, GetDesktopEnvironmentXFCE) {
+  MockEnvironment getter;
+  EXPECT_CALL(getter, GetVar(_, _)).WillRepeatedly(Return(false));
+  EXPECT_CALL(getter, GetVar(Eq(kDesktopSession), _))
+      .WillOnce(DoAll(SetArgPointee<1>(kDesktopXFCE), Return(true)));
+
+  EXPECT_EQ(DESKTOP_ENVIRONMENT_XFCE, GetDesktopEnvironment(&getter));
+}
+
+TEST(XDGUtilTest, GetXdgDesktopCinnamon) {
+  MockEnvironment getter;
+  EXPECT_CALL(getter, GetVar(_, _)).WillRepeatedly(Return(false));
+  EXPECT_CALL(getter, GetVar(Eq(kXdgDesktop), _))
+      .WillOnce(DoAll(SetArgPointee<1>(kXdgDesktopCinnamon), Return(true)));
+
+  EXPECT_EQ(DESKTOP_ENVIRONMENT_CINNAMON, GetDesktopEnvironment(&getter));
+}
+
+TEST(XDGUtilTest, GetXdgDesktopGnome) {
+  MockEnvironment getter;
+  EXPECT_CALL(getter, GetVar(_, _)).WillRepeatedly(Return(false));
+  EXPECT_CALL(getter, GetVar(Eq(kXdgDesktop), _))
+      .WillOnce(DoAll(SetArgPointee<1>(kXdgDesktopGNOME), Return(true)));
+
+  EXPECT_EQ(DESKTOP_ENVIRONMENT_GNOME, GetDesktopEnvironment(&getter));
+}
+
+TEST(XDGUtilTest, GetXdgDesktopGnomeFallback) {
+  MockEnvironment getter;
+  EXPECT_CALL(getter, GetVar(_, _)).WillRepeatedly(Return(false));
+  EXPECT_CALL(getter, GetVar(Eq(kXdgDesktop), _))
+      .WillOnce(DoAll(SetArgPointee<1>(kXdgDesktopUnity), Return(true)));
+  EXPECT_CALL(getter, GetVar(Eq(kDesktopSession), _))
+      .WillOnce(DoAll(SetArgPointee<1>(kDesktopGnomeFallback), Return(true)));
+
+  EXPECT_EQ(DESKTOP_ENVIRONMENT_GNOME, GetDesktopEnvironment(&getter));
+}
+
+TEST(XDGUtilTest, GetXdgDesktopKDE5) {
+  MockEnvironment getter;
+  EXPECT_CALL(getter, GetVar(_, _)).WillRepeatedly(Return(false));
+  EXPECT_CALL(getter, GetVar(Eq(kXdgDesktop), _))
+      .WillOnce(DoAll(SetArgPointee<1>(kXdgDesktopKDE), Return(true)));
+  EXPECT_CALL(getter, GetVar(Eq(kKDESession), _))
+      .WillOnce(DoAll(SetArgPointee<1>(kKDESessionKDE5), Return(true)));
+
+  EXPECT_EQ(DESKTOP_ENVIRONMENT_KDE5, GetDesktopEnvironment(&getter));
+}
+
+TEST(XDGUtilTest, GetXdgDesktopKDE4) {
+  MockEnvironment getter;
+  EXPECT_CALL(getter, GetVar(_, _)).WillRepeatedly(Return(false));
+  EXPECT_CALL(getter, GetVar(Eq(kXdgDesktop), _))
+      .WillOnce(DoAll(SetArgPointee<1>(kXdgDesktopKDE), Return(true)));
+
+  EXPECT_EQ(DESKTOP_ENVIRONMENT_KDE4, GetDesktopEnvironment(&getter));
+}
+
+TEST(XDGUtilTest, GetXdgDesktopPantheon) {
+  MockEnvironment getter;
+  EXPECT_CALL(getter, GetVar(_, _)).WillRepeatedly(Return(false));
+  EXPECT_CALL(getter, GetVar(Eq(kXdgDesktop), _))
+      .WillOnce(DoAll(SetArgPointee<1>(kXdgDesktopPantheon), Return(true)));
+
+  EXPECT_EQ(DESKTOP_ENVIRONMENT_PANTHEON, GetDesktopEnvironment(&getter));
+}
+
+TEST(XDGUtilTest, GetXdgDesktopUnity) {
+  MockEnvironment getter;
+  EXPECT_CALL(getter, GetVar(_, _)).WillRepeatedly(Return(false));
+  EXPECT_CALL(getter, GetVar(Eq(kXdgDesktop), _))
+      .WillOnce(DoAll(SetArgPointee<1>(kXdgDesktopUnity), Return(true)));
+
+  EXPECT_EQ(DESKTOP_ENVIRONMENT_UNITY, GetDesktopEnvironment(&getter));
+}
+
+TEST(XDGUtilTest, GetXdgDesktopUnity7) {
+  MockEnvironment getter;
+  EXPECT_CALL(getter, GetVar(_, _)).WillRepeatedly(Return(false));
+  EXPECT_CALL(getter, GetVar(Eq(kXdgDesktop), _))
+      .WillOnce(DoAll(SetArgPointee<1>(kXdgDesktopUnity7), Return(true)));
+
+  EXPECT_EQ(DESKTOP_ENVIRONMENT_UNITY, GetDesktopEnvironment(&getter));
+}
+
+TEST(XDGUtilTest, GetXdgDesktopUnity8) {
+  MockEnvironment getter;
+  EXPECT_CALL(getter, GetVar(_, _)).WillRepeatedly(Return(false));
+  EXPECT_CALL(getter, GetVar(Eq(kXdgDesktop), _))
+      .WillOnce(DoAll(SetArgPointee<1>(kXdgDesktopUnity8), Return(true)));
+
+  EXPECT_EQ(DESKTOP_ENVIRONMENT_UNITY, GetDesktopEnvironment(&getter));
+}
+
+}  // namespace nix
+}  // namespace base
diff --git a/base/numerics/BUILD.gn b/base/numerics/BUILD.gn
deleted file mode 100644
index 0bb8dd1..0000000
--- a/base/numerics/BUILD.gn
+++ /dev/null
@@ -1,28 +0,0 @@
-# Copyright (c) 2017 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-# This is a dependency-free, header-only, library, and it needs to stay that
-# way to facilitate pulling it into various third-party projects. So, this
-# file is here to protect against accidentally introducing external
-# dependencies or depending on internal implementation details.
-source_set("base_numerics") {
-  visibility = [ "//base/*" ]
-  sources = [
-    "checked_math_impl.h",
-    "clamped_math_impl.h",
-    "safe_conversions_arm_impl.h",
-    "safe_conversions_impl.h",
-    "safe_math_arm_impl.h",
-    "safe_math_clang_gcc_impl.h",
-    "safe_math_shared_impl.h",
-  ]
-  public = [
-    "checked_math.h",
-    "clamped_math.h",
-    "math_constants.h",
-    "ranges.h",
-    "safe_conversions.h",
-    "safe_math.h",
-  ]
-}
diff --git a/base/os_compat_android.cc b/base/os_compat_android.cc
new file mode 100644
index 0000000..c1a2ac8
--- /dev/null
+++ b/base/os_compat_android.cc
@@ -0,0 +1,178 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/os_compat_android.h"
+
+#include <asm/unistd.h>
+#include <errno.h>
+#include <limits.h>
+#include <math.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+#if !defined(__LP64__)
+#include <time64.h>
+#endif
+
+#include "base/rand_util.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/stringprintf.h"
+
+extern "C" {
+// There is no futimes() avaiable in Bionic, so we provide our own
+// implementation until it is there.
+int futimes(int fd, const struct timeval tv[2]) {
+  if (tv == NULL)
+    return syscall(__NR_utimensat, fd, NULL, NULL, 0);
+
+  if (tv[0].tv_usec < 0 || tv[0].tv_usec >= 1000000 ||
+      tv[1].tv_usec < 0 || tv[1].tv_usec >= 1000000) {
+    errno = EINVAL;
+    return -1;
+  }
+
+  // Convert timeval to timespec.
+  struct timespec ts[2];
+  ts[0].tv_sec = tv[0].tv_sec;
+  ts[0].tv_nsec = tv[0].tv_usec * 1000;
+  ts[1].tv_sec = tv[1].tv_sec;
+  ts[1].tv_nsec = tv[1].tv_usec * 1000;
+  return syscall(__NR_utimensat, fd, NULL, ts, 0);
+}
+
+#if !defined(__LP64__)
+// 32-bit Android has only timegm64() and not timegm().
+// We replicate the behaviour of timegm() when the result overflows time_t.
+time_t timegm(struct tm* const t) {
+  // time_t is signed on Android.
+  static const time_t kTimeMax = ~(1L << (sizeof(time_t) * CHAR_BIT - 1));
+  static const time_t kTimeMin = (1L << (sizeof(time_t) * CHAR_BIT - 1));
+  time64_t result = timegm64(t);
+  if (result < kTimeMin || result > kTimeMax)
+    return -1;
+  return result;
+}
+#endif
+
+// The following is only needed when building with GCC 4.6 or higher
+// (i.e. not with Android GCC 4.4.3, nor with Clang).
+//
+// GCC is now capable of optimizing successive calls to sin() and cos() into
+// a single call to sincos(). This means that source code that looks like:
+//
+//     double c, s;
+//     c = cos(angle);
+//     s = sin(angle);
+//
+// Will generate machine code that looks like:
+//
+//     double c, s;
+//     sincos(angle, &s, &c);
+//
+// Unfortunately, sincos() and friends are not part of the Android libm.so
+// library provided by the NDK for API level 9. When the optimization kicks
+// in, it makes the final build fail with a puzzling message (puzzling
+// because 'sincos' doesn't appear anywhere in the sources!).
+//
+// To solve this, we provide our own implementation of the sincos() function
+// and related friends. Note that we must also explicitely tell GCC to disable
+// optimizations when generating these. Otherwise, the generated machine code
+// for each function would simply end up calling itself, resulting in a
+// runtime crash due to stack overflow.
+//
+#if defined(__GNUC__) && !defined(__clang__) && \
+    !defined(ANDROID_SINCOS_PROVIDED)
+
+// For the record, Clang does not support the 'optimize' attribute.
+// In the unlikely event that it begins performing this optimization too,
+// we'll have to find a different way to achieve this. NOTE: Tested with O1
+// which still performs the optimization.
+//
+#define GCC_NO_OPTIMIZE  __attribute__((optimize("O0")))
+
+GCC_NO_OPTIMIZE
+void sincos(double angle, double* s, double *c) {
+  *c = cos(angle);
+  *s = sin(angle);
+}
+
+GCC_NO_OPTIMIZE
+void sincosf(float angle, float* s, float* c) {
+  *c = cosf(angle);
+  *s = sinf(angle);
+}
+
+#endif // __GNUC__ && !__clang__
+
+// An implementation of mkdtemp, since it is not exposed by the NDK
+// for native API level 9 that we target.
+//
+// For any changes in the mkdtemp function, you should manually run the unittest
+// OsCompatAndroidTest.DISABLED_TestMkdTemp in your local machine to check if it
+// passes. Please don't enable it, since it creates a directory and may be
+// source of flakyness.
+char* mkdtemp(char* path) {
+  if (path == NULL) {
+    errno = EINVAL;
+    return NULL;
+  }
+
+  const int path_len = strlen(path);
+
+  // The last six characters of 'path' must be XXXXXX.
+  const base::StringPiece kSuffix("XXXXXX");
+  const int kSuffixLen = kSuffix.length();
+  if (!base::StringPiece(path, path_len).ends_with(kSuffix)) {
+    errno = EINVAL;
+    return NULL;
+  }
+
+  // If the path contains a directory, as in /tmp/foo/XXXXXXXX, make sure
+  // that /tmp/foo exists, otherwise we're going to loop a really long
+  // time for nothing below
+  char* dirsep = strrchr(path, '/');
+  if (dirsep != NULL) {
+    struct stat st;
+    int ret;
+
+    *dirsep = '\0';  // Terminating directory path temporarily
+
+    ret = stat(path, &st);
+
+    *dirsep = '/';  // Restoring directory separator
+    if (ret < 0)  // Directory probably does not exist
+      return NULL;
+    if (!S_ISDIR(st.st_mode)) {  // Not a directory
+      errno = ENOTDIR;
+      return NULL;
+    }
+  }
+
+  // Max number of tries using different random suffixes.
+  const int kMaxTries = 100;
+
+  // Now loop until we CAN create a directory by that name or we reach the max
+  // number of tries.
+  for (int i = 0; i < kMaxTries; ++i) {
+    // Fill the suffix XXXXXX with a random string composed of a-z chars.
+    for (int pos = 0; pos < kSuffixLen; ++pos) {
+      char rand_char = static_cast<char>(base::RandInt('a', 'z'));
+      path[path_len - kSuffixLen + pos] = rand_char;
+    }
+    if (mkdir(path, 0700) == 0) {
+      // We just created the directory succesfully.
+      return path;
+    }
+    if (errno != EEXIST) {
+      // The directory doesn't exist, but an error occured
+      return NULL;
+    }
+  }
+
+  // We reached the max number of tries.
+  return NULL;
+}
+
+}  // extern "C"
diff --git a/base/os_compat_android.h b/base/os_compat_android.h
new file mode 100644
index 0000000..e33b1f7
--- /dev/null
+++ b/base/os_compat_android.h
@@ -0,0 +1,21 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_OS_COMPAT_ANDROID_H_
+#define BASE_OS_COMPAT_ANDROID_H_
+
+#include <fcntl.h>
+#include <sys/types.h>
+#include <utime.h>
+
+// Not implemented in Bionic.
+extern "C" int futimes(int fd, const struct timeval tv[2]);
+
+// Not exposed or implemented in Bionic.
+extern "C" char* mkdtemp(char* path);
+
+// Android has no timegm().
+extern "C" time_t timegm(struct tm* const t);
+
+#endif  // BASE_OS_COMPAT_ANDROID_H_
diff --git a/base/os_compat_android_unittest.cc b/base/os_compat_android_unittest.cc
new file mode 100644
index 0000000..7fbdc6d
--- /dev/null
+++ b/base/os_compat_android_unittest.cc
@@ -0,0 +1,41 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/os_compat_android.h"
+
+#include "base/files/file_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+typedef testing::Test OsCompatAndroidTest;
+
+// Keep this Unittest DISABLED_ , because it actually creates a directory in the
+// device and it may be source of flakyness. For any changes in the mkdtemp
+// function, you should run this unittest in your local machine to check if it
+// passes.
+TEST_F(OsCompatAndroidTest, DISABLED_TestMkdTemp) {
+  FilePath tmp_dir;
+  EXPECT_TRUE(base::GetTempDir(&tmp_dir));
+
+  // Not six XXXXXX at the suffix of the path.
+  FilePath sub_dir = tmp_dir.Append("XX");
+  std::string sub_dir_string = sub_dir.value();
+  // this should be OK since mkdtemp just replaces characters in place
+  char* buffer = const_cast<char*>(sub_dir_string.c_str());
+  EXPECT_EQ(NULL, mkdtemp(buffer));
+
+  // Directory does not exist
+  char invalid_path2[] = "doesntoexist/foobarXXXXXX";
+  EXPECT_EQ(NULL, mkdtemp(invalid_path2));
+
+  // Successfully create a tmp dir.
+  FilePath sub_dir2 = tmp_dir.Append("XXXXXX");
+  std::string sub_dir2_string = sub_dir2.value();
+  // this should be OK since mkdtemp just replaces characters in place
+  char* buffer2 = const_cast<char*>(sub_dir2_string.c_str());
+  EXPECT_TRUE(mkdtemp(buffer2) != NULL);
+}
+
+}  // namespace base
diff --git a/base/path_service_unittest.cc b/base/path_service_unittest.cc
new file mode 100644
index 0000000..cf69ef1
--- /dev/null
+++ b/base/path_service_unittest.cc
@@ -0,0 +1,278 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/path_service.h"
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/strings/string_util.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest-spi.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/platform_test.h"
+
+#if defined(OS_WIN)
+#include "base/win/windows_version.h"
+#endif
+
+namespace base {
+
+namespace {
+
+// 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) {
+  FilePath path;
+  bool result = PathService::Get(dir_type, &path);
+
+  // Some paths might not exist on some platforms in which case confirming
+  // |result| is true and !path.empty() is the best we can do.
+  bool check_path_exists = true;
+#if defined(OS_POSIX)
+  // If chromium has never been started on this account, the cache path may not
+  // exist.
+  if (dir_type == DIR_CACHE)
+    check_path_exists = false;
+#endif
+#if defined(OS_LINUX)
+  // On the linux try-bots: a path is returned (e.g. /home/chrome-bot/Desktop),
+  // but it doesn't exist.
+  if (dir_type == DIR_USER_DESKTOP)
+    check_path_exists = false;
+#endif
+#if defined(OS_IOS)
+  // Bundled unittests on iOS may not have Resources directory in the bundle.
+  if (dir_type == DIR_ASSETS)
+    check_path_exists = false;
+#endif
+#if defined(OS_MACOSX)
+  if (dir_type != DIR_EXE && dir_type != DIR_MODULE && dir_type != FILE_EXE &&
+      dir_type != FILE_MODULE) {
+    if (path.ReferencesParent())
+      return false;
+  }
+#else
+  if (path.ReferencesParent())
+    return false;
+#endif
+  return result && !path.empty() && (!check_path_exists || PathExists(path));
+}
+
+#if defined(OS_WIN)
+// Function to test any directory keys that are not supported on some versions
+// of Windows. Checks that the function fails and that the returned path is
+// empty.
+bool ReturnsInvalidPath(int dir_type) {
+  FilePath path;
+  bool result = PathService::Get(dir_type, &path);
+  return !result && path.empty();
+}
+#endif
+
+}  // namespace
+
+// On the Mac this winds up using some autoreleased objects, so we need to
+// be a PlatformTest.
+typedef PlatformTest PathServiceTest;
+
+// 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_F(PathServiceTest, Get) {
+  for (int key = PATH_START + 1; key < PATH_END; ++key) {
+#if defined(OS_ANDROID)
+    if (key == FILE_MODULE || key == DIR_USER_DESKTOP ||
+        key == DIR_HOME)
+      continue;  // Android doesn't implement these.
+#elif defined(OS_IOS)
+    if (key == DIR_USER_DESKTOP)
+      continue;  // iOS doesn't implement DIR_USER_DESKTOP.
+#elif defined(OS_FUCHSIA)
+    if (key == DIR_USER_DESKTOP || key == FILE_MODULE || key == DIR_MODULE)
+      continue;  // Fuchsia doesn't implement DIR_USER_DESKTOP, FILE_MODULE and
+                 // DIR_MODULE.
+#endif
+    EXPECT_PRED1(ReturnsValidPath, key);
+  }
+#if defined(OS_WIN)
+  for (int key = PATH_WIN_START + 1; key < PATH_WIN_END; ++key) {
+    bool valid = true;
+    if (key == DIR_APP_SHORTCUTS)
+      valid = base::win::GetVersion() >= base::win::VERSION_WIN8;
+
+    if (valid)
+      EXPECT_TRUE(ReturnsValidPath(key)) << key;
+    else
+      EXPECT_TRUE(ReturnsInvalidPath(key)) << key;
+  }
+#elif defined(OS_MACOSX)
+  for (int key = PATH_MAC_START + 1; key < PATH_MAC_END; ++key) {
+    EXPECT_PRED1(ReturnsValidPath, key);
+  }
+#elif defined(OS_ANDROID)
+  for (int key = PATH_ANDROID_START + 1; key < PATH_ANDROID_END;
+       ++key) {
+    EXPECT_PRED1(ReturnsValidPath, key);
+  }
+#elif defined(OS_POSIX)
+  for (int key = PATH_POSIX_START + 1; key < PATH_POSIX_END;
+       ++key) {
+    EXPECT_PRED1(ReturnsValidPath, key);
+  }
+#endif
+}
+
+// Test that all versions of the Override function of PathService do what they
+// are supposed to do.
+TEST_F(PathServiceTest, Override) {
+  int my_special_key = 666;
+  ScopedTempDir temp_dir;
+  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+  FilePath fake_cache_dir(temp_dir.GetPath().AppendASCII("cache"));
+  // PathService::Override should always create the path provided if it doesn't
+  // exist.
+  EXPECT_TRUE(PathService::Override(my_special_key, fake_cache_dir));
+  EXPECT_TRUE(PathExists(fake_cache_dir));
+
+  FilePath fake_cache_dir2(temp_dir.GetPath().AppendASCII("cache2"));
+  // PathService::OverrideAndCreateIfNeeded should obey the |create| parameter.
+  PathService::OverrideAndCreateIfNeeded(my_special_key,
+                                         fake_cache_dir2,
+                                         false,
+                                         false);
+  EXPECT_FALSE(PathExists(fake_cache_dir2));
+  EXPECT_TRUE(PathService::OverrideAndCreateIfNeeded(my_special_key,
+                                                     fake_cache_dir2,
+                                                     false,
+                                                     true));
+  EXPECT_TRUE(PathExists(fake_cache_dir2));
+
+#if defined(OS_POSIX)
+  FilePath non_existent(
+      MakeAbsoluteFilePath(temp_dir.GetPath()).AppendASCII("non_existent"));
+  EXPECT_TRUE(non_existent.IsAbsolute());
+  EXPECT_FALSE(PathExists(non_existent));
+#if !defined(OS_ANDROID)
+  // This fails because MakeAbsoluteFilePath fails for non-existent files.
+  // Earlier versions of Bionic libc don't fail for non-existent files, so
+  // skip this check on Android.
+  EXPECT_FALSE(PathService::OverrideAndCreateIfNeeded(my_special_key,
+                                                      non_existent,
+                                                      false,
+                                                      false));
+#endif
+  // This works because indicating that |non_existent| is absolute skips the
+  // internal MakeAbsoluteFilePath call.
+  EXPECT_TRUE(PathService::OverrideAndCreateIfNeeded(my_special_key,
+                                                     non_existent,
+                                                     true,
+                                                     false));
+  // Check that the path has been overridden and no directory was created.
+  EXPECT_FALSE(PathExists(non_existent));
+  FilePath path;
+  EXPECT_TRUE(PathService::Get(my_special_key, &path));
+  EXPECT_EQ(non_existent, path);
+#endif
+}
+
+// Check if multiple overrides can co-exist.
+TEST_F(PathServiceTest, OverrideMultiple) {
+  int my_special_key = 666;
+  ScopedTempDir temp_dir;
+  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+  FilePath fake_cache_dir1(temp_dir.GetPath().AppendASCII("1"));
+  EXPECT_TRUE(PathService::Override(my_special_key, fake_cache_dir1));
+  EXPECT_TRUE(PathExists(fake_cache_dir1));
+  ASSERT_EQ(1, WriteFile(fake_cache_dir1.AppendASCII("t1"), ".", 1));
+
+  FilePath fake_cache_dir2(temp_dir.GetPath().AppendASCII("2"));
+  EXPECT_TRUE(PathService::Override(my_special_key + 1, fake_cache_dir2));
+  EXPECT_TRUE(PathExists(fake_cache_dir2));
+  ASSERT_EQ(1, WriteFile(fake_cache_dir2.AppendASCII("t2"), ".", 1));
+
+  FilePath result;
+  EXPECT_TRUE(PathService::Get(my_special_key, &result));
+  // Override might have changed the path representation but our test file
+  // should be still there.
+  EXPECT_TRUE(PathExists(result.AppendASCII("t1")));
+  EXPECT_TRUE(PathService::Get(my_special_key + 1, &result));
+  EXPECT_TRUE(PathExists(result.AppendASCII("t2")));
+}
+
+TEST_F(PathServiceTest, RemoveOverride) {
+  // Before we start the test we have to call RemoveOverride at least once to
+  // clear any overrides that might have been left from other tests.
+  PathService::RemoveOverride(DIR_TEMP);
+
+  FilePath original_user_data_dir;
+  EXPECT_TRUE(PathService::Get(DIR_TEMP, &original_user_data_dir));
+  EXPECT_FALSE(PathService::RemoveOverride(DIR_TEMP));
+
+  ScopedTempDir temp_dir;
+  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+  EXPECT_TRUE(PathService::Override(DIR_TEMP, temp_dir.GetPath()));
+  FilePath new_user_data_dir;
+  EXPECT_TRUE(PathService::Get(DIR_TEMP, &new_user_data_dir));
+  EXPECT_NE(original_user_data_dir, new_user_data_dir);
+
+  EXPECT_TRUE(PathService::RemoveOverride(DIR_TEMP));
+  EXPECT_TRUE(PathService::Get(DIR_TEMP, &new_user_data_dir));
+  EXPECT_EQ(original_user_data_dir, new_user_data_dir);
+}
+
+#if defined(OS_WIN)
+TEST_F(PathServiceTest, GetProgramFiles) {
+  FilePath programfiles_dir;
+#if defined(_WIN64)
+  // 64-bit on 64-bit.
+  EXPECT_TRUE(PathService::Get(DIR_PROGRAM_FILES,
+      &programfiles_dir));
+  EXPECT_EQ(programfiles_dir.value(),
+      FILE_PATH_LITERAL("C:\\Program Files"));
+  EXPECT_TRUE(PathService::Get(DIR_PROGRAM_FILESX86,
+      &programfiles_dir));
+  EXPECT_EQ(programfiles_dir.value(),
+      FILE_PATH_LITERAL("C:\\Program Files (x86)"));
+  EXPECT_TRUE(PathService::Get(DIR_PROGRAM_FILES6432,
+      &programfiles_dir));
+  EXPECT_EQ(programfiles_dir.value(),
+      FILE_PATH_LITERAL("C:\\Program Files"));
+#else
+  if (base::win::OSInfo::GetInstance()->wow64_status() ==
+      base::win::OSInfo::WOW64_ENABLED) {
+    // 32-bit on 64-bit.
+    EXPECT_TRUE(PathService::Get(DIR_PROGRAM_FILES,
+        &programfiles_dir));
+    EXPECT_EQ(programfiles_dir.value(),
+        FILE_PATH_LITERAL("C:\\Program Files (x86)"));
+    EXPECT_TRUE(PathService::Get(DIR_PROGRAM_FILESX86,
+        &programfiles_dir));
+    EXPECT_EQ(programfiles_dir.value(),
+        FILE_PATH_LITERAL("C:\\Program Files (x86)"));
+    EXPECT_TRUE(PathService::Get(DIR_PROGRAM_FILES6432,
+        &programfiles_dir));
+    EXPECT_EQ(programfiles_dir.value(),
+        FILE_PATH_LITERAL("C:\\Program Files"));
+  } else {
+    // 32-bit on 32-bit.
+    EXPECT_TRUE(PathService::Get(DIR_PROGRAM_FILES,
+        &programfiles_dir));
+    EXPECT_EQ(programfiles_dir.value(),
+        FILE_PATH_LITERAL("C:\\Program Files"));
+    EXPECT_TRUE(PathService::Get(DIR_PROGRAM_FILESX86,
+        &programfiles_dir));
+    EXPECT_EQ(programfiles_dir.value(),
+        FILE_PATH_LITERAL("C:\\Program Files"));
+    EXPECT_TRUE(PathService::Get(DIR_PROGRAM_FILES6432,
+        &programfiles_dir));
+    EXPECT_EQ(programfiles_dir.value(),
+        FILE_PATH_LITERAL("C:\\Program Files"));
+  }
+#endif
+}
+#endif
+
+}  // namespace base
diff --git a/base/power_monitor/power_monitor.cc b/base/power_monitor/power_monitor.cc
new file mode 100644
index 0000000..ba0ef6b
--- /dev/null
+++ b/base/power_monitor/power_monitor.cc
@@ -0,0 +1,66 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/power_monitor/power_monitor.h"
+
+#include <utility>
+
+#include "base/power_monitor/power_monitor_source.h"
+
+namespace base {
+
+static PowerMonitor* g_power_monitor = nullptr;
+
+PowerMonitor::PowerMonitor(std::unique_ptr<PowerMonitorSource> source)
+    : observers_(new ObserverListThreadSafe<PowerObserver>()),
+      source_(std::move(source)) {
+  DCHECK(!g_power_monitor);
+  g_power_monitor = this;
+}
+
+PowerMonitor::~PowerMonitor() {
+  source_->Shutdown();
+  DCHECK_EQ(this, g_power_monitor);
+  g_power_monitor = nullptr;
+}
+
+// static
+PowerMonitor* PowerMonitor::Get() {
+  return g_power_monitor;
+}
+
+void PowerMonitor::AddObserver(PowerObserver* obs) {
+  observers_->AddObserver(obs);
+}
+
+void PowerMonitor::RemoveObserver(PowerObserver* obs) {
+  observers_->RemoveObserver(obs);
+}
+
+PowerMonitorSource* PowerMonitor::Source() {
+  return source_.get();
+}
+
+bool PowerMonitor::IsOnBatteryPower() {
+  return source_->IsOnBatteryPower();
+}
+
+void PowerMonitor::NotifyPowerStateChange(bool battery_in_use) {
+  DVLOG(1) << "PowerStateChange: " << (battery_in_use ? "On" : "Off")
+           << " battery";
+  observers_->Notify(FROM_HERE, &PowerObserver::OnPowerStateChange,
+                     battery_in_use);
+}
+
+void PowerMonitor::NotifySuspend() {
+  DVLOG(1) << "Power Suspending";
+  observers_->Notify(FROM_HERE, &PowerObserver::OnSuspend);
+}
+
+void PowerMonitor::NotifyResume() {
+  DVLOG(1) << "Power Resuming";
+  observers_->Notify(FROM_HERE, &PowerObserver::OnResume);
+}
+
+}  // namespace base
diff --git a/base/power_monitor/power_monitor_device_source.cc b/base/power_monitor/power_monitor_device_source.cc
new file mode 100644
index 0000000..f420654
--- /dev/null
+++ b/base/power_monitor/power_monitor_device_source.cc
@@ -0,0 +1,33 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/power_monitor/power_monitor_device_source.h"
+
+namespace base {
+
+PowerMonitorDeviceSource::PowerMonitorDeviceSource() {
+#if defined(OS_MACOSX)
+  PlatformInit();
+#endif
+
+#if defined(OS_WIN) || defined(OS_MACOSX)
+  // Provide the correct battery status if possible. Others platforms, such as
+  // Android and ChromeOS, will update their status once their backends are
+  // actually initialized.
+  SetInitialOnBatteryPowerState(IsOnBatteryPowerImpl());
+#endif
+}
+
+PowerMonitorDeviceSource::~PowerMonitorDeviceSource() {
+#if defined(OS_MACOSX)
+  PlatformDestroy();
+#endif
+}
+
+// PowerMonitorDeviceSource does not need to take any special action to ensure
+// that it doesn't callback into PowerMonitor after this phase of shutdown has
+// completed.
+void PowerMonitorDeviceSource::Shutdown() {}
+
+}  // namespace base
diff --git a/base/power_monitor/power_monitor_device_source_android.cc b/base/power_monitor/power_monitor_device_source_android.cc
new file mode 100644
index 0000000..7688513
--- /dev/null
+++ b/base/power_monitor/power_monitor_device_source_android.cc
@@ -0,0 +1,39 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/power_monitor/power_monitor.h"
+#include "base/power_monitor/power_monitor_device_source.h"
+#include "base/power_monitor/power_monitor_source.h"
+#include "jni/PowerMonitor_jni.h"
+
+namespace base {
+
+// A helper function which is a friend of PowerMonitorSource.
+void ProcessPowerEventHelper(PowerMonitorSource::PowerEvent event) {
+  PowerMonitorSource::ProcessPowerEvent(event);
+}
+
+namespace android {
+
+// Native implementation of PowerMonitor.java. Note: This will be invoked by
+// PowerMonitor.java shortly after startup to set the correct initial value for
+// "is on battery power."
+void JNI_PowerMonitor_OnBatteryChargingChanged(
+    JNIEnv* env,
+    const JavaParamRef<jclass>& clazz) {
+  ProcessPowerEventHelper(PowerMonitorSource::POWER_STATE_EVENT);
+}
+
+// Note: Android does not have the concept of suspend / resume as it's known by
+// other platforms. Thus we do not send Suspend/Resume notifications. See
+// http://crbug.com/644515
+
+}  // namespace android
+
+bool PowerMonitorDeviceSource::IsOnBatteryPowerImpl() {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  return base::android::Java_PowerMonitor_isBatteryPower(env);
+}
+
+}  // namespace base
diff --git a/base/power_monitor/power_monitor_device_source_chromeos.cc b/base/power_monitor/power_monitor_device_source_chromeos.cc
new file mode 100644
index 0000000..c3466ee
--- /dev/null
+++ b/base/power_monitor/power_monitor_device_source_chromeos.cc
@@ -0,0 +1,40 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/power_monitor/power_monitor.h"
+#include "base/power_monitor/power_monitor_device_source.h"
+#include "base/power_monitor/power_monitor_source.h"
+
+namespace base {
+
+namespace {
+
+// The most-recently-seen power source.
+bool g_on_battery = false;
+
+}  // namespace
+
+// static
+void PowerMonitorDeviceSource::SetPowerSource(bool on_battery) {
+  if (on_battery != g_on_battery) {
+    g_on_battery = on_battery;
+    ProcessPowerEvent(POWER_STATE_EVENT);
+  }
+}
+
+// static
+void PowerMonitorDeviceSource::HandleSystemSuspending() {
+  ProcessPowerEvent(SUSPEND_EVENT);
+}
+
+// static
+void PowerMonitorDeviceSource::HandleSystemResumed() {
+  ProcessPowerEvent(RESUME_EVENT);
+}
+
+bool PowerMonitorDeviceSource::IsOnBatteryPowerImpl() {
+  return g_on_battery;
+}
+
+}  // namespace base
diff --git a/base/power_monitor/power_monitor_device_source_stub.cc b/base/power_monitor/power_monitor_device_source_stub.cc
new file mode 100644
index 0000000..f24e5b2
--- /dev/null
+++ b/base/power_monitor/power_monitor_device_source_stub.cc
@@ -0,0 +1,14 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/power_monitor/power_monitor_device_source.h"
+
+namespace base {
+
+bool PowerMonitorDeviceSource::IsOnBatteryPowerImpl() {
+  NOTIMPLEMENTED();
+  return false;
+}
+
+}  // namespace base
diff --git a/base/power_monitor/power_monitor_source.cc b/base/power_monitor/power_monitor_source.cc
new file mode 100644
index 0000000..d4757b0
--- /dev/null
+++ b/base/power_monitor/power_monitor_source.cc
@@ -0,0 +1,69 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/power_monitor/power_monitor_source.h"
+
+#include "base/power_monitor/power_monitor.h"
+#include "build/build_config.h"
+
+namespace base {
+
+PowerMonitorSource::PowerMonitorSource() = default;
+PowerMonitorSource::~PowerMonitorSource() = default;
+
+bool PowerMonitorSource::IsOnBatteryPower() {
+  AutoLock auto_lock(battery_lock_);
+  return on_battery_power_;
+}
+
+void PowerMonitorSource::ProcessPowerEvent(PowerEvent event_id) {
+  PowerMonitor* monitor = PowerMonitor::Get();
+  if (!monitor)
+    return;
+
+  PowerMonitorSource* source = monitor->Source();
+
+  // Suppress duplicate notifications.  Some platforms may
+  // send multiple notifications of the same event.
+  switch (event_id) {
+    case POWER_STATE_EVENT:
+      {
+        bool new_on_battery_power = source->IsOnBatteryPowerImpl();
+        bool changed = false;
+
+        {
+          AutoLock auto_lock(source->battery_lock_);
+          if (source->on_battery_power_ != new_on_battery_power) {
+              changed = true;
+              source->on_battery_power_ = new_on_battery_power;
+          }
+        }
+
+        if (changed)
+          monitor->NotifyPowerStateChange(new_on_battery_power);
+      }
+      break;
+    case RESUME_EVENT:
+      if (source->suspended_) {
+        source->suspended_ = false;
+        monitor->NotifyResume();
+      }
+      break;
+    case SUSPEND_EVENT:
+      if (!source->suspended_) {
+        source->suspended_ = true;
+        monitor->NotifySuspend();
+      }
+      break;
+  }
+}
+
+void PowerMonitorSource::SetInitialOnBatteryPowerState(bool on_battery_power) {
+  // Must only be called before a monitor exists, otherwise the caller should
+  // have just used a normal ProcessPowerEvent(POWER_STATE_EVENT) call.
+  DCHECK(!PowerMonitor::Get());
+  on_battery_power_ = on_battery_power;
+}
+
+}  // namespace base
diff --git a/base/power_monitor/power_monitor_unittest.cc b/base/power_monitor/power_monitor_unittest.cc
new file mode 100644
index 0000000..7f2a84b
--- /dev/null
+++ b/base/power_monitor/power_monitor_unittest.cc
@@ -0,0 +1,83 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "base/power_monitor/power_monitor.h"
+#include "base/test/power_monitor_test_base.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+class PowerMonitorTest : public testing::Test {
+ protected:
+  PowerMonitorTest() {
+    power_monitor_source_ = new PowerMonitorTestSource();
+    power_monitor_.reset(new PowerMonitor(
+        std::unique_ptr<PowerMonitorSource>(power_monitor_source_)));
+  }
+  ~PowerMonitorTest() override = default;
+
+  PowerMonitorTestSource* source() { return power_monitor_source_; }
+  PowerMonitor* monitor() { return power_monitor_.get(); }
+
+ private:
+  base::MessageLoop message_loop_;
+  PowerMonitorTestSource* power_monitor_source_;
+  std::unique_ptr<PowerMonitor> power_monitor_;
+
+  DISALLOW_COPY_AND_ASSIGN(PowerMonitorTest);
+};
+
+// PowerMonitorSource is tightly coupled with the PowerMonitor, so this test
+// Will cover both classes
+TEST_F(PowerMonitorTest, PowerNotifications) {
+  const int kObservers = 5;
+
+  PowerMonitorTestObserver observers[kObservers];
+  for (int index = 0; index < kObservers; ++index)
+    monitor()->AddObserver(&observers[index]);
+
+  // Sending resume when not suspended should have no effect.
+  source()->GenerateResumeEvent();
+  EXPECT_EQ(observers[0].resumes(), 0);
+
+  // Pretend we suspended.
+  source()->GenerateSuspendEvent();
+  // Ensure all observers were notified of the event
+  for (int index = 0; index < kObservers; ++index)
+    EXPECT_EQ(observers[index].suspends(), 1);
+
+  // Send a second suspend notification.  This should be suppressed.
+  source()->GenerateSuspendEvent();
+  EXPECT_EQ(observers[0].suspends(), 1);
+
+  // Pretend we were awakened.
+  source()->GenerateResumeEvent();
+  EXPECT_EQ(observers[0].resumes(), 1);
+
+  // Send a duplicate resume notification.  This should be suppressed.
+  source()->GenerateResumeEvent();
+  EXPECT_EQ(observers[0].resumes(), 1);
+
+  // Pretend the device has gone on battery power
+  source()->GeneratePowerStateEvent(true);
+  EXPECT_EQ(observers[0].power_state_changes(), 1);
+  EXPECT_EQ(observers[0].last_power_state(), true);
+
+  // Repeated indications the device is on battery power should be suppressed.
+  source()->GeneratePowerStateEvent(true);
+  EXPECT_EQ(observers[0].power_state_changes(), 1);
+
+  // Pretend the device has gone off battery power
+  source()->GeneratePowerStateEvent(false);
+  EXPECT_EQ(observers[0].power_state_changes(), 2);
+  EXPECT_EQ(observers[0].last_power_state(), false);
+
+  // Repeated indications the device is off battery power should be suppressed.
+  source()->GeneratePowerStateEvent(false);
+  EXPECT_EQ(observers[0].power_state_changes(), 2);
+}
+
+}  // namespace base
diff --git a/base/process/memory_unittest.cc b/base/process/memory_unittest.cc
new file mode 100644
index 0000000..835cf7e
--- /dev/null
+++ b/base/process/memory_unittest.cc
@@ -0,0 +1,533 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#define _CRT_SECURE_NO_WARNINGS
+
+#include "base/process/memory.h"
+
+#include <stddef.h>
+
+#include <limits>
+
+#include "base/allocator/allocator_check.h"
+#include "base/allocator/buildflags.h"
+#include "base/compiler_specific.h"
+#include "base/debug/alias.h"
+#include "base/memory/aligned_memory.h"
+#include "base/strings/stringprintf.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+#if defined(OS_POSIX)
+#include <errno.h>
+#endif
+#if defined(OS_MACOSX)
+#include <malloc/malloc.h>
+#include "base/allocator/allocator_interception_mac.h"
+#include "base/allocator/allocator_shim.h"
+#include "base/process/memory_unittest_mac.h"
+#endif
+#if defined(OS_LINUX)
+#include <malloc.h>
+#include "base/test/malloc_wrapper.h"
+#endif
+
+#if defined(OS_WIN)
+
+#if defined(COMPILER_MSVC)
+// ssize_t needed for OutOfMemoryTest.
+#if defined(_WIN64)
+typedef __int64 ssize_t;
+#else
+typedef long ssize_t;
+#endif
+#endif
+
+// HeapQueryInformation function pointer.
+typedef BOOL (WINAPI* HeapQueryFn)  \
+    (HANDLE, HEAP_INFORMATION_CLASS, PVOID, SIZE_T, PSIZE_T);
+
+#endif  // defined(OS_WIN)
+
+#if defined(OS_MACOSX)
+
+// For the following Mac tests:
+// Note that base::EnableTerminationOnHeapCorruption() is called as part of
+// test suite setup and does not need to be done again, else mach_override
+// will fail.
+
+TEST(ProcessMemoryTest, MacTerminateOnHeapCorruption) {
+#if BUILDFLAG(USE_ALLOCATOR_SHIM)
+  base::allocator::InitializeAllocatorShim();
+#endif
+  // Assert that freeing an unallocated pointer will crash the process.
+  char buf[9];
+  asm("" : "=r" (buf));  // Prevent clang from being too smart.
+#if ARCH_CPU_64_BITS
+  // On 64 bit Macs, the malloc system automatically abort()s on heap corruption
+  // but does not output anything.
+  ASSERT_DEATH(free(buf), "");
+#elif defined(ADDRESS_SANITIZER)
+  // AddressSanitizer replaces malloc() and prints a different error message on
+  // heap corruption.
+  ASSERT_DEATH(free(buf), "attempting free on address which "
+      "was not malloc\\(\\)-ed");
+#else
+  ADD_FAILURE() << "This test is not supported in this build configuration.";
+#endif
+
+#if BUILDFLAG(USE_ALLOCATOR_SHIM)
+  base::allocator::UninterceptMallocZonesForTesting();
+#endif
+}
+
+#endif  // defined(OS_MACOSX)
+
+TEST(MemoryTest, AllocatorShimWorking) {
+#if defined(OS_MACOSX)
+#if BUILDFLAG(USE_ALLOCATOR_SHIM)
+  base::allocator::InitializeAllocatorShim();
+#endif
+  base::allocator::InterceptAllocationsMac();
+#endif
+  ASSERT_TRUE(base::allocator::IsAllocatorInitialized());
+
+#if defined(OS_MACOSX)
+  base::allocator::UninterceptMallocZonesForTesting();
+#endif
+}
+
+// OpenBSD does not support these tests. Don't test these on ASan/TSan/MSan
+// configurations: only test the real allocator.
+// Windows only supports these tests with the allocator shim in place.
+#if !defined(OS_OPENBSD) && BUILDFLAG(USE_ALLOCATOR_SHIM) && \
+    !defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
+
+namespace {
+#if defined(OS_WIN)
+// Windows raises an exception rather than using LOG(FATAL) in order to make the
+// exit code unique to OOM.
+const char* kOomRegex = "";
+const int kExitCode = base::win::kOomExceptionCode;
+#else
+const char* kOomRegex = "Out of memory";
+const int kExitCode = 1;
+#endif
+}  // namespace
+
+class OutOfMemoryTest : public testing::Test {
+ public:
+  OutOfMemoryTest()
+      : value_(nullptr),
+        // Make test size as large as possible minus a few pages so
+        // that alignment or other rounding doesn't make it wrap.
+        test_size_(std::numeric_limits<std::size_t>::max() - 12 * 1024),
+        // A test size that is > 2Gb and will cause the allocators to reject
+        // the allocation due to security restrictions. See crbug.com/169327.
+        insecure_test_size_(std::numeric_limits<int>::max()),
+        signed_test_size_(std::numeric_limits<ssize_t>::max()) {}
+
+ protected:
+  void* value_;
+  size_t test_size_;
+  size_t insecure_test_size_;
+  ssize_t signed_test_size_;
+};
+
+class OutOfMemoryDeathTest : public OutOfMemoryTest {
+ public:
+  void SetUpInDeathAssert() {
+#if defined(OS_MACOSX) && BUILDFLAG(USE_ALLOCATOR_SHIM)
+    base::allocator::InitializeAllocatorShim();
+#endif
+
+    // Must call EnableTerminationOnOutOfMemory() because that is called from
+    // chrome's main function and therefore hasn't been called yet.
+    // Since this call may result in another thread being created and death
+    // tests shouldn't be started in a multithread environment, this call
+    // should be done inside of the ASSERT_DEATH.
+    base::EnableTerminationOnOutOfMemory();
+  }
+
+#if defined(OS_MACOSX)
+  void TearDown() override {
+    base::allocator::UninterceptMallocZonesForTesting();
+  }
+#endif
+};
+
+TEST_F(OutOfMemoryDeathTest, New) {
+  ASSERT_EXIT({
+      SetUpInDeathAssert();
+      value_ = operator new(test_size_);
+    }, testing::ExitedWithCode(kExitCode), kOomRegex);
+}
+
+TEST_F(OutOfMemoryDeathTest, NewArray) {
+  ASSERT_EXIT({
+      SetUpInDeathAssert();
+      value_ = new char[test_size_];
+    }, testing::ExitedWithCode(kExitCode), kOomRegex);
+}
+
+TEST_F(OutOfMemoryDeathTest, Malloc) {
+  ASSERT_EXIT({
+      SetUpInDeathAssert();
+      value_ = malloc(test_size_);
+    }, testing::ExitedWithCode(kExitCode), kOomRegex);
+}
+
+TEST_F(OutOfMemoryDeathTest, Realloc) {
+  ASSERT_EXIT({
+      SetUpInDeathAssert();
+      value_ = realloc(nullptr, test_size_);
+    }, testing::ExitedWithCode(kExitCode), kOomRegex);
+}
+
+TEST_F(OutOfMemoryDeathTest, Calloc) {
+  ASSERT_EXIT({
+      SetUpInDeathAssert();
+      value_ = calloc(1024, test_size_ / 1024L);
+    }, testing::ExitedWithCode(kExitCode), kOomRegex);
+}
+
+TEST_F(OutOfMemoryDeathTest, AlignedAlloc) {
+  ASSERT_EXIT({
+      SetUpInDeathAssert();
+      value_ = base::AlignedAlloc(test_size_, 8);
+    }, testing::ExitedWithCode(kExitCode), kOomRegex);
+}
+
+// POSIX does not define an aligned realloc function.
+#if defined(OS_WIN)
+TEST_F(OutOfMemoryDeathTest, AlignedRealloc) {
+  ASSERT_EXIT({
+      SetUpInDeathAssert();
+      value_ = _aligned_realloc(NULL, test_size_, 8);
+    }, testing::ExitedWithCode(kExitCode), kOomRegex);
+}
+
+namespace {
+
+constexpr uint32_t kUnhandledExceptionExitCode = 0xBADA55;
+
+// This unhandled exception filter exits the process with an exit code distinct
+// from the exception code. This is to verify that the out of memory new handler
+// causes an unhandled exception.
+LONG WINAPI ExitingUnhandledExceptionFilter(EXCEPTION_POINTERS* ExceptionInfo) {
+  _exit(kUnhandledExceptionExitCode);
+}
+
+}  // namespace
+
+TEST_F(OutOfMemoryDeathTest, NewHandlerGeneratesUnhandledException) {
+  ASSERT_EXIT(
+      {
+        SetUpInDeathAssert();
+        SetUnhandledExceptionFilter(&ExitingUnhandledExceptionFilter);
+        value_ = new char[test_size_];
+      },
+      testing::ExitedWithCode(kUnhandledExceptionExitCode), kOomRegex);
+}
+#endif  // defined(OS_WIN)
+
+// OS X and Android have no 2Gb allocation limit.
+// See https://crbug.com/169327.
+#if !defined(OS_MACOSX) && !defined(OS_ANDROID)
+TEST_F(OutOfMemoryDeathTest, SecurityNew) {
+  ASSERT_EXIT({
+      SetUpInDeathAssert();
+      value_ = operator new(insecure_test_size_);
+    }, testing::ExitedWithCode(kExitCode), kOomRegex);
+}
+
+TEST_F(OutOfMemoryDeathTest, SecurityNewArray) {
+  ASSERT_EXIT({
+      SetUpInDeathAssert();
+      value_ = new char[insecure_test_size_];
+    }, testing::ExitedWithCode(kExitCode), kOomRegex);
+}
+
+TEST_F(OutOfMemoryDeathTest, SecurityMalloc) {
+  ASSERT_EXIT({
+      SetUpInDeathAssert();
+      value_ = malloc(insecure_test_size_);
+    }, testing::ExitedWithCode(kExitCode), kOomRegex);
+}
+
+TEST_F(OutOfMemoryDeathTest, SecurityRealloc) {
+  ASSERT_EXIT({
+      SetUpInDeathAssert();
+      value_ = realloc(nullptr, insecure_test_size_);
+    }, testing::ExitedWithCode(kExitCode), kOomRegex);
+}
+
+TEST_F(OutOfMemoryDeathTest, SecurityCalloc) {
+  ASSERT_EXIT({
+      SetUpInDeathAssert();
+      value_ = calloc(1024, insecure_test_size_ / 1024L);
+    }, testing::ExitedWithCode(kExitCode), kOomRegex);
+}
+
+TEST_F(OutOfMemoryDeathTest, SecurityAlignedAlloc) {
+  ASSERT_EXIT({
+      SetUpInDeathAssert();
+      value_ = base::AlignedAlloc(insecure_test_size_, 8);
+    }, testing::ExitedWithCode(kExitCode), kOomRegex);
+}
+
+// POSIX does not define an aligned realloc function.
+#if defined(OS_WIN)
+TEST_F(OutOfMemoryDeathTest, SecurityAlignedRealloc) {
+  ASSERT_EXIT({
+      SetUpInDeathAssert();
+      value_ = _aligned_realloc(NULL, insecure_test_size_, 8);
+    }, testing::ExitedWithCode(kExitCode), kOomRegex);
+}
+#endif  // defined(OS_WIN)
+#endif  // !defined(OS_MACOSX) && !defined(OS_ANDROID)
+
+#if defined(OS_LINUX)
+
+TEST_F(OutOfMemoryDeathTest, Valloc) {
+  ASSERT_DEATH({
+      SetUpInDeathAssert();
+      value_ = valloc(test_size_);
+    }, kOomRegex);
+}
+
+TEST_F(OutOfMemoryDeathTest, SecurityValloc) {
+  ASSERT_DEATH({
+      SetUpInDeathAssert();
+      value_ = valloc(insecure_test_size_);
+    }, kOomRegex);
+}
+
+#if PVALLOC_AVAILABLE == 1
+TEST_F(OutOfMemoryDeathTest, Pvalloc) {
+  ASSERT_DEATH({
+      SetUpInDeathAssert();
+      value_ = pvalloc(test_size_);
+    }, kOomRegex);
+}
+
+TEST_F(OutOfMemoryDeathTest, SecurityPvalloc) {
+  ASSERT_DEATH({
+      SetUpInDeathAssert();
+      value_ = pvalloc(insecure_test_size_);
+    }, kOomRegex);
+}
+#endif  // PVALLOC_AVAILABLE == 1
+
+TEST_F(OutOfMemoryDeathTest, Memalign) {
+  ASSERT_DEATH({
+      SetUpInDeathAssert();
+      value_ = memalign(4, test_size_);
+    }, kOomRegex);
+}
+
+TEST_F(OutOfMemoryDeathTest, ViaSharedLibraries) {
+  // This tests that the run-time symbol resolution is overriding malloc for
+  // shared libraries as well as for our code.
+  ASSERT_DEATH({
+    SetUpInDeathAssert();
+    value_ = MallocWrapper(test_size_);
+  }, kOomRegex);
+}
+#endif  // OS_LINUX
+
+// Android doesn't implement posix_memalign().
+#if defined(OS_POSIX) && !defined(OS_ANDROID)
+TEST_F(OutOfMemoryDeathTest, Posix_memalign) {
+  // Grab the return value of posix_memalign to silence a compiler warning
+  // about unused return values. We don't actually care about the return
+  // value, since we're asserting death.
+  ASSERT_DEATH({
+      SetUpInDeathAssert();
+      EXPECT_EQ(ENOMEM, posix_memalign(&value_, 8, test_size_));
+    }, kOomRegex);
+}
+#endif  // defined(OS_POSIX) && !defined(OS_ANDROID)
+
+#if defined(OS_MACOSX)
+
+// Purgeable zone tests
+
+TEST_F(OutOfMemoryDeathTest, MallocPurgeable) {
+  malloc_zone_t* zone = malloc_default_purgeable_zone();
+  ASSERT_DEATH({
+      SetUpInDeathAssert();
+      value_ = malloc_zone_malloc(zone, test_size_);
+    }, kOomRegex);
+}
+
+TEST_F(OutOfMemoryDeathTest, ReallocPurgeable) {
+  malloc_zone_t* zone = malloc_default_purgeable_zone();
+  ASSERT_DEATH({
+      SetUpInDeathAssert();
+      value_ = malloc_zone_realloc(zone, NULL, test_size_);
+    }, kOomRegex);
+}
+
+TEST_F(OutOfMemoryDeathTest, CallocPurgeable) {
+  malloc_zone_t* zone = malloc_default_purgeable_zone();
+  ASSERT_DEATH({
+      SetUpInDeathAssert();
+      value_ = malloc_zone_calloc(zone, 1024, test_size_ / 1024L);
+    }, kOomRegex);
+}
+
+TEST_F(OutOfMemoryDeathTest, VallocPurgeable) {
+  malloc_zone_t* zone = malloc_default_purgeable_zone();
+  ASSERT_DEATH({
+      SetUpInDeathAssert();
+      value_ = malloc_zone_valloc(zone, test_size_);
+    }, kOomRegex);
+}
+
+TEST_F(OutOfMemoryDeathTest, PosixMemalignPurgeable) {
+  malloc_zone_t* zone = malloc_default_purgeable_zone();
+  ASSERT_DEATH({
+      SetUpInDeathAssert();
+      value_ = malloc_zone_memalign(zone, 8, test_size_);
+    }, kOomRegex);
+}
+
+// Since these allocation functions take a signed size, it's possible that
+// calling them just once won't be enough to exhaust memory. In the 32-bit
+// environment, it's likely that these allocation attempts will fail because
+// not enough contiguous address space is available. In the 64-bit environment,
+// it's likely that they'll fail because they would require a preposterous
+// amount of (virtual) memory.
+
+TEST_F(OutOfMemoryDeathTest, CFAllocatorSystemDefault) {
+  ASSERT_DEATH({
+      SetUpInDeathAssert();
+      while ((value_ =
+              base::AllocateViaCFAllocatorSystemDefault(signed_test_size_))) {}
+    }, kOomRegex);
+}
+
+TEST_F(OutOfMemoryDeathTest, CFAllocatorMalloc) {
+  ASSERT_DEATH({
+      SetUpInDeathAssert();
+      while ((value_ =
+              base::AllocateViaCFAllocatorMalloc(signed_test_size_))) {}
+    }, kOomRegex);
+}
+
+TEST_F(OutOfMemoryDeathTest, CFAllocatorMallocZone) {
+  ASSERT_DEATH({
+      SetUpInDeathAssert();
+      while ((value_ =
+              base::AllocateViaCFAllocatorMallocZone(signed_test_size_))) {}
+    }, kOomRegex);
+}
+
+#if !defined(ARCH_CPU_64_BITS)
+
+// See process_util_unittest_mac.mm for an explanation of why this test isn't
+// run in the 64-bit environment.
+
+TEST_F(OutOfMemoryDeathTest, PsychoticallyBigObjCObject) {
+  ASSERT_DEATH({
+      SetUpInDeathAssert();
+      while ((value_ = base::AllocatePsychoticallyBigObjCObject())) {}
+    }, kOomRegex);
+}
+
+#endif  // !ARCH_CPU_64_BITS
+#endif  // OS_MACOSX
+
+class OutOfMemoryHandledTest : public OutOfMemoryTest {
+ public:
+  static const size_t kSafeMallocSize = 512;
+  static const size_t kSafeCallocSize = 128;
+  static const size_t kSafeCallocItems = 4;
+
+  void SetUp() override {
+    OutOfMemoryTest::SetUp();
+
+    // We enable termination on OOM - just as Chrome does at early
+    // initialization - and test that UncheckedMalloc and  UncheckedCalloc
+    // properly by-pass this in order to allow the caller to handle OOM.
+    base::EnableTerminationOnOutOfMemory();
+  }
+
+  void TearDown() override {
+#if defined(OS_MACOSX)
+    base::allocator::UninterceptMallocZonesForTesting();
+#endif
+  }
+};
+
+#if defined(OS_WIN)
+
+namespace {
+
+DWORD HandleOutOfMemoryException(EXCEPTION_POINTERS* exception_ptrs,
+                                 size_t expected_size) {
+  EXPECT_EQ(base::win::kOomExceptionCode,
+            exception_ptrs->ExceptionRecord->ExceptionCode);
+  EXPECT_LE(1U, exception_ptrs->ExceptionRecord->NumberParameters);
+  EXPECT_EQ(expected_size,
+            exception_ptrs->ExceptionRecord->ExceptionInformation[0]);
+  return EXCEPTION_EXECUTE_HANDLER;
+}
+
+}  // namespace
+
+TEST_F(OutOfMemoryTest, TerminateBecauseOutOfMemoryReportsAllocSize) {
+// On Windows, TerminateBecauseOutOfMemory reports the attempted allocation
+// size in the exception raised.
+#if defined(ARCH_CPU_64_BITS)
+  // Test with a size larger than 32 bits on 64 bit machines.
+  const size_t kAttemptedAllocationSize = 0xBADA55F00DULL;
+#else
+  const size_t kAttemptedAllocationSize = 0xBADA55;
+#endif
+
+  __try {
+    base::TerminateBecauseOutOfMemory(kAttemptedAllocationSize);
+  } __except (HandleOutOfMemoryException(GetExceptionInformation(),
+                                         kAttemptedAllocationSize)) {
+  }
+}
+#endif  // OS_WIN
+
+// TODO(b.kelemen): make UncheckedMalloc and UncheckedCalloc work
+// on Windows as well.
+TEST_F(OutOfMemoryHandledTest, UncheckedMalloc) {
+  EXPECT_TRUE(base::UncheckedMalloc(kSafeMallocSize, &value_));
+  EXPECT_TRUE(value_ != nullptr);
+  free(value_);
+
+  EXPECT_FALSE(base::UncheckedMalloc(test_size_, &value_));
+  EXPECT_TRUE(value_ == nullptr);
+}
+
+TEST_F(OutOfMemoryHandledTest, UncheckedCalloc) {
+  EXPECT_TRUE(base::UncheckedCalloc(1, kSafeMallocSize, &value_));
+  EXPECT_TRUE(value_ != nullptr);
+  const char* bytes = static_cast<const char*>(value_);
+  for (size_t i = 0; i < kSafeMallocSize; ++i)
+    EXPECT_EQ(0, bytes[i]);
+  free(value_);
+
+  EXPECT_TRUE(
+      base::UncheckedCalloc(kSafeCallocItems, kSafeCallocSize, &value_));
+  EXPECT_TRUE(value_ != nullptr);
+  bytes = static_cast<const char*>(value_);
+  for (size_t i = 0; i < (kSafeCallocItems * kSafeCallocSize); ++i)
+    EXPECT_EQ(0, bytes[i]);
+  free(value_);
+
+  EXPECT_FALSE(base::UncheckedCalloc(1, test_size_, &value_));
+  EXPECT_TRUE(value_ == nullptr);
+}
+#endif  // !defined(OS_OPENBSD) && BUILDFLAG(ENABLE_WIN_ALLOCATOR_SHIM_TESTS) &&
+        // !defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
diff --git a/base/process/process_linux.cc b/base/process/process_linux.cc
new file mode 100644
index 0000000..faf39af
--- /dev/null
+++ b/base/process/process_linux.cc
@@ -0,0 +1,201 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/process/process.h"
+
+#include <errno.h>
+#include <sys/resource.h>
+
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/strings/stringprintf.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/thread_restrictions.h"
+#include "build/build_config.h"
+
+// Not defined on AIX by default.
+#if defined(OS_AIX)
+#define RLIMIT_NICE 20
+#endif
+
+namespace base {
+
+namespace {
+
+const int kForegroundPriority = 0;
+
+#if defined(OS_CHROMEOS)
+// We are more aggressive in our lowering of background process priority
+// for chromeos as we have much more control over other processes running
+// on the machine.
+//
+// TODO(davemoore) Refactor this by adding support for higher levels to set
+// the foregrounding / backgrounding process so we don't have to keep
+// chrome / chromeos specific logic here.
+const int kBackgroundPriority = 19;
+const char kControlPath[] = "/sys/fs/cgroup/cpu%s/cgroup.procs";
+const char kForeground[] = "/chrome_renderers/foreground";
+const char kBackground[] = "/chrome_renderers/background";
+const char kProcPath[] = "/proc/%d/cgroup";
+
+struct CGroups {
+  // Check for cgroups files. ChromeOS supports these by default. It creates
+  // a cgroup mount in /sys/fs/cgroup and then configures two cpu task groups,
+  // one contains at most a single foreground renderer and the other contains
+  // all background renderers. This allows us to limit the impact of background
+  // renderers on foreground ones to a greater level than simple renicing.
+  bool enabled;
+  base::FilePath foreground_file;
+  base::FilePath background_file;
+
+  CGroups() {
+    foreground_file =
+        base::FilePath(base::StringPrintf(kControlPath, kForeground));
+    background_file =
+        base::FilePath(base::StringPrintf(kControlPath, kBackground));
+    base::FileSystemType foreground_type;
+    base::FileSystemType background_type;
+    enabled =
+        base::GetFileSystemType(foreground_file, &foreground_type) &&
+        base::GetFileSystemType(background_file, &background_type) &&
+        foreground_type == FILE_SYSTEM_CGROUP &&
+        background_type == FILE_SYSTEM_CGROUP;
+  }
+
+  static CGroups& Get() {
+    static auto& groups = *new CGroups;
+    return groups;
+  }
+};
+#else
+const int kBackgroundPriority = 5;
+#endif  // defined(OS_CHROMEOS)
+
+bool CanReraisePriority() {
+  // We won't be able to raise the priority if we don't have the right rlimit.
+  // The limit may be adjusted in /etc/security/limits.conf for PAM systems.
+  struct rlimit rlim;
+  return (getrlimit(RLIMIT_NICE, &rlim) == 0) &&
+         (20 - kForegroundPriority) <= static_cast<int>(rlim.rlim_cur);
+}
+
+}  // namespace
+
+// static
+bool Process::CanBackgroundProcesses() {
+#if defined(OS_CHROMEOS)
+  if (CGroups::Get().enabled)
+    return true;
+#endif  // defined(OS_CHROMEOS)
+
+  static const bool can_reraise_priority = CanReraisePriority();
+  return can_reraise_priority;
+}
+
+bool Process::IsProcessBackgrounded() const {
+  DCHECK(IsValid());
+
+#if defined(OS_CHROMEOS)
+  if (CGroups::Get().enabled) {
+    // Used to allow reading the process priority from proc on thread launch.
+    base::ThreadRestrictions::ScopedAllowIO allow_io;
+    std::string proc;
+    if (base::ReadFileToString(
+            base::FilePath(StringPrintf(kProcPath, process_)), &proc)) {
+      return IsProcessBackgroundedCGroup(proc);
+    }
+    return false;
+  }
+#endif  // defined(OS_CHROMEOS)
+
+  return GetPriority() == kBackgroundPriority;
+}
+
+bool Process::SetProcessBackgrounded(bool background) {
+  DCHECK(IsValid());
+
+#if defined(OS_CHROMEOS)
+  if (CGroups::Get().enabled) {
+    std::string pid = IntToString(process_);
+    const base::FilePath file = background ? CGroups::Get().background_file
+                                           : CGroups::Get().foreground_file;
+    return base::WriteFile(file, pid.c_str(), pid.size()) > 0;
+  }
+#endif  // defined(OS_CHROMEOS)
+
+  if (!CanBackgroundProcesses())
+    return false;
+
+  int priority = background ? kBackgroundPriority : kForegroundPriority;
+  int result = setpriority(PRIO_PROCESS, process_, priority);
+  DPCHECK(result == 0);
+  return result == 0;
+}
+
+#if defined(OS_CHROMEOS)
+bool IsProcessBackgroundedCGroup(const StringPiece& cgroup_contents) {
+  // The process can be part of multiple control groups, and for each cgroup
+  // hierarchy there's an entry in the file. We look for a control group
+  // named "/chrome_renderers/background" to determine if the process is
+  // backgrounded. crbug.com/548818.
+  std::vector<StringPiece> lines = SplitStringPiece(
+      cgroup_contents, "\n", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
+  for (const auto& line : lines) {
+    std::vector<StringPiece> fields =
+        SplitStringPiece(line, ":", TRIM_WHITESPACE, SPLIT_WANT_ALL);
+    if (fields.size() != 3U) {
+      NOTREACHED();
+      continue;
+    }
+    if (fields[2] == kBackground)
+      return true;
+  }
+
+  return false;
+}
+#endif  // defined(OS_CHROMEOS)
+
+#if defined(OS_CHROMEOS)
+// Reads /proc/<pid>/status and returns the PID in its PID namespace.
+// If the process is not in a PID namespace or /proc/<pid>/status does not
+// report NSpid, kNullProcessId is returned.
+ProcessId Process::GetPidInNamespace() const {
+  std::string status;
+  {
+    // Synchronously reading files in /proc does not hit the disk.
+    ThreadRestrictions::ScopedAllowIO allow_io;
+    FilePath status_file =
+        FilePath("/proc").Append(IntToString(process_)).Append("status");
+    if (!ReadFileToString(status_file, &status)) {
+      return kNullProcessId;
+    }
+  }
+
+  StringPairs pairs;
+  SplitStringIntoKeyValuePairs(status, ':', '\n', &pairs);
+  for (const auto& pair : pairs) {
+    const std::string& key = pair.first;
+    const std::string& value_str = pair.second;
+    if (key == "NSpid") {
+      std::vector<StringPiece> split_value_str = SplitStringPiece(
+          value_str, "\t", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
+      if (split_value_str.size() <= 1) {
+        return kNullProcessId;
+      }
+      int value;
+      // The last value in the list is the PID in the namespace.
+      if (!StringToInt(split_value_str.back(), &value)) {
+        NOTREACHED();
+        return kNullProcessId;
+      }
+      return value;
+    }
+  }
+  return kNullProcessId;
+}
+#endif  // defined(OS_CHROMEOS)
+
+}  // namespace base
diff --git a/base/process/process_unittest.cc b/base/process/process_unittest.cc
new file mode 100644
index 0000000..219944d
--- /dev/null
+++ b/base/process/process_unittest.cc
@@ -0,0 +1,330 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/process/process.h"
+
+#include <utility>
+
+#include "base/at_exit.h"
+#include "base/process/kill.h"
+#include "base/test/multiprocess_test.h"
+#include "base/test/test_timeouts.h"
+#include "base/threading/platform_thread.h"
+#include "base/threading/thread_local.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/multiprocess_func_list.h"
+
+namespace {
+
+#if defined(OS_WIN)
+const int kExpectedStillRunningExitCode = 0x102;
+#else
+const int kExpectedStillRunningExitCode = 0;
+#endif
+
+#if defined(OS_MACOSX)
+// Fake port provider that returns the calling process's
+// task port, ignoring its argument.
+class FakePortProvider : public base::PortProvider {
+  mach_port_t TaskForPid(base::ProcessHandle process) const override {
+    return mach_task_self();
+  }
+};
+#endif
+
+}  // namespace
+
+namespace base {
+
+class ProcessTest : public MultiProcessTest {
+};
+
+TEST_F(ProcessTest, Create) {
+  Process process(SpawnChild("SimpleChildProcess"));
+  ASSERT_TRUE(process.IsValid());
+  ASSERT_FALSE(process.is_current());
+  EXPECT_NE(process.Pid(), kNullProcessId);
+  process.Close();
+  ASSERT_FALSE(process.IsValid());
+}
+
+TEST_F(ProcessTest, CreateCurrent) {
+  Process process = Process::Current();
+  ASSERT_TRUE(process.IsValid());
+  ASSERT_TRUE(process.is_current());
+  EXPECT_NE(process.Pid(), kNullProcessId);
+  process.Close();
+  ASSERT_FALSE(process.IsValid());
+}
+
+TEST_F(ProcessTest, Move) {
+  Process process1(SpawnChild("SimpleChildProcess"));
+  EXPECT_TRUE(process1.IsValid());
+
+  Process process2;
+  EXPECT_FALSE(process2.IsValid());
+
+  process2 = std::move(process1);
+  EXPECT_TRUE(process2.IsValid());
+  EXPECT_FALSE(process1.IsValid());
+  EXPECT_FALSE(process2.is_current());
+
+  Process process3 = Process::Current();
+  process2 = std::move(process3);
+  EXPECT_TRUE(process2.is_current());
+  EXPECT_TRUE(process2.IsValid());
+  EXPECT_FALSE(process3.IsValid());
+}
+
+TEST_F(ProcessTest, Duplicate) {
+  Process process1(SpawnChild("SimpleChildProcess"));
+  ASSERT_TRUE(process1.IsValid());
+
+  Process process2 = process1.Duplicate();
+  ASSERT_TRUE(process1.IsValid());
+  ASSERT_TRUE(process2.IsValid());
+  EXPECT_EQ(process1.Pid(), process2.Pid());
+  EXPECT_FALSE(process1.is_current());
+  EXPECT_FALSE(process2.is_current());
+
+  process1.Close();
+  ASSERT_TRUE(process2.IsValid());
+}
+
+TEST_F(ProcessTest, DuplicateCurrent) {
+  Process process1 = Process::Current();
+  ASSERT_TRUE(process1.IsValid());
+
+  Process process2 = process1.Duplicate();
+  ASSERT_TRUE(process1.IsValid());
+  ASSERT_TRUE(process2.IsValid());
+  EXPECT_EQ(process1.Pid(), process2.Pid());
+  EXPECT_TRUE(process1.is_current());
+  EXPECT_TRUE(process2.is_current());
+
+  process1.Close();
+  ASSERT_TRUE(process2.IsValid());
+}
+
+TEST_F(ProcessTest, DeprecatedGetProcessFromHandle) {
+  Process process1(SpawnChild("SimpleChildProcess"));
+  ASSERT_TRUE(process1.IsValid());
+
+  Process process2 = Process::DeprecatedGetProcessFromHandle(process1.Handle());
+  ASSERT_TRUE(process1.IsValid());
+  ASSERT_TRUE(process2.IsValid());
+  EXPECT_EQ(process1.Pid(), process2.Pid());
+  EXPECT_FALSE(process1.is_current());
+  EXPECT_FALSE(process2.is_current());
+
+  process1.Close();
+  ASSERT_TRUE(process2.IsValid());
+}
+
+MULTIPROCESS_TEST_MAIN(SleepyChildProcess) {
+  PlatformThread::Sleep(TestTimeouts::action_max_timeout());
+  return 0;
+}
+
+TEST_F(ProcessTest, Terminate) {
+  Process process(SpawnChild("SleepyChildProcess"));
+  ASSERT_TRUE(process.IsValid());
+
+  const int kDummyExitCode = 42;
+  int exit_code = kDummyExitCode;
+  EXPECT_EQ(TERMINATION_STATUS_STILL_RUNNING,
+            GetTerminationStatus(process.Handle(), &exit_code));
+  EXPECT_EQ(kExpectedStillRunningExitCode, exit_code);
+
+  exit_code = kDummyExitCode;
+  int kExpectedExitCode = 250;
+  process.Terminate(kExpectedExitCode, false);
+  process.WaitForExitWithTimeout(TestTimeouts::action_max_timeout(),
+                                 &exit_code);
+
+  EXPECT_NE(TERMINATION_STATUS_STILL_RUNNING,
+            GetTerminationStatus(process.Handle(), &exit_code));
+#if !defined(OS_POSIX) && !defined(OS_FUCHSIA)
+  // The POSIX & Fuchsia implementations actually ignore the exit_code.
+  EXPECT_EQ(kExpectedExitCode, exit_code);
+#endif
+}
+
+void AtExitHandler(void*) {
+  // At-exit handler should not be called at
+  // Process::TerminateCurrentProcessImmediately.
+  DCHECK(false);
+}
+
+class ThreadLocalObject {
+  ~ThreadLocalObject() {
+    // Thread-local storage should not be destructed at
+    // Process::TerminateCurrentProcessImmediately.
+    DCHECK(false);
+  }
+};
+
+MULTIPROCESS_TEST_MAIN(TerminateCurrentProcessImmediatelyWithCode0) {
+  base::ThreadLocalPointer<ThreadLocalObject> object;
+  base::AtExitManager::RegisterCallback(&AtExitHandler, nullptr);
+  Process::TerminateCurrentProcessImmediately(0);
+}
+
+TEST_F(ProcessTest, TerminateCurrentProcessImmediatelyWithZeroExitCode) {
+  Process process(SpawnChild("TerminateCurrentProcessImmediatelyWithCode0"));
+  ASSERT_TRUE(process.IsValid());
+  int exit_code = 42;
+  ASSERT_TRUE(process.WaitForExitWithTimeout(TestTimeouts::action_max_timeout(),
+                                             &exit_code));
+  EXPECT_EQ(0, exit_code);
+}
+
+MULTIPROCESS_TEST_MAIN(TerminateCurrentProcessImmediatelyWithCode250) {
+  Process::TerminateCurrentProcessImmediately(250);
+}
+
+TEST_F(ProcessTest, TerminateCurrentProcessImmediatelyWithNonZeroExitCode) {
+  Process process(SpawnChild("TerminateCurrentProcessImmediatelyWithCode250"));
+  ASSERT_TRUE(process.IsValid());
+  int exit_code = 42;
+  ASSERT_TRUE(process.WaitForExitWithTimeout(TestTimeouts::action_max_timeout(),
+                                             &exit_code));
+  EXPECT_EQ(250, exit_code);
+}
+
+MULTIPROCESS_TEST_MAIN(FastSleepyChildProcess) {
+  PlatformThread::Sleep(TestTimeouts::tiny_timeout() * 10);
+  return 0;
+}
+
+TEST_F(ProcessTest, WaitForExit) {
+  Process process(SpawnChild("FastSleepyChildProcess"));
+  ASSERT_TRUE(process.IsValid());
+
+  const int kDummyExitCode = 42;
+  int exit_code = kDummyExitCode;
+  EXPECT_TRUE(process.WaitForExit(&exit_code));
+  EXPECT_EQ(0, exit_code);
+}
+
+TEST_F(ProcessTest, WaitForExitWithTimeout) {
+  Process process(SpawnChild("SleepyChildProcess"));
+  ASSERT_TRUE(process.IsValid());
+
+  const int kDummyExitCode = 42;
+  int exit_code = kDummyExitCode;
+  TimeDelta timeout = TestTimeouts::tiny_timeout();
+  EXPECT_FALSE(process.WaitForExitWithTimeout(timeout, &exit_code));
+  EXPECT_EQ(kDummyExitCode, exit_code);
+
+  process.Terminate(kDummyExitCode, false);
+}
+
+// Ensure that the priority of a process is restored correctly after
+// backgrounding and restoring.
+// Note: a platform may not be willing or able to lower the priority of
+// a process. The calls to SetProcessBackground should be noops then.
+TEST_F(ProcessTest, SetProcessBackgrounded) {
+  if (!Process::CanBackgroundProcesses())
+    return;
+  Process process(SpawnChild("SimpleChildProcess"));
+  int old_priority = process.GetPriority();
+#if defined(OS_WIN)
+  EXPECT_TRUE(process.SetProcessBackgrounded(true));
+  EXPECT_TRUE(process.IsProcessBackgrounded());
+  EXPECT_TRUE(process.SetProcessBackgrounded(false));
+  EXPECT_FALSE(process.IsProcessBackgrounded());
+#elif defined(OS_MACOSX)
+  // On the Mac, backgrounding a process requires a port to that process.
+  // In the browser it's available through the MachBroker class, which is not
+  // part of base. Additionally, there is an indefinite amount of time between
+  // spawning a process and receiving its port. Because this test just checks
+  // the ability to background/foreground a process, we can use the current
+  // process's port instead.
+  FakePortProvider provider;
+  EXPECT_TRUE(process.SetProcessBackgrounded(&provider, true));
+  EXPECT_TRUE(process.IsProcessBackgrounded(&provider));
+  EXPECT_TRUE(process.SetProcessBackgrounded(&provider, false));
+  EXPECT_FALSE(process.IsProcessBackgrounded(&provider));
+
+#else
+  process.SetProcessBackgrounded(true);
+  process.SetProcessBackgrounded(false);
+#endif
+  int new_priority = process.GetPriority();
+  EXPECT_EQ(old_priority, new_priority);
+}
+
+// Same as SetProcessBackgrounded but to this very process. It uses
+// a different code path at least for Windows.
+TEST_F(ProcessTest, SetProcessBackgroundedSelf) {
+  if (!Process::CanBackgroundProcesses())
+    return;
+  Process process = Process::Current();
+  int old_priority = process.GetPriority();
+#if defined(OS_WIN)
+  EXPECT_TRUE(process.SetProcessBackgrounded(true));
+  EXPECT_TRUE(process.IsProcessBackgrounded());
+  EXPECT_TRUE(process.SetProcessBackgrounded(false));
+  EXPECT_FALSE(process.IsProcessBackgrounded());
+#elif defined(OS_MACOSX)
+  FakePortProvider provider;
+  EXPECT_TRUE(process.SetProcessBackgrounded(&provider, true));
+  EXPECT_TRUE(process.IsProcessBackgrounded(&provider));
+  EXPECT_TRUE(process.SetProcessBackgrounded(&provider, false));
+  EXPECT_FALSE(process.IsProcessBackgrounded(&provider));
+#else
+  process.SetProcessBackgrounded(true);
+  process.SetProcessBackgrounded(false);
+#endif
+  int new_priority = process.GetPriority();
+  EXPECT_EQ(old_priority, new_priority);
+}
+
+// Consumers can use WaitForExitWithTimeout(base::TimeDelta(), nullptr) to check
+// whether the process is still running. This may not be safe because of the
+// potential reusing of the process id. So we won't export Process::IsRunning()
+// on all platforms. But for the controllable scenario in the test cases, the
+// behavior should be guaranteed.
+TEST_F(ProcessTest, CurrentProcessIsRunning) {
+  EXPECT_FALSE(Process::Current().WaitForExitWithTimeout(
+      base::TimeDelta(), nullptr));
+}
+
+#if defined(OS_MACOSX)
+// On Mac OSX, we can detect whether a non-child process is running.
+TEST_F(ProcessTest, PredefinedProcessIsRunning) {
+  // Process 1 is the /sbin/launchd, it should be always running.
+  EXPECT_FALSE(Process::Open(1).WaitForExitWithTimeout(
+      base::TimeDelta(), nullptr));
+}
+#endif
+
+TEST_F(ProcessTest, ChildProcessIsRunning) {
+  Process process(SpawnChild("SleepyChildProcess"));
+  EXPECT_FALSE(process.WaitForExitWithTimeout(
+      base::TimeDelta(), nullptr));
+  process.Terminate(0, true);
+  EXPECT_TRUE(process.WaitForExitWithTimeout(
+      base::TimeDelta(), nullptr));
+}
+
+#if defined(OS_CHROMEOS)
+
+// Tests that the function IsProcessBackgroundedCGroup() can parse the contents
+// of the /proc/<pid>/cgroup file successfully.
+TEST_F(ProcessTest, TestIsProcessBackgroundedCGroup) {
+  const char kNotBackgrounded[] = "5:cpuacct,cpu,cpuset:/daemons\n";
+  const char kBackgrounded[] =
+      "2:freezer:/chrome_renderers/to_be_frozen\n"
+      "1:cpu:/chrome_renderers/background\n";
+
+  EXPECT_FALSE(IsProcessBackgroundedCGroup(kNotBackgrounded));
+  EXPECT_TRUE(IsProcessBackgroundedCGroup(kBackgrounded));
+}
+
+#endif  // defined(OS_CHROMEOS)
+
+}  // namespace base
diff --git a/base/process/process_util_unittest.cc b/base/process/process_util_unittest.cc
new file mode 100644
index 0000000..4e788b7
--- /dev/null
+++ b/base/process/process_util_unittest.cc
@@ -0,0 +1,1357 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#define _CRT_SECURE_NO_WARNINGS
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <limits>
+
+#include "base/command_line.h"
+#include "base/debug/alias.h"
+#include "base/debug/stack_trace.h"
+#include "base/files/file_enumerator.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_file.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/path_service.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/process/kill.h"
+#include "base/process/launch.h"
+#include "base/process/memory.h"
+#include "base/process/process.h"
+#include "base/process/process_metrics.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/test/multiprocess_test.h"
+#include "base/test/scoped_task_environment.h"
+#include "base/test/test_timeouts.h"
+#include "base/threading/platform_thread.h"
+#include "base/threading/thread.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/multiprocess_func_list.h"
+
+#if defined(OS_LINUX)
+#include <malloc.h>
+#include <sched.h>
+#include <sys/syscall.h>
+#endif
+#if defined(OS_POSIX)
+#include <sys/resource.h>
+#endif
+#if defined(OS_POSIX)
+#include <dlfcn.h>
+#include <errno.h>
+#include <sched.h>
+#include <signal.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#endif
+#if defined(OS_POSIX) || defined(OS_FUCHSIA)
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#endif
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+#if defined(OS_MACOSX)
+#include <mach/vm_param.h>
+#include <malloc/malloc.h>
+#endif
+#if defined(OS_ANDROID)
+#include "third_party/lss/linux_syscall_support.h"
+#endif
+#if defined(OS_FUCHSIA)
+#include <lib/fdio/limits.h>
+#include <zircon/process.h>
+#include <zircon/processargs.h>
+#include <zircon/syscalls.h>
+#include "base/base_paths_fuchsia.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/fuchsia/file_utils.h"
+#include "base/fuchsia/fuchsia_logging.h"
+#endif
+
+namespace base {
+
+namespace {
+
+const char kSignalFileSlow[] = "SlowChildProcess.die";
+const char kSignalFileKill[] = "KilledChildProcess.die";
+const char kTestHelper[] = "test_child_process";
+
+#if defined(OS_POSIX)
+const char kSignalFileTerm[] = "TerminatedChildProcess.die";
+#endif
+
+#if defined(OS_FUCHSIA)
+const char kSignalFileClone[] = "ClonedTmpDir.die";
+const char kDataDirHasStaged[] = "DataDirHasStaged.die";
+const char kFooDirHasStaged[] = "FooDirHasStaged.die";
+const char kFooDirDoesNotHaveStaged[] = "FooDirDoesNotHaveStaged.die";
+#endif
+
+#if defined(OS_WIN)
+const int kExpectedStillRunningExitCode = 0x102;
+const int kExpectedKilledExitCode = 1;
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+const int kExpectedStillRunningExitCode = 0;
+#endif
+
+// Sleeps until file filename is created.
+void WaitToDie(const char* filename) {
+  FILE* fp;
+  do {
+    PlatformThread::Sleep(TimeDelta::FromMilliseconds(10));
+    fp = fopen(filename, "r");
+  } while (!fp);
+  fclose(fp);
+}
+
+// Signals children they should die now.
+void SignalChildren(const char* filename) {
+  FILE* fp = fopen(filename, "w");
+  fclose(fp);
+}
+
+// Using a pipe to the child to wait for an event was considered, but
+// there were cases in the past where pipes caused problems (other
+// libraries closing the fds, child deadlocking). This is a simple
+// case, so it's not worth the risk.  Using wait loops is discouraged
+// in most instances.
+TerminationStatus WaitForChildTermination(ProcessHandle handle,
+                                          int* exit_code) {
+  // Now we wait until the result is something other than STILL_RUNNING.
+  TerminationStatus status = TERMINATION_STATUS_STILL_RUNNING;
+  const TimeDelta kInterval = TimeDelta::FromMilliseconds(20);
+  TimeDelta waited;
+  do {
+    status = GetTerminationStatus(handle, exit_code);
+    PlatformThread::Sleep(kInterval);
+    waited += kInterval;
+  } while (status == TERMINATION_STATUS_STILL_RUNNING &&
+           waited < TestTimeouts::action_max_timeout());
+
+  return status;
+}
+
+}  // namespace
+
+const int kSuccess = 0;
+
+class ProcessUtilTest : public MultiProcessTest {
+ public:
+  void SetUp() override {
+    ASSERT_TRUE(PathService::Get(DIR_ASSETS, &test_helper_path_));
+    test_helper_path_ = test_helper_path_.AppendASCII(kTestHelper);
+  }
+
+#if defined(OS_POSIX) || defined(OS_FUCHSIA)
+  // Spawn a child process that counts how many file descriptors are open.
+  int CountOpenFDsInChild();
+#endif
+  // Converts the filename to a platform specific filepath.
+  // On Android files can not be created in arbitrary directories.
+  static std::string GetSignalFilePath(const char* filename);
+
+ protected:
+  base::FilePath test_helper_path_;
+};
+
+std::string ProcessUtilTest::GetSignalFilePath(const char* filename) {
+#if defined(OS_ANDROID) || defined(OS_FUCHSIA)
+  FilePath tmp_dir;
+  PathService::Get(DIR_TEMP, &tmp_dir);
+  tmp_dir = tmp_dir.Append(filename);
+  return tmp_dir.value();
+#else
+  return filename;
+#endif
+}
+
+MULTIPROCESS_TEST_MAIN(SimpleChildProcess) {
+  return kSuccess;
+}
+
+// TODO(viettrungluu): This should be in a "MultiProcessTestTest".
+TEST_F(ProcessUtilTest, SpawnChild) {
+  Process process = SpawnChild("SimpleChildProcess");
+  ASSERT_TRUE(process.IsValid());
+  int exit_code;
+  EXPECT_TRUE(process.WaitForExitWithTimeout(TestTimeouts::action_max_timeout(),
+                                             &exit_code));
+}
+
+MULTIPROCESS_TEST_MAIN(SlowChildProcess) {
+  WaitToDie(ProcessUtilTest::GetSignalFilePath(kSignalFileSlow).c_str());
+  return kSuccess;
+}
+
+TEST_F(ProcessUtilTest, KillSlowChild) {
+  const std::string signal_file =
+      ProcessUtilTest::GetSignalFilePath(kSignalFileSlow);
+  remove(signal_file.c_str());
+  Process process = SpawnChild("SlowChildProcess");
+  ASSERT_TRUE(process.IsValid());
+  SignalChildren(signal_file.c_str());
+  int exit_code;
+  EXPECT_TRUE(process.WaitForExitWithTimeout(TestTimeouts::action_max_timeout(),
+                                             &exit_code));
+  remove(signal_file.c_str());
+}
+
+// Times out on Linux and Win, flakes on other platforms, http://crbug.com/95058
+TEST_F(ProcessUtilTest, DISABLED_GetTerminationStatusExit) {
+  const std::string signal_file =
+      ProcessUtilTest::GetSignalFilePath(kSignalFileSlow);
+  remove(signal_file.c_str());
+  Process process = SpawnChild("SlowChildProcess");
+  ASSERT_TRUE(process.IsValid());
+
+  int exit_code = 42;
+  EXPECT_EQ(TERMINATION_STATUS_STILL_RUNNING,
+            GetTerminationStatus(process.Handle(), &exit_code));
+  EXPECT_EQ(kExpectedStillRunningExitCode, exit_code);
+
+  SignalChildren(signal_file.c_str());
+  exit_code = 42;
+  TerminationStatus status =
+      WaitForChildTermination(process.Handle(), &exit_code);
+  EXPECT_EQ(TERMINATION_STATUS_NORMAL_TERMINATION, status);
+  EXPECT_EQ(kSuccess, exit_code);
+  remove(signal_file.c_str());
+}
+
+#if defined(OS_FUCHSIA)
+
+MULTIPROCESS_TEST_MAIN(CheckDataDirHasStaged) {
+  if (!PathExists(base::FilePath("/data/staged"))) {
+    return 1;
+  }
+  WaitToDie(ProcessUtilTest::GetSignalFilePath(kDataDirHasStaged).c_str());
+  return kSuccess;
+}
+
+// Test transferred paths override cloned paths.
+TEST_F(ProcessUtilTest, HandleTransfersOverrideClones) {
+  const std::string signal_file =
+      ProcessUtilTest::GetSignalFilePath(kDataDirHasStaged);
+  remove(signal_file.c_str());
+
+  // Create a tempdir with "staged" as its contents.
+  ScopedTempDir tmpdir_with_staged;
+  ASSERT_TRUE(tmpdir_with_staged.CreateUniqueTempDir());
+  {
+    base::FilePath staged_file_path =
+        tmpdir_with_staged.GetPath().Append("staged");
+    base::File staged_file(staged_file_path,
+                           base::File::FLAG_CREATE | base::File::FLAG_WRITE);
+    ASSERT_TRUE(staged_file.created());
+    staged_file.Close();
+  }
+
+  base::LaunchOptions options;
+  options.spawn_flags = FDIO_SPAWN_CLONE_STDIO;
+
+  // Attach the tempdir to "data", but also try to duplicate the existing "data"
+  // directory.
+  options.paths_to_clone.push_back(base::FilePath("/data"));
+  options.paths_to_clone.push_back(base::FilePath("/tmp"));
+  options.paths_to_transfer.push_back(
+      {FilePath("/data"),
+       fuchsia::GetHandleFromFile(
+           base::File(base::FilePath(tmpdir_with_staged.GetPath()),
+                      base::File::FLAG_OPEN | base::File::FLAG_READ))
+           .release()});
+
+  // Verify from that "/data/staged" exists from the child process' perspective.
+  Process process(SpawnChildWithOptions("CheckDataDirHasStaged", options));
+  ASSERT_TRUE(process.IsValid());
+  SignalChildren(signal_file.c_str());
+
+  int exit_code = 42;
+  EXPECT_TRUE(process.WaitForExit(&exit_code));
+  EXPECT_EQ(kSuccess, exit_code);
+}
+
+MULTIPROCESS_TEST_MAIN(CheckMountedDir) {
+  if (!PathExists(base::FilePath("/foo/staged"))) {
+    return 1;
+  }
+  WaitToDie(ProcessUtilTest::GetSignalFilePath(kFooDirHasStaged).c_str());
+  return kSuccess;
+}
+
+// Test that we can install an opaque handle in the child process' namespace.
+TEST_F(ProcessUtilTest, TransferHandleToPath) {
+  const std::string signal_file =
+      ProcessUtilTest::GetSignalFilePath(kFooDirHasStaged);
+  remove(signal_file.c_str());
+
+  // Create a tempdir with "staged" as its contents.
+  ScopedTempDir new_tmpdir;
+  ASSERT_TRUE(new_tmpdir.CreateUniqueTempDir());
+  base::FilePath staged_file_path = new_tmpdir.GetPath().Append("staged");
+  base::File staged_file(staged_file_path,
+                         base::File::FLAG_CREATE | base::File::FLAG_WRITE);
+  ASSERT_TRUE(staged_file.created());
+  staged_file.Close();
+
+  // Mount the tempdir to "/foo".
+  zx::handle tmp_handle = fuchsia::GetHandleFromFile(
+      base::File(base::FilePath(new_tmpdir.GetPath()),
+                 base::File::FLAG_OPEN | base::File::FLAG_READ));
+  ASSERT_TRUE(tmp_handle.is_valid());
+  LaunchOptions options;
+  options.paths_to_clone.push_back(base::FilePath("/tmp"));
+  options.paths_to_transfer.push_back(
+      {base::FilePath("/foo"), tmp_handle.release()});
+  options.spawn_flags = FDIO_SPAWN_CLONE_STDIO;
+
+  // Verify from that "/foo/staged" exists from the child process' perspective.
+  Process process(SpawnChildWithOptions("CheckMountedDir", options));
+  ASSERT_TRUE(process.IsValid());
+  SignalChildren(signal_file.c_str());
+
+  int exit_code = 42;
+  EXPECT_TRUE(process.WaitForExit(&exit_code));
+  EXPECT_EQ(kSuccess, exit_code);
+}
+
+MULTIPROCESS_TEST_MAIN(CheckTmpFileExists) {
+  // Look through the filesystem to ensure that no other directories
+  // besides "tmp" are in the namespace.
+  base::FileEnumerator enumerator(
+      base::FilePath("/"), false,
+      base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES);
+  base::FilePath next_path;
+  while (!(next_path = enumerator.Next()).empty()) {
+    if (next_path != base::FilePath("/tmp")) {
+      LOG(ERROR) << "Clone policy violation: found non-tmp directory "
+                 << next_path.MaybeAsASCII();
+      return 1;
+    }
+  }
+  WaitToDie(ProcessUtilTest::GetSignalFilePath(kSignalFileClone).c_str());
+  return kSuccess;
+}
+
+TEST_F(ProcessUtilTest, CloneTmp) {
+  const std::string signal_file =
+      ProcessUtilTest::GetSignalFilePath(kSignalFileClone);
+  remove(signal_file.c_str());
+
+  LaunchOptions options;
+  options.paths_to_clone.push_back(base::FilePath("/tmp"));
+  options.spawn_flags = FDIO_SPAWN_CLONE_STDIO;
+
+  Process process(SpawnChildWithOptions("CheckTmpFileExists", options));
+  ASSERT_TRUE(process.IsValid());
+
+  SignalChildren(signal_file.c_str());
+
+  int exit_code = 42;
+  EXPECT_TRUE(process.WaitForExit(&exit_code));
+  EXPECT_EQ(kSuccess, exit_code);
+}
+
+MULTIPROCESS_TEST_MAIN(CheckMountedDirDoesNotExist) {
+  if (PathExists(base::FilePath("/foo"))) {
+    return 1;
+  }
+  WaitToDie(
+      ProcessUtilTest::GetSignalFilePath(kFooDirDoesNotHaveStaged).c_str());
+  return kSuccess;
+}
+
+TEST_F(ProcessUtilTest, TransferInvalidHandleFails) {
+  LaunchOptions options;
+  options.paths_to_clone.push_back(base::FilePath("/tmp"));
+  options.paths_to_transfer.push_back(
+      {base::FilePath("/foo"), ZX_HANDLE_INVALID});
+  options.spawn_flags = FDIO_SPAWN_CLONE_STDIO;
+
+  // Verify that the process is never constructed.
+  const std::string signal_file =
+      ProcessUtilTest::GetSignalFilePath(kFooDirDoesNotHaveStaged);
+  remove(signal_file.c_str());
+  Process process(
+      SpawnChildWithOptions("CheckMountedDirDoesNotExist", options));
+  ASSERT_FALSE(process.IsValid());
+}
+
+TEST_F(ProcessUtilTest, CloneInvalidDirFails) {
+  const std::string signal_file =
+      ProcessUtilTest::GetSignalFilePath(kSignalFileClone);
+  remove(signal_file.c_str());
+
+  LaunchOptions options;
+  options.paths_to_clone.push_back(base::FilePath("/tmp"));
+  options.paths_to_clone.push_back(base::FilePath("/definitely_not_a_dir"));
+  options.spawn_flags = FDIO_SPAWN_CLONE_STDIO;
+
+  Process process(SpawnChildWithOptions("CheckTmpFileExists", options));
+  ASSERT_FALSE(process.IsValid());
+}
+
+// Test that we can clone other directories. CheckTmpFileExists will return an
+// error code if it detects a directory other than "/tmp", so we can use that as
+// a signal that it successfully detected another entry in the root namespace.
+TEST_F(ProcessUtilTest, CloneAlternateDir) {
+  const std::string signal_file =
+      ProcessUtilTest::GetSignalFilePath(kSignalFileClone);
+  remove(signal_file.c_str());
+
+  LaunchOptions options;
+  options.paths_to_clone.push_back(base::FilePath("/tmp"));
+  options.paths_to_clone.push_back(base::FilePath("/data"));
+  options.spawn_flags = FDIO_SPAWN_CLONE_STDIO;
+
+  Process process(SpawnChildWithOptions("CheckTmpFileExists", options));
+  ASSERT_TRUE(process.IsValid());
+
+  SignalChildren(signal_file.c_str());
+
+  int exit_code = 42;
+  EXPECT_TRUE(process.WaitForExit(&exit_code));
+  EXPECT_EQ(1, exit_code);
+}
+
+TEST_F(ProcessUtilTest, HandlesToTransferClosedOnSpawnFailure) {
+  zx::handle handles[2];
+  zx_status_t result = zx_channel_create(0, handles[0].reset_and_get_address(),
+                                         handles[1].reset_and_get_address());
+  ZX_CHECK(ZX_OK == result, result) << "zx_channel_create";
+
+  LaunchOptions options;
+  options.handles_to_transfer.push_back({0, handles[0].get()});
+
+  // Launch a non-existent binary, causing fdio_spawn() to fail.
+  CommandLine command_line(FilePath(
+      FILE_PATH_LITERAL("💩magical_filename_that_will_never_exist_ever")));
+  Process process(LaunchProcess(command_line, options));
+  ASSERT_FALSE(process.IsValid());
+
+  // If LaunchProcess did its job then handles[0] is no longer valid, and
+  // handles[1] should observe a channel-closed signal.
+  EXPECT_EQ(
+      zx_object_wait_one(handles[1].get(), ZX_CHANNEL_PEER_CLOSED, 0, nullptr),
+      ZX_OK);
+  EXPECT_EQ(ZX_ERR_BAD_HANDLE, zx_handle_close(handles[0].get()));
+  ignore_result(handles[0].release());
+}
+
+TEST_F(ProcessUtilTest, HandlesToTransferClosedOnBadPathToMapFailure) {
+  zx::handle handles[2];
+  zx_status_t result = zx_channel_create(0, handles[0].reset_and_get_address(),
+                                         handles[1].reset_and_get_address());
+  ZX_CHECK(ZX_OK == result, result) << "zx_channel_create";
+
+  LaunchOptions options;
+  options.handles_to_transfer.push_back({0, handles[0].get()});
+  options.spawn_flags = options.spawn_flags & ~FDIO_SPAWN_CLONE_NAMESPACE;
+  options.paths_to_clone.emplace_back(
+      "💩magical_path_that_will_never_exist_ever");
+
+  // LaunchProces should fail to open() the path_to_map, and fail before
+  // fdio_spawn().
+  Process process(LaunchProcess(CommandLine(FilePath()), options));
+  ASSERT_FALSE(process.IsValid());
+
+  // If LaunchProcess did its job then handles[0] is no longer valid, and
+  // handles[1] should observe a channel-closed signal.
+  EXPECT_EQ(
+      zx_object_wait_one(handles[1].get(), ZX_CHANNEL_PEER_CLOSED, 0, nullptr),
+      ZX_OK);
+  EXPECT_EQ(ZX_ERR_BAD_HANDLE, zx_handle_close(handles[0].get()));
+  ignore_result(handles[0].release());
+}
+#endif  // defined(OS_FUCHSIA)
+
+// On Android SpawnProcess() doesn't use LaunchProcess() and doesn't support
+// LaunchOptions::current_directory.
+#if !defined(OS_ANDROID)
+MULTIPROCESS_TEST_MAIN(CheckCwdProcess) {
+  FilePath expected;
+  CHECK(GetTempDir(&expected));
+  expected = MakeAbsoluteFilePath(expected);
+  CHECK(!expected.empty());
+
+  FilePath actual;
+  CHECK(GetCurrentDirectory(&actual));
+  actual = MakeAbsoluteFilePath(actual);
+  CHECK(!actual.empty());
+
+  CHECK(expected == actual) << "Expected: " << expected.value()
+                            << "  Actual: " << actual.value();
+  return kSuccess;
+}
+
+TEST_F(ProcessUtilTest, CurrentDirectory) {
+  // TODO(rickyz): Add support for passing arguments to multiprocess children,
+  // then create a special directory for this test.
+  FilePath tmp_dir;
+  ASSERT_TRUE(GetTempDir(&tmp_dir));
+
+  LaunchOptions options;
+  options.current_directory = tmp_dir;
+
+  Process process(SpawnChildWithOptions("CheckCwdProcess", options));
+  ASSERT_TRUE(process.IsValid());
+
+  int exit_code = 42;
+  EXPECT_TRUE(process.WaitForExit(&exit_code));
+  EXPECT_EQ(kSuccess, exit_code);
+}
+#endif  // !defined(OS_ANDROID)
+
+#if defined(OS_WIN)
+// TODO(cpu): figure out how to test this in other platforms.
+TEST_F(ProcessUtilTest, GetProcId) {
+  ProcessId id1 = GetProcId(GetCurrentProcess());
+  EXPECT_NE(0ul, id1);
+  Process process = SpawnChild("SimpleChildProcess");
+  ASSERT_TRUE(process.IsValid());
+  ProcessId id2 = process.Pid();
+  EXPECT_NE(0ul, id2);
+  EXPECT_NE(id1, id2);
+}
+#endif  // defined(OS_WIN)
+
+#if !defined(OS_MACOSX) && !defined(OS_ANDROID)
+// This test is disabled on Mac, since it's flaky due to ReportCrash
+// taking a variable amount of time to parse and load the debug and
+// symbol data for this unit test's executable before firing the
+// signal handler.
+//
+// TODO(gspencer): turn this test process into a very small program
+// with no symbols (instead of using the multiprocess testing
+// framework) to reduce the ReportCrash overhead.
+//
+// It is disabled on Android as MultiprocessTests are started as services that
+// the framework restarts on crashes.
+const char kSignalFileCrash[] = "CrashingChildProcess.die";
+
+MULTIPROCESS_TEST_MAIN(CrashingChildProcess) {
+  WaitToDie(ProcessUtilTest::GetSignalFilePath(kSignalFileCrash).c_str());
+#if defined(OS_POSIX)
+  // Have to disable to signal handler for segv so we can get a crash
+  // instead of an abnormal termination through the crash dump handler.
+  ::signal(SIGSEGV, SIG_DFL);
+#endif
+  // Make this process have a segmentation fault.
+  volatile int* oops = nullptr;
+  *oops = 0xDEAD;
+  return 1;
+}
+
+// This test intentionally crashes, so we don't need to run it under
+// AddressSanitizer.
+#if defined(ADDRESS_SANITIZER) || defined(OS_FUCHSIA)
+// TODO(crbug.com/753490): Access to the process termination reason is not
+// implemented in Fuchsia.
+#define MAYBE_GetTerminationStatusCrash DISABLED_GetTerminationStatusCrash
+#else
+#define MAYBE_GetTerminationStatusCrash GetTerminationStatusCrash
+#endif
+TEST_F(ProcessUtilTest, MAYBE_GetTerminationStatusCrash) {
+  const std::string signal_file =
+    ProcessUtilTest::GetSignalFilePath(kSignalFileCrash);
+  remove(signal_file.c_str());
+  Process process = SpawnChild("CrashingChildProcess");
+  ASSERT_TRUE(process.IsValid());
+
+  int exit_code = 42;
+  EXPECT_EQ(TERMINATION_STATUS_STILL_RUNNING,
+            GetTerminationStatus(process.Handle(), &exit_code));
+  EXPECT_EQ(kExpectedStillRunningExitCode, exit_code);
+
+  SignalChildren(signal_file.c_str());
+  exit_code = 42;
+  TerminationStatus status =
+      WaitForChildTermination(process.Handle(), &exit_code);
+  EXPECT_EQ(TERMINATION_STATUS_PROCESS_CRASHED, status);
+
+#if defined(OS_WIN)
+  EXPECT_EQ(static_cast<int>(0xc0000005), exit_code);
+#elif defined(OS_POSIX)
+  int signaled = WIFSIGNALED(exit_code);
+  EXPECT_NE(0, signaled);
+  int signal = WTERMSIG(exit_code);
+  EXPECT_EQ(SIGSEGV, signal);
+#endif
+
+  // Reset signal handlers back to "normal".
+  debug::EnableInProcessStackDumping();
+  remove(signal_file.c_str());
+}
+#endif  // !defined(OS_MACOSX) && !defined(OS_ANDROID)
+
+MULTIPROCESS_TEST_MAIN(KilledChildProcess) {
+  WaitToDie(ProcessUtilTest::GetSignalFilePath(kSignalFileKill).c_str());
+#if defined(OS_WIN)
+  // Kill ourselves.
+  HANDLE handle = ::OpenProcess(PROCESS_ALL_ACCESS, 0, ::GetCurrentProcessId());
+  ::TerminateProcess(handle, kExpectedKilledExitCode);
+#elif defined(OS_POSIX)
+  // Send a SIGKILL to this process, just like the OOM killer would.
+  ::kill(getpid(), SIGKILL);
+#elif defined(OS_FUCHSIA)
+  zx_task_kill(zx_process_self());
+#endif
+  return 1;
+}
+
+#if defined(OS_POSIX)
+MULTIPROCESS_TEST_MAIN(TerminatedChildProcess) {
+  WaitToDie(ProcessUtilTest::GetSignalFilePath(kSignalFileTerm).c_str());
+  // Send a SIGTERM to this process.
+  ::kill(getpid(), SIGTERM);
+  return 1;
+}
+#endif  // defined(OS_POSIX) || defined(OS_FUCHSIA)
+
+#if defined(OS_FUCHSIA)
+// TODO(crbug.com/753490): Access to the process termination reason is not
+// implemented in Fuchsia.
+#define MAYBE_GetTerminationStatusSigKill DISABLED_GetTerminationStatusSigKill
+#else
+#define MAYBE_GetTerminationStatusSigKill GetTerminationStatusSigKill
+#endif
+TEST_F(ProcessUtilTest, MAYBE_GetTerminationStatusSigKill) {
+  const std::string signal_file =
+    ProcessUtilTest::GetSignalFilePath(kSignalFileKill);
+  remove(signal_file.c_str());
+  Process process = SpawnChild("KilledChildProcess");
+  ASSERT_TRUE(process.IsValid());
+
+  int exit_code = 42;
+  EXPECT_EQ(TERMINATION_STATUS_STILL_RUNNING,
+            GetTerminationStatus(process.Handle(), &exit_code));
+  EXPECT_EQ(kExpectedStillRunningExitCode, exit_code);
+
+  SignalChildren(signal_file.c_str());
+  exit_code = 42;
+  TerminationStatus status =
+      WaitForChildTermination(process.Handle(), &exit_code);
+#if defined(OS_CHROMEOS)
+  EXPECT_EQ(TERMINATION_STATUS_PROCESS_WAS_KILLED_BY_OOM, status);
+#else
+  EXPECT_EQ(TERMINATION_STATUS_PROCESS_WAS_KILLED, status);
+#endif
+
+#if defined(OS_WIN)
+  EXPECT_EQ(kExpectedKilledExitCode, exit_code);
+#elif defined(OS_POSIX)
+  int signaled = WIFSIGNALED(exit_code);
+  EXPECT_NE(0, signaled);
+  int signal = WTERMSIG(exit_code);
+  EXPECT_EQ(SIGKILL, signal);
+#endif
+  remove(signal_file.c_str());
+}
+
+#if defined(OS_POSIX)
+// TODO(crbug.com/753490): Access to the process termination reason is not
+// implemented in Fuchsia. Unix signals are not implemented in Fuchsia so this
+// test might not be relevant anyway.
+TEST_F(ProcessUtilTest, GetTerminationStatusSigTerm) {
+  const std::string signal_file =
+    ProcessUtilTest::GetSignalFilePath(kSignalFileTerm);
+  remove(signal_file.c_str());
+  Process process = SpawnChild("TerminatedChildProcess");
+  ASSERT_TRUE(process.IsValid());
+
+  int exit_code = 42;
+  EXPECT_EQ(TERMINATION_STATUS_STILL_RUNNING,
+            GetTerminationStatus(process.Handle(), &exit_code));
+  EXPECT_EQ(kExpectedStillRunningExitCode, exit_code);
+
+  SignalChildren(signal_file.c_str());
+  exit_code = 42;
+  TerminationStatus status =
+      WaitForChildTermination(process.Handle(), &exit_code);
+  EXPECT_EQ(TERMINATION_STATUS_PROCESS_WAS_KILLED, status);
+
+  int signaled = WIFSIGNALED(exit_code);
+  EXPECT_NE(0, signaled);
+  int signal = WTERMSIG(exit_code);
+  EXPECT_EQ(SIGTERM, signal);
+  remove(signal_file.c_str());
+}
+#endif  // defined(OS_POSIX)
+
+TEST_F(ProcessUtilTest, EnsureTerminationUndying) {
+  test::ScopedTaskEnvironment task_environment;
+
+  Process child_process = SpawnChild("process_util_test_never_die");
+  ASSERT_TRUE(child_process.IsValid());
+
+  EnsureProcessTerminated(child_process.Duplicate());
+
+#if defined(OS_POSIX)
+  errno = 0;
+#endif  // defined(OS_POSIX)
+
+  // Allow a generous timeout, to cope with slow/loaded test bots.
+  bool did_exit = child_process.WaitForExitWithTimeout(
+      TestTimeouts::action_max_timeout(), nullptr);
+
+#if defined(OS_POSIX)
+  // Both EnsureProcessTerminated() and WaitForExitWithTimeout() will call
+  // waitpid(). One will succeed, and the other will fail with ECHILD. If our
+  // wait failed then check for ECHILD, and assumed |did_exit| in that case.
+  did_exit = did_exit || (errno == ECHILD);
+#endif  // defined(OS_POSIX)
+
+  EXPECT_TRUE(did_exit);
+}
+
+MULTIPROCESS_TEST_MAIN(process_util_test_never_die) {
+  while (1) {
+    PlatformThread::Sleep(TimeDelta::FromSeconds(500));
+  }
+  return kSuccess;
+}
+
+TEST_F(ProcessUtilTest, EnsureTerminationGracefulExit) {
+  test::ScopedTaskEnvironment task_environment;
+
+  Process child_process = SpawnChild("process_util_test_die_immediately");
+  ASSERT_TRUE(child_process.IsValid());
+
+  // Wait for the child process to actually exit.
+  child_process.Duplicate().WaitForExitWithTimeout(
+      TestTimeouts::action_max_timeout(), nullptr);
+
+  EnsureProcessTerminated(child_process.Duplicate());
+
+  // Verify that the process is really, truly gone.
+  EXPECT_TRUE(child_process.WaitForExitWithTimeout(
+      TestTimeouts::action_max_timeout(), nullptr));
+}
+
+MULTIPROCESS_TEST_MAIN(process_util_test_die_immediately) {
+  return kSuccess;
+}
+
+#if defined(OS_WIN)
+// TODO(estade): if possible, port this test.
+TEST_F(ProcessUtilTest, LaunchAsUser) {
+  UserTokenHandle token;
+  ASSERT_TRUE(OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &token));
+  LaunchOptions options;
+  options.as_user = token;
+  EXPECT_TRUE(
+      LaunchProcess(MakeCmdLine("SimpleChildProcess"), options).IsValid());
+}
+
+static const char kEventToTriggerHandleSwitch[] = "event-to-trigger-handle";
+
+MULTIPROCESS_TEST_MAIN(TriggerEventChildProcess) {
+  std::string handle_value_string =
+      CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+          kEventToTriggerHandleSwitch);
+  CHECK(!handle_value_string.empty());
+
+  uint64_t handle_value_uint64;
+  CHECK(StringToUint64(handle_value_string, &handle_value_uint64));
+  // Give ownership of the handle to |event|.
+  WaitableEvent event(
+      win::ScopedHandle(reinterpret_cast<HANDLE>(handle_value_uint64)));
+
+  event.Signal();
+
+  return 0;
+}
+
+TEST_F(ProcessUtilTest, InheritSpecifiedHandles) {
+  // Manually create the event, so that it can be inheritable.
+  SECURITY_ATTRIBUTES security_attributes = {};
+  security_attributes.nLength = static_cast<DWORD>(sizeof(security_attributes));
+  security_attributes.lpSecurityDescriptor = NULL;
+  security_attributes.bInheritHandle = true;
+
+  // Takes ownership of the event handle.
+  WaitableEvent event(
+      win::ScopedHandle(CreateEvent(&security_attributes, true, false, NULL)));
+  LaunchOptions options;
+  options.handles_to_inherit.emplace_back(event.handle());
+
+  CommandLine cmd_line = MakeCmdLine("TriggerEventChildProcess");
+  cmd_line.AppendSwitchASCII(
+      kEventToTriggerHandleSwitch,
+      NumberToString(reinterpret_cast<uint64_t>(event.handle())));
+
+  // Launch the process and wait for it to trigger the event.
+  ASSERT_TRUE(LaunchProcess(cmd_line, options).IsValid());
+  EXPECT_TRUE(event.TimedWait(TestTimeouts::action_max_timeout()));
+}
+#endif  // defined(OS_WIN)
+
+TEST_F(ProcessUtilTest, GetAppOutput) {
+  base::CommandLine command(test_helper_path_);
+  command.AppendArg("hello");
+  command.AppendArg("there");
+  command.AppendArg("good");
+  command.AppendArg("people");
+  std::string output;
+  EXPECT_TRUE(GetAppOutput(command, &output));
+  EXPECT_EQ("hello there good people", output);
+  output.clear();
+
+  const char* kEchoMessage = "blah";
+  command = base::CommandLine(test_helper_path_);
+  command.AppendArg("-x");
+  command.AppendArg("28");
+  command.AppendArg(kEchoMessage);
+  EXPECT_FALSE(GetAppOutput(command, &output));
+  EXPECT_EQ(kEchoMessage, output);
+}
+
+TEST_F(ProcessUtilTest, GetAppOutputWithExitCode) {
+  const char* kEchoMessage1 = "doge";
+  int exit_code = -1;
+  base::CommandLine command(test_helper_path_);
+  command.AppendArg(kEchoMessage1);
+  std::string output;
+  EXPECT_TRUE(GetAppOutputWithExitCode(command, &output, &exit_code));
+  EXPECT_EQ(kEchoMessage1, output);
+  EXPECT_EQ(0, exit_code);
+  output.clear();
+
+  const char* kEchoMessage2 = "pupper";
+  const int kExpectedExitCode = 42;
+  command = base::CommandLine(test_helper_path_);
+  command.AppendArg("-x");
+  command.AppendArg(base::IntToString(kExpectedExitCode));
+  command.AppendArg(kEchoMessage2);
+#if defined(OS_WIN)
+  // On Windows, anything that quits with a nonzero status code is handled as a
+  // "crash", so just ignore GetAppOutputWithExitCode's return value.
+  GetAppOutputWithExitCode(command, &output, &exit_code);
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+  EXPECT_TRUE(GetAppOutputWithExitCode(command, &output, &exit_code));
+#endif
+  EXPECT_EQ(kEchoMessage2, output);
+  EXPECT_EQ(kExpectedExitCode, exit_code);
+}
+
+#if defined(OS_POSIX) || defined(OS_FUCHSIA)
+
+namespace {
+
+// Returns the maximum number of files that a process can have open.
+// Returns 0 on error.
+int GetMaxFilesOpenInProcess() {
+#if defined(OS_FUCHSIA)
+  return FDIO_MAX_FD;
+#else
+  struct rlimit rlim;
+  if (getrlimit(RLIMIT_NOFILE, &rlim) != 0) {
+    return 0;
+  }
+
+  // rlim_t is a uint64_t - clip to maxint. We do this since FD #s are ints
+  // which are all 32 bits on the supported platforms.
+  rlim_t max_int = static_cast<rlim_t>(std::numeric_limits<int32_t>::max());
+  if (rlim.rlim_cur > max_int) {
+    return max_int;
+  }
+
+  return rlim.rlim_cur;
+#endif  // defined(OS_FUCHSIA)
+}
+
+const int kChildPipe = 20;  // FD # for write end of pipe in child process.
+
+#if defined(OS_MACOSX)
+
+// <http://opensource.apple.com/source/xnu/xnu-2422.1.72/bsd/sys/guarded.h>
+#if !defined(_GUARDID_T)
+#define _GUARDID_T
+typedef __uint64_t guardid_t;
+#endif  // _GUARDID_T
+
+// From .../MacOSX10.9.sdk/usr/include/sys/syscall.h
+#if !defined(SYS_change_fdguard_np)
+#define SYS_change_fdguard_np 444
+#endif
+
+// <http://opensource.apple.com/source/xnu/xnu-2422.1.72/bsd/sys/guarded.h>
+#if !defined(GUARD_DUP)
+#define GUARD_DUP (1u << 1)
+#endif
+
+// <http://opensource.apple.com/source/xnu/xnu-2422.1.72/bsd/kern/kern_guarded.c?txt>
+//
+// Atomically replaces |guard|/|guardflags| with |nguard|/|nguardflags| on |fd|.
+int change_fdguard_np(int fd,
+                      const guardid_t *guard, u_int guardflags,
+                      const guardid_t *nguard, u_int nguardflags,
+                      int *fdflagsp) {
+  return syscall(SYS_change_fdguard_np, fd, guard, guardflags,
+                 nguard, nguardflags, fdflagsp);
+}
+
+// Attempt to set a file-descriptor guard on |fd|.  In case of success, remove
+// it and return |true| to indicate that it can be guarded.  Returning |false|
+// means either that |fd| is guarded by some other code, or more likely EBADF.
+//
+// Starting with 10.9, libdispatch began setting GUARD_DUP on a file descriptor.
+// Unfortunately, it is spun up as part of +[NSApplication initialize], which is
+// not really something that Chromium can avoid using on OSX.  See
+// <http://crbug.com/338157>.  This function allows querying whether the file
+// descriptor is guarded before attempting to close it.
+bool CanGuardFd(int fd) {
+  // Saves the original flags to reset later.
+  int original_fdflags = 0;
+
+  // This can be any value at all, it just has to match up between the two
+  // calls.
+  const guardid_t kGuard = 15;
+
+  // Attempt to change the guard.  This can fail with EBADF if the file
+  // descriptor is bad, or EINVAL if the fd already has a guard set.
+  int ret =
+      change_fdguard_np(fd, NULL, 0, &kGuard, GUARD_DUP, &original_fdflags);
+  if (ret == -1)
+    return false;
+
+  // Remove the guard.  It should not be possible to fail in removing the guard
+  // just added.
+  ret = change_fdguard_np(fd, &kGuard, GUARD_DUP, NULL, 0, &original_fdflags);
+  DPCHECK(ret == 0);
+
+  return true;
+}
+#endif  // defined(OS_MACOSX)
+
+}  // namespace
+
+MULTIPROCESS_TEST_MAIN(ProcessUtilsLeakFDChildProcess) {
+  // This child process counts the number of open FDs, it then writes that
+  // number out to a pipe connected to the parent.
+  int num_open_files = 0;
+  int write_pipe = kChildPipe;
+  int max_files = GetMaxFilesOpenInProcess();
+  for (int i = STDERR_FILENO + 1; i < max_files; i++) {
+#if defined(OS_MACOSX)
+    // Ignore guarded or invalid file descriptors.
+    if (!CanGuardFd(i))
+      continue;
+#endif
+
+    if (i != kChildPipe) {
+      int fd;
+      if ((fd = HANDLE_EINTR(dup(i))) != -1) {
+        close(fd);
+        num_open_files += 1;
+      }
+    }
+  }
+
+  int written = HANDLE_EINTR(write(write_pipe, &num_open_files,
+                                   sizeof(num_open_files)));
+  DCHECK_EQ(static_cast<size_t>(written), sizeof(num_open_files));
+  int ret = IGNORE_EINTR(close(write_pipe));
+  DPCHECK(ret == 0);
+
+  return 0;
+}
+
+int ProcessUtilTest::CountOpenFDsInChild() {
+  int fds[2];
+  if (pipe(fds) < 0)
+    NOTREACHED();
+
+  LaunchOptions options;
+  options.fds_to_remap.emplace_back(fds[1], kChildPipe);
+  Process process =
+      SpawnChildWithOptions("ProcessUtilsLeakFDChildProcess", options);
+  CHECK(process.IsValid());
+  int ret = IGNORE_EINTR(close(fds[1]));
+  DPCHECK(ret == 0);
+
+  // Read number of open files in client process from pipe;
+  int num_open_files = -1;
+  ssize_t bytes_read =
+      HANDLE_EINTR(read(fds[0], &num_open_files, sizeof(num_open_files)));
+  CHECK_EQ(bytes_read, static_cast<ssize_t>(sizeof(num_open_files)));
+
+#if defined(THREAD_SANITIZER)
+  // Compiler-based ThreadSanitizer makes this test slow.
+  TimeDelta timeout = TimeDelta::FromSeconds(3);
+#else
+  TimeDelta timeout = TimeDelta::FromSeconds(1);
+#endif
+  int exit_code;
+  CHECK(process.WaitForExitWithTimeout(timeout, &exit_code));
+  ret = IGNORE_EINTR(close(fds[0]));
+  DPCHECK(ret == 0);
+
+  return num_open_files;
+}
+
+#if defined(ADDRESS_SANITIZER) || defined(THREAD_SANITIZER)
+// ProcessUtilTest.FDRemapping is flaky when ran under xvfb-run on Precise.
+// The problem is 100% reproducible with both ASan and TSan.
+// See http://crbug.com/136720.
+#define MAYBE_FDRemapping DISABLED_FDRemapping
+#else
+#define MAYBE_FDRemapping FDRemapping
+#endif  // defined(ADDRESS_SANITIZER) || defined(THREAD_SANITIZER)
+TEST_F(ProcessUtilTest, MAYBE_FDRemapping) {
+  int fds_before = CountOpenFDsInChild();
+
+  // open some dummy fds to make sure they don't propagate over to the
+  // child process.
+  int dev_null = open("/dev/null", O_RDONLY);
+  DPCHECK(dev_null != -1);
+  int sockets[2];
+  int ret = socketpair(AF_UNIX, SOCK_STREAM, 0, sockets);
+  DPCHECK(ret == 0);
+
+  int fds_after = CountOpenFDsInChild();
+
+  ASSERT_EQ(fds_after, fds_before);
+
+  ret = IGNORE_EINTR(close(sockets[0]));
+  DPCHECK(ret == 0);
+  ret = IGNORE_EINTR(close(sockets[1]));
+  DPCHECK(ret == 0);
+  ret = IGNORE_EINTR(close(dev_null));
+  DPCHECK(ret == 0);
+}
+
+const char kPipeValue = '\xcc';
+MULTIPROCESS_TEST_MAIN(ProcessUtilsVerifyStdio) {
+  // Write to stdio so the parent process can observe output.
+  CHECK_EQ(1, HANDLE_EINTR(write(STDOUT_FILENO, &kPipeValue, 1)));
+
+  // Close all of the handles, to verify they are valid.
+  CHECK_EQ(0, IGNORE_EINTR(close(STDIN_FILENO)));
+  CHECK_EQ(0, IGNORE_EINTR(close(STDOUT_FILENO)));
+  CHECK_EQ(0, IGNORE_EINTR(close(STDERR_FILENO)));
+  return 0;
+}
+
+TEST_F(ProcessUtilTest, FDRemappingIncludesStdio) {
+  int dev_null = open("/dev/null", O_RDONLY);
+  ASSERT_LT(2, dev_null);
+
+  // Backup stdio and replace it with the write end of a pipe, for our
+  // child process to inherit.
+  int pipe_fds[2];
+  int result = pipe(pipe_fds);
+  ASSERT_EQ(0, result);
+  int backup_stdio = HANDLE_EINTR(dup(STDOUT_FILENO));
+  ASSERT_LE(0, backup_stdio);
+  result = dup2(pipe_fds[1], STDOUT_FILENO);
+  ASSERT_EQ(STDOUT_FILENO, result);
+
+  // Launch the test process, which should inherit our pipe stdio.
+  LaunchOptions options;
+  options.fds_to_remap.emplace_back(dev_null, dev_null);
+  Process process = SpawnChildWithOptions("ProcessUtilsVerifyStdio", options);
+  ASSERT_TRUE(process.IsValid());
+
+  // Restore stdio, so we can output stuff.
+  result = dup2(backup_stdio, STDOUT_FILENO);
+  ASSERT_EQ(STDOUT_FILENO, result);
+
+  // Close our copy of the write end of the pipe, so that the read()
+  // from the other end will see EOF if it wasn't copied to the child.
+  result = IGNORE_EINTR(close(pipe_fds[1]));
+  ASSERT_EQ(0, result);
+
+  result = IGNORE_EINTR(close(backup_stdio));
+  ASSERT_EQ(0, result);
+  result = IGNORE_EINTR(close(dev_null));
+  ASSERT_EQ(0, result);
+
+  // Read from the pipe to verify that it is connected to the child
+  // process' stdio.
+  char buf[16] = {};
+  EXPECT_EQ(1, HANDLE_EINTR(read(pipe_fds[0], buf, sizeof(buf))));
+  EXPECT_EQ(kPipeValue, buf[0]);
+
+  result = IGNORE_EINTR(close(pipe_fds[0]));
+  ASSERT_EQ(0, result);
+
+  int exit_code;
+  ASSERT_TRUE(
+      process.WaitForExitWithTimeout(TimeDelta::FromSeconds(5), &exit_code));
+  EXPECT_EQ(0, exit_code);
+}
+
+#if defined(OS_FUCHSIA)
+
+const uint16_t kStartupHandleId = 43;
+MULTIPROCESS_TEST_MAIN(ProcessUtilsVerifyHandle) {
+  zx_handle_t handle =
+      zx_take_startup_handle(PA_HND(PA_USER0, kStartupHandleId));
+  CHECK_NE(ZX_HANDLE_INVALID, handle);
+
+  // Write to the pipe so the parent process can observe output.
+  size_t bytes_written = 0;
+  zx_status_t result = zx_socket_write(handle, 0, &kPipeValue,
+                                       sizeof(kPipeValue), &bytes_written);
+  CHECK_EQ(ZX_OK, result);
+  CHECK_EQ(1u, bytes_written);
+
+  CHECK_EQ(ZX_OK, zx_handle_close(handle));
+  return 0;
+}
+
+TEST_F(ProcessUtilTest, LaunchWithHandleTransfer) {
+  // Create a pipe to pass to the child process.
+  zx_handle_t handles[2];
+  zx_status_t result =
+      zx_socket_create(ZX_SOCKET_STREAM, &handles[0], &handles[1]);
+  ASSERT_EQ(ZX_OK, result);
+
+  // Launch the test process, and pass it one end of the pipe.
+  LaunchOptions options;
+  options.handles_to_transfer.push_back(
+      {PA_HND(PA_USER0, kStartupHandleId), handles[0]});
+  Process process = SpawnChildWithOptions("ProcessUtilsVerifyHandle", options);
+  ASSERT_TRUE(process.IsValid());
+
+  // Read from the pipe to verify that the child received it.
+  zx_signals_t signals = 0;
+  result = zx_object_wait_one(
+      handles[1], ZX_SOCKET_READABLE | ZX_SOCKET_PEER_CLOSED,
+      (base::TimeTicks::Now() + TestTimeouts::action_timeout()).ToZxTime(),
+      &signals);
+  ASSERT_EQ(ZX_OK, result);
+  ASSERT_TRUE(signals & ZX_SOCKET_READABLE);
+
+  size_t bytes_read = 0;
+  char buf[16] = {0};
+  result = zx_socket_read(handles[1], 0, buf, sizeof(buf), &bytes_read);
+  EXPECT_EQ(ZX_OK, result);
+  EXPECT_EQ(1u, bytes_read);
+  EXPECT_EQ(kPipeValue, buf[0]);
+
+  CHECK_EQ(ZX_OK, zx_handle_close(handles[1]));
+
+  int exit_code;
+  ASSERT_TRUE(process.WaitForExitWithTimeout(TestTimeouts::action_timeout(),
+                                             &exit_code));
+  EXPECT_EQ(0, exit_code);
+}
+
+#endif  // defined(OS_FUCHSIA)
+
+namespace {
+
+std::string TestLaunchProcess(const std::vector<std::string>& args,
+                              const EnvironmentMap& env_changes,
+                              const bool clear_environ,
+                              const int clone_flags) {
+  int fds[2];
+  PCHECK(pipe(fds) == 0);
+
+  LaunchOptions options;
+  options.wait = true;
+  options.environ = env_changes;
+  options.clear_environ = clear_environ;
+  options.fds_to_remap.emplace_back(fds[1], 1);
+#if defined(OS_LINUX)
+  options.clone_flags = clone_flags;
+#else
+  CHECK_EQ(0, clone_flags);
+#endif  // defined(OS_LINUX)
+  EXPECT_TRUE(LaunchProcess(args, options).IsValid());
+  PCHECK(IGNORE_EINTR(close(fds[1])) == 0);
+
+  char buf[512];
+  const ssize_t n = HANDLE_EINTR(read(fds[0], buf, sizeof(buf)));
+
+  PCHECK(IGNORE_EINTR(close(fds[0])) == 0);
+
+  return std::string(buf, n);
+}
+
+const char kLargeString[] =
+    "0123456789012345678901234567890123456789012345678901234567890123456789"
+    "0123456789012345678901234567890123456789012345678901234567890123456789"
+    "0123456789012345678901234567890123456789012345678901234567890123456789"
+    "0123456789012345678901234567890123456789012345678901234567890123456789"
+    "0123456789012345678901234567890123456789012345678901234567890123456789"
+    "0123456789012345678901234567890123456789012345678901234567890123456789"
+    "0123456789012345678901234567890123456789012345678901234567890123456789";
+
+}  // namespace
+
+TEST_F(ProcessUtilTest, LaunchProcess) {
+  const int no_clone_flags = 0;
+  const bool no_clear_environ = false;
+  const char kBaseTest[] = "BASE_TEST";
+  const std::vector<std::string> kPrintEnvCommand = {test_helper_path_.value(),
+                                                     "-e", kBaseTest};
+
+  EnvironmentMap env_changes;
+  env_changes[kBaseTest] = "bar";
+  EXPECT_EQ("bar", TestLaunchProcess(kPrintEnvCommand, env_changes,
+                                     no_clear_environ, no_clone_flags));
+  env_changes.clear();
+
+  EXPECT_EQ(0, setenv(kBaseTest, "testing", 1 /* override */));
+  EXPECT_EQ("testing", TestLaunchProcess(kPrintEnvCommand, env_changes,
+                                         no_clear_environ, no_clone_flags));
+
+  env_changes[kBaseTest] = std::string();
+  EXPECT_EQ("", TestLaunchProcess(kPrintEnvCommand, env_changes,
+                                  no_clear_environ, no_clone_flags));
+
+  env_changes[kBaseTest] = "foo";
+  EXPECT_EQ("foo", TestLaunchProcess(kPrintEnvCommand, env_changes,
+                                     no_clear_environ, no_clone_flags));
+
+  env_changes.clear();
+  EXPECT_EQ(0, setenv(kBaseTest, kLargeString, 1 /* override */));
+  EXPECT_EQ(std::string(kLargeString),
+            TestLaunchProcess(kPrintEnvCommand, env_changes, no_clear_environ,
+                              no_clone_flags));
+
+  env_changes[kBaseTest] = "wibble";
+  EXPECT_EQ("wibble", TestLaunchProcess(kPrintEnvCommand, env_changes,
+                                        no_clear_environ, no_clone_flags));
+
+#if defined(OS_LINUX)
+  // Test a non-trival value for clone_flags.
+  EXPECT_EQ("wibble", TestLaunchProcess(kPrintEnvCommand, env_changes,
+                                        no_clear_environ, CLONE_FS));
+
+  EXPECT_EQ("wibble",
+            TestLaunchProcess(kPrintEnvCommand, env_changes,
+                              true /* clear_environ */, no_clone_flags));
+  env_changes.clear();
+  EXPECT_EQ("", TestLaunchProcess(kPrintEnvCommand, env_changes,
+                                  true /* clear_environ */, no_clone_flags));
+#endif  // defined(OS_LINUX)
+}
+
+// There's no such thing as a parent process id on Fuchsia.
+#if !defined(OS_FUCHSIA)
+TEST_F(ProcessUtilTest, GetParentProcessId) {
+  ProcessId ppid = GetParentProcessId(GetCurrentProcessHandle());
+  EXPECT_EQ(ppid, static_cast<ProcessId>(getppid()));
+}
+#endif  // !defined(OS_FUCHSIA)
+
+#if !defined(OS_ANDROID) && !defined(OS_FUCHSIA)
+class WriteToPipeDelegate : public LaunchOptions::PreExecDelegate {
+ public:
+  explicit WriteToPipeDelegate(int fd) : fd_(fd) {}
+  ~WriteToPipeDelegate() override = default;
+  void RunAsyncSafe() override {
+    RAW_CHECK(HANDLE_EINTR(write(fd_, &kPipeValue, 1)) == 1);
+    RAW_CHECK(IGNORE_EINTR(close(fd_)) == 0);
+  }
+
+ private:
+  int fd_;
+  DISALLOW_COPY_AND_ASSIGN(WriteToPipeDelegate);
+};
+
+TEST_F(ProcessUtilTest, PreExecHook) {
+  int pipe_fds[2];
+  ASSERT_EQ(0, pipe(pipe_fds));
+
+  ScopedFD read_fd(pipe_fds[0]);
+  ScopedFD write_fd(pipe_fds[1]);
+
+  WriteToPipeDelegate write_to_pipe_delegate(write_fd.get());
+  LaunchOptions options;
+  options.fds_to_remap.emplace_back(write_fd.get(), write_fd.get());
+  options.pre_exec_delegate = &write_to_pipe_delegate;
+  Process process(SpawnChildWithOptions("SimpleChildProcess", options));
+  ASSERT_TRUE(process.IsValid());
+
+  write_fd.reset();
+  char c;
+  ASSERT_EQ(1, HANDLE_EINTR(read(read_fd.get(), &c, 1)));
+  EXPECT_EQ(c, kPipeValue);
+
+  int exit_code = 42;
+  EXPECT_TRUE(process.WaitForExit(&exit_code));
+  EXPECT_EQ(0, exit_code);
+}
+#endif  // !defined(OS_ANDROID) && !defined(OS_FUCHSIA)
+
+#endif  // !defined(OS_MACOSX) && !defined(OS_ANDROID)
+
+#if defined(OS_LINUX)
+MULTIPROCESS_TEST_MAIN(CheckPidProcess) {
+  const pid_t kInitPid = 1;
+  const pid_t pid = syscall(__NR_getpid);
+  CHECK(pid == kInitPid);
+  CHECK(getpid() == pid);
+  return kSuccess;
+}
+
+#if defined(CLONE_NEWUSER) && defined(CLONE_NEWPID)
+TEST_F(ProcessUtilTest, CloneFlags) {
+  if (!PathExists(FilePath("/proc/self/ns/user")) ||
+      !PathExists(FilePath("/proc/self/ns/pid"))) {
+    // User or PID namespaces are not supported.
+    return;
+  }
+
+  LaunchOptions options;
+  options.clone_flags = CLONE_NEWUSER | CLONE_NEWPID;
+
+  Process process(SpawnChildWithOptions("CheckPidProcess", options));
+  ASSERT_TRUE(process.IsValid());
+
+  int exit_code = 42;
+  EXPECT_TRUE(process.WaitForExit(&exit_code));
+  EXPECT_EQ(kSuccess, exit_code);
+}
+#endif  // defined(CLONE_NEWUSER) && defined(CLONE_NEWPID)
+
+TEST(ForkWithFlagsTest, UpdatesPidCache) {
+  // Warm up the libc pid cache, if there is one.
+  ASSERT_EQ(syscall(__NR_getpid), getpid());
+
+  pid_t ctid = 0;
+  const pid_t pid = ForkWithFlags(SIGCHLD | CLONE_CHILD_SETTID, nullptr, &ctid);
+  if (pid == 0) {
+    // In child.  Check both the raw getpid syscall and the libc getpid wrapper
+    // (which may rely on a pid cache).
+    RAW_CHECK(syscall(__NR_getpid) == ctid);
+    RAW_CHECK(getpid() == ctid);
+    _exit(kSuccess);
+  }
+
+  ASSERT_NE(-1, pid);
+  int status = 42;
+  ASSERT_EQ(pid, HANDLE_EINTR(waitpid(pid, &status, 0)));
+  ASSERT_TRUE(WIFEXITED(status));
+  EXPECT_EQ(kSuccess, WEXITSTATUS(status));
+}
+
+TEST_F(ProcessUtilTest, InvalidCurrentDirectory) {
+  LaunchOptions options;
+  options.current_directory = FilePath("/dev/null");
+
+  Process process(SpawnChildWithOptions("SimpleChildProcess", options));
+  ASSERT_TRUE(process.IsValid());
+
+  int exit_code = kSuccess;
+  EXPECT_TRUE(process.WaitForExit(&exit_code));
+  EXPECT_NE(kSuccess, exit_code);
+}
+#endif  // defined(OS_LINUX)
+
+}  // namespace base
diff --git a/base/profiler/native_stack_sampler.cc b/base/profiler/native_stack_sampler.cc
new file mode 100644
index 0000000..6eed54f
--- /dev/null
+++ b/base/profiler/native_stack_sampler.cc
@@ -0,0 +1,34 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/profiler/native_stack_sampler.h"
+
+#include "base/memory/ptr_util.h"
+
+namespace base {
+
+NativeStackSampler::StackBuffer::StackBuffer(size_t buffer_size)
+    : buffer_(new uintptr_t[(buffer_size + sizeof(uintptr_t) - 1) /
+                            sizeof(uintptr_t)]),
+      size_(buffer_size) {}
+
+NativeStackSampler::StackBuffer::~StackBuffer() = default;
+
+NativeStackSampler::NativeStackSampler() = default;
+
+NativeStackSampler::~NativeStackSampler() = default;
+
+std::unique_ptr<NativeStackSampler::StackBuffer>
+NativeStackSampler::CreateStackBuffer() {
+  size_t size = GetStackBufferSize();
+  if (size == 0)
+    return nullptr;
+  return std::make_unique<StackBuffer>(size);
+}
+
+NativeStackSamplerTestDelegate::~NativeStackSamplerTestDelegate() = default;
+
+NativeStackSamplerTestDelegate::NativeStackSamplerTestDelegate() = default;
+
+}  // namespace base
diff --git a/base/profiler/native_stack_sampler.h b/base/profiler/native_stack_sampler.h
new file mode 100644
index 0000000..5d7e9b0
--- /dev/null
+++ b/base/profiler/native_stack_sampler.h
@@ -0,0 +1,97 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_PROFILER_NATIVE_STACK_SAMPLER_H_
+#define BASE_PROFILER_NATIVE_STACK_SAMPLER_H_
+
+#include <memory>
+
+#include "base/base_export.h"
+#include "base/macros.h"
+#include "base/profiler/stack_sampling_profiler.h"
+#include "base/threading/platform_thread.h"
+
+namespace base {
+
+class NativeStackSamplerTestDelegate;
+
+// NativeStackSampler is an implementation detail of StackSamplingProfiler. It
+// abstracts the native implementation required to record a set of stack frames
+// for a given thread.
+class NativeStackSampler {
+ public:
+  // This class contains a buffer for stack copies that can be shared across
+  // multiple instances of NativeStackSampler.
+  class StackBuffer {
+   public:
+    StackBuffer(size_t buffer_size);
+    ~StackBuffer();
+
+    void* buffer() const { return buffer_.get(); }
+    size_t size() const { return size_; }
+
+   private:
+    // The word-aligned buffer.
+    const std::unique_ptr<uintptr_t[]> buffer_;
+
+    // The size of the buffer.
+    const size_t size_;
+
+    DISALLOW_COPY_AND_ASSIGN(StackBuffer);
+  };
+
+  virtual ~NativeStackSampler();
+
+  // Creates a stack sampler that records samples for thread with |thread_id|.
+  // Returns null if this platform does not support stack sampling.
+  static std::unique_ptr<NativeStackSampler> Create(
+      PlatformThreadId thread_id,
+      NativeStackSamplerTestDelegate* test_delegate);
+
+  // Gets the required size of the stack buffer.
+  static size_t GetStackBufferSize();
+
+  // Creates an instance of the a stack buffer that can be used for calls to
+  // any NativeStackSampler object.
+  static std::unique_ptr<StackBuffer> CreateStackBuffer();
+
+  // The following functions are all called on the SamplingThread (not the
+  // thread being sampled).
+
+  // Notifies the sampler that we're starting to record a new profile.
+  virtual void ProfileRecordingStarting() = 0;
+
+  // Records a set of internal frames and returns them.
+  virtual std::vector<StackSamplingProfiler::InternalFrame> RecordStackFrames(
+      StackBuffer* stackbuffer,
+      StackSamplingProfiler::ProfileBuilder* profile_builder) = 0;
+
+ protected:
+  NativeStackSampler();
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(NativeStackSampler);
+};
+
+// NativeStackSamplerTestDelegate provides seams for test code to execute during
+// stack collection.
+class BASE_EXPORT NativeStackSamplerTestDelegate {
+ public:
+  virtual ~NativeStackSamplerTestDelegate();
+
+  // Called after copying the stack and resuming the target thread, but prior to
+  // walking the stack. Invoked on the SamplingThread.
+  virtual void OnPreStackWalk() = 0;
+
+ protected:
+  NativeStackSamplerTestDelegate();
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(NativeStackSamplerTestDelegate);
+};
+
+}  // namespace base
+
+#endif  // BASE_PROFILER_NATIVE_STACK_SAMPLER_H_
+
diff --git a/base/profiler/native_stack_sampler_posix.cc b/base/profiler/native_stack_sampler_posix.cc
new file mode 100644
index 0000000..fdc18e0
--- /dev/null
+++ b/base/profiler/native_stack_sampler_posix.cc
@@ -0,0 +1,19 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/profiler/native_stack_sampler.h"
+
+namespace base {
+
+std::unique_ptr<NativeStackSampler> NativeStackSampler::Create(
+    PlatformThreadId thread_id,
+    NativeStackSamplerTestDelegate* test_delegate) {
+  return std::unique_ptr<NativeStackSampler>();
+}
+
+size_t NativeStackSampler::GetStackBufferSize() {
+  return 0;
+}
+
+}  // namespace base
diff --git a/base/profiler/stack_sampling_profiler.cc b/base/profiler/stack_sampling_profiler.cc
new file mode 100644
index 0000000..02df814
--- /dev/null
+++ b/base/profiler/stack_sampling_profiler.cc
@@ -0,0 +1,808 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/profiler/stack_sampling_profiler.h"
+
+#include <algorithm>
+#include <utility>
+
+#include "base/atomic_sequence_num.h"
+#include "base/atomicops.h"
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback.h"
+#include "base/location.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/singleton.h"
+#include "base/profiler/native_stack_sampler.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/thread.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/timer/elapsed_timer.h"
+
+namespace base {
+
+const size_t kUnknownModuleIndex = static_cast<size_t>(-1);
+
+namespace {
+
+// This value is used to initialize the WaitableEvent object. This MUST BE set
+// to MANUAL for correct operation of the IsSignaled() call in Start(). See the
+// comment there for why.
+constexpr WaitableEvent::ResetPolicy kResetPolicy =
+    WaitableEvent::ResetPolicy::MANUAL;
+
+// This value is used when there is no collection in progress and thus no ID
+// for referencing the active collection to the SamplingThread.
+const int kNullProfilerId = -1;
+
+}  // namespace
+
+// StackSamplingProfiler::Module ----------------------------------------------
+
+StackSamplingProfiler::Module::Module() : base_address(0u) {}
+
+StackSamplingProfiler::Module::Module(uintptr_t base_address,
+                                      const std::string& id,
+                                      const FilePath& filename)
+    : base_address(base_address), id(id), filename(filename) {}
+
+StackSamplingProfiler::Module::~Module() = default;
+
+// StackSamplingProfiler::InternalModule --------------------------------------
+
+StackSamplingProfiler::InternalModule::InternalModule() : is_valid(false) {}
+
+StackSamplingProfiler::InternalModule::InternalModule(uintptr_t base_address,
+                                                      const std::string& id,
+                                                      const FilePath& filename)
+    : base_address(base_address), id(id), filename(filename), is_valid(true) {}
+
+StackSamplingProfiler::InternalModule::~InternalModule() = default;
+
+// StackSamplingProfiler::Frame -----------------------------------------------
+
+StackSamplingProfiler::Frame::Frame(uintptr_t instruction_pointer,
+                                    size_t module_index)
+    : instruction_pointer(instruction_pointer), module_index(module_index) {}
+
+StackSamplingProfiler::Frame::~Frame() = default;
+
+StackSamplingProfiler::Frame::Frame()
+    : instruction_pointer(0), module_index(kUnknownModuleIndex) {}
+
+// StackSamplingProfiler::InternalFrame -------------------------------------
+
+StackSamplingProfiler::InternalFrame::InternalFrame(
+    uintptr_t instruction_pointer,
+    InternalModule internal_module)
+    : instruction_pointer(instruction_pointer),
+      internal_module(std::move(internal_module)) {}
+
+StackSamplingProfiler::InternalFrame::~InternalFrame() = default;
+
+// StackSamplingProfiler::Sample ----------------------------------------------
+
+StackSamplingProfiler::Sample::Sample() = default;
+
+StackSamplingProfiler::Sample::Sample(const Sample& sample) = default;
+
+StackSamplingProfiler::Sample::~Sample() = default;
+
+StackSamplingProfiler::Sample::Sample(const Frame& frame) {
+  frames.push_back(std::move(frame));
+}
+
+StackSamplingProfiler::Sample::Sample(const std::vector<Frame>& frames)
+    : frames(frames) {}
+
+// StackSamplingProfiler::CallStackProfile ------------------------------------
+
+StackSamplingProfiler::CallStackProfile::CallStackProfile() = default;
+
+StackSamplingProfiler::CallStackProfile::CallStackProfile(
+    CallStackProfile&& other) = default;
+
+StackSamplingProfiler::CallStackProfile::~CallStackProfile() = default;
+
+StackSamplingProfiler::CallStackProfile&
+StackSamplingProfiler::CallStackProfile::operator=(CallStackProfile&& other) =
+    default;
+
+StackSamplingProfiler::CallStackProfile
+StackSamplingProfiler::CallStackProfile::CopyForTesting() const {
+  return CallStackProfile(*this);
+}
+
+StackSamplingProfiler::CallStackProfile::CallStackProfile(
+    const CallStackProfile& other) = default;
+
+// StackSamplingProfiler::SamplingThread --------------------------------------
+
+class StackSamplingProfiler::SamplingThread : public Thread {
+ public:
+  class TestAPI {
+   public:
+    // Reset the existing sampler. This will unfortunately create the object
+    // unnecessarily if it doesn't already exist but there's no way around that.
+    static void Reset();
+
+    // Disables inherent idle-shutdown behavior.
+    static void DisableIdleShutdown();
+
+    // Begins an idle shutdown as if the idle-timer had expired and wait for
+    // it to execute. Since the timer would have only been started at a time
+    // when the sampling thread actually was idle, this must be called only
+    // when it is known that there are no active sampling threads. If
+    // |simulate_intervening_add| is true then, when executed, the shutdown
+    // task will believe that a new collection has been added since it was
+    // posted.
+    static void ShutdownAssumingIdle(bool simulate_intervening_add);
+
+   private:
+    // Calls the sampling threads ShutdownTask and then signals an event.
+    static void ShutdownTaskAndSignalEvent(SamplingThread* sampler,
+                                           int add_events,
+                                           WaitableEvent* event);
+  };
+
+  struct CollectionContext {
+    CollectionContext(PlatformThreadId target,
+                      const SamplingParams& params,
+                      WaitableEvent* finished,
+                      std::unique_ptr<NativeStackSampler> sampler,
+                      std::unique_ptr<ProfileBuilder> profile_builder)
+        : collection_id(next_collection_id.GetNext()),
+          target(target),
+          params(params),
+          finished(finished),
+          native_sampler(std::move(sampler)),
+          profile_builder(std::move(profile_builder)) {}
+    ~CollectionContext() = default;
+
+    // An identifier for this collection, used to uniquely identify the
+    // collection to outside interests.
+    const int collection_id;
+
+    const PlatformThreadId target;  // ID of The thread being sampled.
+    const SamplingParams params;    // Information about how to sample.
+    WaitableEvent* const finished;  // Signaled when all sampling complete.
+
+    // Platform-specific module that does the actual sampling.
+    std::unique_ptr<NativeStackSampler> native_sampler;
+
+    // Receives the sampling data and builds a CallStackProfile.
+    std::unique_ptr<ProfileBuilder> profile_builder;
+
+    // The absolute time for the next sample.
+    Time next_sample_time;
+
+    // The time that a profile was started, for calculating the total duration.
+    Time profile_start_time;
+
+    // Counter that indicates the current sample position along the acquisition.
+    int sample_count = 0;
+
+    // Sequence number for generating new collection ids.
+    static AtomicSequenceNumber next_collection_id;
+  };
+
+  // Gets the single instance of this class.
+  static SamplingThread* GetInstance();
+
+  // Adds a new CollectionContext to the thread. This can be called externally
+  // from any thread. This returns a collection id that can later be used to
+  // stop the sampling.
+  int Add(std::unique_ptr<CollectionContext> collection);
+
+  // Removes an active collection based on its collection id, forcing it to run
+  // its callback if any data has been collected. This can be called externally
+  // from any thread.
+  void Remove(int collection_id);
+
+ private:
+  friend class TestAPI;
+  friend struct DefaultSingletonTraits<SamplingThread>;
+
+  // The different states in which the sampling-thread can be.
+  enum ThreadExecutionState {
+    // The thread is not running because it has never been started. It will be
+    // started when a sampling request is received.
+    NOT_STARTED,
+
+    // The thread is running and processing tasks. This is the state when any
+    // sampling requests are active and during the "idle" period afterward
+    // before the thread is stopped.
+    RUNNING,
+
+    // Once all sampling requests have finished and the "idle" period has
+    // expired, the thread will be set to this state and its shutdown
+    // initiated. A call to Stop() must be made to ensure the previous thread
+    // has completely exited before calling Start() and moving back to the
+    // RUNNING state.
+    EXITING,
+  };
+
+  SamplingThread();
+  ~SamplingThread() override;
+
+  // Get task runner that is usable from the outside.
+  scoped_refptr<SingleThreadTaskRunner> GetOrCreateTaskRunnerForAdd();
+  scoped_refptr<SingleThreadTaskRunner> GetTaskRunner(
+      ThreadExecutionState* out_state);
+
+  // Get task runner that is usable from the sampling thread itself.
+  scoped_refptr<SingleThreadTaskRunner> GetTaskRunnerOnSamplingThread();
+
+  // Finishes a collection. The collection's |finished| waitable event will be
+  // signalled. The |collection| should already have been removed from
+  // |active_collections_| by the caller, as this is needed to avoid flakiness
+  // in unit tests.
+  void FinishCollection(CollectionContext* collection);
+
+  // Check if the sampling thread is idle and begin a shutdown if it is.
+  void ScheduleShutdownIfIdle();
+
+  // These methods are tasks that get posted to the internal message queue.
+  void AddCollectionTask(std::unique_ptr<CollectionContext> collection);
+  void RemoveCollectionTask(int collection_id);
+  void RecordSampleTask(int collection_id);
+  void ShutdownTask(int add_events);
+
+  // Thread:
+  void CleanUp() override;
+
+  // A stack-buffer used by the native sampler for its work. This buffer can
+  // be re-used for multiple native sampler objects so long as the API calls
+  // that take it are not called concurrently.
+  std::unique_ptr<NativeStackSampler::StackBuffer> stack_buffer_;
+
+  // A map of collection ids to collection contexts. Because this class is a
+  // singleton that is never destroyed, context objects will never be destructed
+  // except by explicit action. Thus, it's acceptable to pass unretained
+  // pointers to these objects when posting tasks.
+  std::map<int, std::unique_ptr<CollectionContext>> active_collections_;
+
+  // State maintained about the current execution (or non-execution) of
+  // the thread. This state must always be accessed while holding the
+  // lock. A copy of the task-runner is maintained here for use by any
+  // calling thread; this is necessary because Thread's accessor for it is
+  // not itself thread-safe. The lock is also used to order calls to the
+  // Thread API (Start, Stop, StopSoon, & DetachFromSequence) so that
+  // multiple threads may make those calls.
+  Lock thread_execution_state_lock_;  // Protects all thread_execution_state_*
+  ThreadExecutionState thread_execution_state_ = NOT_STARTED;
+  scoped_refptr<SingleThreadTaskRunner> thread_execution_state_task_runner_;
+  bool thread_execution_state_disable_idle_shutdown_for_testing_ = false;
+
+  // A counter that notes adds of new collection requests. It is incremented
+  // when changes occur so that delayed shutdown tasks are able to detect if
+  // something new has happened while it was waiting. Like all "execution_state"
+  // vars, this must be accessed while holding |thread_execution_state_lock_|.
+  int thread_execution_state_add_events_ = 0;
+
+  DISALLOW_COPY_AND_ASSIGN(SamplingThread);
+};
+
+// static
+void StackSamplingProfiler::SamplingThread::TestAPI::Reset() {
+  SamplingThread* sampler = SamplingThread::GetInstance();
+
+  ThreadExecutionState state;
+  {
+    AutoLock lock(sampler->thread_execution_state_lock_);
+    state = sampler->thread_execution_state_;
+    DCHECK(sampler->active_collections_.empty());
+  }
+
+  // Stop the thread and wait for it to exit. This has to be done through by
+  // the thread itself because it has taken ownership of its own lifetime.
+  if (state == RUNNING) {
+    ShutdownAssumingIdle(false);
+    state = EXITING;
+  }
+  // Make sure thread is cleaned up since state will be reset to NOT_STARTED.
+  if (state == EXITING)
+    sampler->Stop();
+
+  // Reset internal variables to the just-initialized state.
+  {
+    AutoLock lock(sampler->thread_execution_state_lock_);
+    sampler->thread_execution_state_ = NOT_STARTED;
+    sampler->thread_execution_state_task_runner_ = nullptr;
+    sampler->thread_execution_state_disable_idle_shutdown_for_testing_ = false;
+    sampler->thread_execution_state_add_events_ = 0;
+  }
+}
+
+// static
+void StackSamplingProfiler::SamplingThread::TestAPI::DisableIdleShutdown() {
+  SamplingThread* sampler = SamplingThread::GetInstance();
+
+  {
+    AutoLock lock(sampler->thread_execution_state_lock_);
+    sampler->thread_execution_state_disable_idle_shutdown_for_testing_ = true;
+  }
+}
+
+// static
+void StackSamplingProfiler::SamplingThread::TestAPI::ShutdownAssumingIdle(
+    bool simulate_intervening_add) {
+  SamplingThread* sampler = SamplingThread::GetInstance();
+
+  ThreadExecutionState state;
+  scoped_refptr<SingleThreadTaskRunner> task_runner =
+      sampler->GetTaskRunner(&state);
+  DCHECK_EQ(RUNNING, state);
+  DCHECK(task_runner);
+
+  int add_events;
+  {
+    AutoLock lock(sampler->thread_execution_state_lock_);
+    add_events = sampler->thread_execution_state_add_events_;
+    if (simulate_intervening_add)
+      ++sampler->thread_execution_state_add_events_;
+  }
+
+  WaitableEvent executed(WaitableEvent::ResetPolicy::MANUAL,
+                         WaitableEvent::InitialState::NOT_SIGNALED);
+  // PostTaskAndReply won't work because thread and associated message-loop may
+  // be shut down.
+  task_runner->PostTask(
+      FROM_HERE, BindOnce(&ShutdownTaskAndSignalEvent, Unretained(sampler),
+                          add_events, Unretained(&executed)));
+  executed.Wait();
+}
+
+// static
+void StackSamplingProfiler::SamplingThread::TestAPI::ShutdownTaskAndSignalEvent(
+    SamplingThread* sampler,
+    int add_events,
+    WaitableEvent* event) {
+  sampler->ShutdownTask(add_events);
+  event->Signal();
+}
+
+AtomicSequenceNumber StackSamplingProfiler::SamplingThread::CollectionContext::
+    next_collection_id;
+
+StackSamplingProfiler::SamplingThread::SamplingThread()
+    : Thread("StackSamplingProfiler") {}
+
+StackSamplingProfiler::SamplingThread::~SamplingThread() = default;
+
+StackSamplingProfiler::SamplingThread*
+StackSamplingProfiler::SamplingThread::GetInstance() {
+  return Singleton<SamplingThread, LeakySingletonTraits<SamplingThread>>::get();
+}
+
+int StackSamplingProfiler::SamplingThread::Add(
+    std::unique_ptr<CollectionContext> collection) {
+  // This is not to be run on the sampling thread.
+
+  int collection_id = collection->collection_id;
+  scoped_refptr<SingleThreadTaskRunner> task_runner =
+      GetOrCreateTaskRunnerForAdd();
+
+  task_runner->PostTask(
+      FROM_HERE, BindOnce(&SamplingThread::AddCollectionTask, Unretained(this),
+                          std::move(collection)));
+
+  return collection_id;
+}
+
+void StackSamplingProfiler::SamplingThread::Remove(int collection_id) {
+  // This is not to be run on the sampling thread.
+
+  ThreadExecutionState state;
+  scoped_refptr<SingleThreadTaskRunner> task_runner = GetTaskRunner(&state);
+  if (state != RUNNING)
+    return;
+  DCHECK(task_runner);
+
+  // This can fail if the thread were to exit between acquisition of the task
+  // runner above and the call below. In that case, however, everything has
+  // stopped so there's no need to try to stop it.
+  task_runner->PostTask(FROM_HERE,
+                        BindOnce(&SamplingThread::RemoveCollectionTask,
+                                 Unretained(this), collection_id));
+}
+
+scoped_refptr<SingleThreadTaskRunner>
+StackSamplingProfiler::SamplingThread::GetOrCreateTaskRunnerForAdd() {
+  AutoLock lock(thread_execution_state_lock_);
+
+  // The increment of the "add events" count is why this method is to be only
+  // called from "add".
+  ++thread_execution_state_add_events_;
+
+  if (thread_execution_state_ == RUNNING) {
+    DCHECK(thread_execution_state_task_runner_);
+    // This shouldn't be called from the sampling thread as it's inefficient.
+    // Use GetTaskRunnerOnSamplingThread() instead.
+    DCHECK_NE(GetThreadId(), PlatformThread::CurrentId());
+    return thread_execution_state_task_runner_;
+  }
+
+  if (thread_execution_state_ == EXITING) {
+    // StopSoon() was previously called to shut down the thread
+    // asynchonously. Stop() must now be called before calling Start() again to
+    // reset the thread state.
+    //
+    // We must allow blocking here to satisfy the Thread implementation, but in
+    // practice the Stop() call is unlikely to actually block. For this to
+    // happen a new profiling request would have to be made within the narrow
+    // window between StopSoon() and thread exit following the end of the 60
+    // second idle period.
+    ScopedAllowBlocking allow_blocking;
+    Stop();
+  }
+
+  DCHECK(!stack_buffer_);
+  stack_buffer_ = NativeStackSampler::CreateStackBuffer();
+
+  // The thread is not running. Start it and get associated runner. The task-
+  // runner has to be saved for future use because though it can be used from
+  // any thread, it can be acquired via task_runner() only on the created
+  // thread and the thread that creates it (i.e. this thread) for thread-safety
+  // reasons which are alleviated in SamplingThread by gating access to it with
+  // the |thread_execution_state_lock_|.
+  Start();
+  thread_execution_state_ = RUNNING;
+  thread_execution_state_task_runner_ = Thread::task_runner();
+
+  // Detach the sampling thread from the "sequence" (i.e. thread) that
+  // started it so that it can be self-managed or stopped by another thread.
+  DetachFromSequence();
+
+  return thread_execution_state_task_runner_;
+}
+
+scoped_refptr<SingleThreadTaskRunner>
+StackSamplingProfiler::SamplingThread::GetTaskRunner(
+    ThreadExecutionState* out_state) {
+  AutoLock lock(thread_execution_state_lock_);
+  if (out_state)
+    *out_state = thread_execution_state_;
+  if (thread_execution_state_ == RUNNING) {
+    // This shouldn't be called from the sampling thread as it's inefficient.
+    // Use GetTaskRunnerOnSamplingThread() instead.
+    DCHECK_NE(GetThreadId(), PlatformThread::CurrentId());
+    DCHECK(thread_execution_state_task_runner_);
+  } else {
+    DCHECK(!thread_execution_state_task_runner_);
+  }
+
+  return thread_execution_state_task_runner_;
+}
+
+scoped_refptr<SingleThreadTaskRunner>
+StackSamplingProfiler::SamplingThread::GetTaskRunnerOnSamplingThread() {
+  // This should be called only from the sampling thread as it has limited
+  // accessibility.
+  DCHECK_EQ(GetThreadId(), PlatformThread::CurrentId());
+
+  return Thread::task_runner();
+}
+
+void StackSamplingProfiler::SamplingThread::FinishCollection(
+    CollectionContext* collection) {
+  DCHECK_EQ(GetThreadId(), PlatformThread::CurrentId());
+  DCHECK_EQ(0u, active_collections_.count(collection->collection_id));
+
+  TimeDelta profile_duration = Time::Now() - collection->profile_start_time +
+                               collection->params.sampling_interval;
+
+  collection->profile_builder->OnProfileCompleted(
+      profile_duration, collection->params.sampling_interval);
+
+  // Signal that this collection is finished.
+  collection->finished->Signal();
+
+  ScheduleShutdownIfIdle();
+}
+
+void StackSamplingProfiler::SamplingThread::ScheduleShutdownIfIdle() {
+  DCHECK_EQ(GetThreadId(), PlatformThread::CurrentId());
+
+  if (!active_collections_.empty())
+    return;
+
+  int add_events;
+  {
+    AutoLock lock(thread_execution_state_lock_);
+    if (thread_execution_state_disable_idle_shutdown_for_testing_)
+      return;
+    add_events = thread_execution_state_add_events_;
+  }
+
+  GetTaskRunnerOnSamplingThread()->PostDelayedTask(
+      FROM_HERE,
+      BindOnce(&SamplingThread::ShutdownTask, Unretained(this), add_events),
+      TimeDelta::FromSeconds(60));
+}
+
+void StackSamplingProfiler::SamplingThread::AddCollectionTask(
+    std::unique_ptr<CollectionContext> collection) {
+  DCHECK_EQ(GetThreadId(), PlatformThread::CurrentId());
+
+  const int collection_id = collection->collection_id;
+  const TimeDelta initial_delay = collection->params.initial_delay;
+
+  active_collections_.insert(
+      std::make_pair(collection_id, std::move(collection)));
+
+  GetTaskRunnerOnSamplingThread()->PostDelayedTask(
+      FROM_HERE,
+      BindOnce(&SamplingThread::RecordSampleTask, Unretained(this),
+               collection_id),
+      initial_delay);
+
+  // Another increment of "add events" serves to invalidate any pending
+  // shutdown tasks that may have been initiated between the Add() and this
+  // task running.
+  {
+    AutoLock lock(thread_execution_state_lock_);
+    ++thread_execution_state_add_events_;
+  }
+}
+
+void StackSamplingProfiler::SamplingThread::RemoveCollectionTask(
+    int collection_id) {
+  DCHECK_EQ(GetThreadId(), PlatformThread::CurrentId());
+
+  auto found = active_collections_.find(collection_id);
+  if (found == active_collections_.end())
+    return;
+
+  // Remove |collection| from |active_collections_|.
+  std::unique_ptr<CollectionContext> collection = std::move(found->second);
+  size_t count = active_collections_.erase(collection_id);
+  DCHECK_EQ(1U, count);
+
+  FinishCollection(collection.get());
+}
+
+void StackSamplingProfiler::SamplingThread::RecordSampleTask(
+    int collection_id) {
+  DCHECK_EQ(GetThreadId(), PlatformThread::CurrentId());
+
+  auto found = active_collections_.find(collection_id);
+
+  // The task won't be found if it has been stopped.
+  if (found == active_collections_.end())
+    return;
+
+  CollectionContext* collection = found->second.get();
+
+  // If this is the first sample, the collection params need to be filled.
+  if (collection->sample_count == 0) {
+    collection->profile_start_time = Time::Now();
+    collection->next_sample_time = Time::Now();
+    collection->native_sampler->ProfileRecordingStarting();
+  }
+
+  // Record a single sample.
+  collection->profile_builder->OnSampleCompleted(
+      collection->native_sampler->RecordStackFrames(
+          stack_buffer_.get(), collection->profile_builder.get()));
+
+  // Schedule the next sample recording if there is one.
+  if (++collection->sample_count < collection->params.samples_per_profile) {
+    // This will keep a consistent average interval between samples but will
+    // result in constant series of acquisitions, thus nearly locking out the
+    // target thread, if the interval is smaller than the time it takes to
+    // actually acquire the sample. Anything sampling that quickly is going
+    // to be a problem anyway so don't worry about it.
+    collection->next_sample_time += collection->params.sampling_interval;
+    bool success = GetTaskRunnerOnSamplingThread()->PostDelayedTask(
+        FROM_HERE,
+        BindOnce(&SamplingThread::RecordSampleTask, Unretained(this),
+                 collection_id),
+        std::max(collection->next_sample_time - Time::Now(), TimeDelta()));
+    DCHECK(success);
+    return;
+  }
+
+  // Take ownership of |collection| and remove it from the map.
+  std::unique_ptr<CollectionContext> owned_collection =
+      std::move(found->second);
+  size_t count = active_collections_.erase(collection_id);
+  DCHECK_EQ(1U, count);
+
+  // All capturing has completed so finish the collection.
+  FinishCollection(collection);
+}
+
+void StackSamplingProfiler::SamplingThread::ShutdownTask(int add_events) {
+  DCHECK_EQ(GetThreadId(), PlatformThread::CurrentId());
+
+  // Holding this lock ensures that any attempt to start another job will
+  // get postponed until |thread_execution_state_| is updated, thus eliminating
+  // the race in starting a new thread while the previous one is exiting.
+  AutoLock lock(thread_execution_state_lock_);
+
+  // If the current count of creation requests doesn't match the passed count
+  // then other tasks have been created since this was posted. Abort shutdown.
+  if (thread_execution_state_add_events_ != add_events)
+    return;
+
+  // There can be no new AddCollectionTasks at this point because creating
+  // those always increments "add events". There may be other requests, like
+  // Remove, but it's okay to schedule the thread to stop once they've been
+  // executed (i.e. "soon").
+  DCHECK(active_collections_.empty());
+  StopSoon();
+
+  // StopSoon will have set the owning sequence (again) so it must be detached
+  // (again) in order for Stop/Start to be called (again) should more work
+  // come in. Holding the |thread_execution_state_lock_| ensures the necessary
+  // happens-after with regard to this detach and future Thread API calls.
+  DetachFromSequence();
+
+  // Set the thread_state variable so the thread will be restarted when new
+  // work comes in. Remove the |thread_execution_state_task_runner_| to avoid
+  // confusion.
+  thread_execution_state_ = EXITING;
+  thread_execution_state_task_runner_ = nullptr;
+  stack_buffer_.reset();
+}
+
+void StackSamplingProfiler::SamplingThread::CleanUp() {
+  DCHECK_EQ(GetThreadId(), PlatformThread::CurrentId());
+
+  // There should be no collections remaining when the thread stops.
+  DCHECK(active_collections_.empty());
+
+  // Let the parent clean up.
+  Thread::CleanUp();
+}
+
+// StackSamplingProfiler ------------------------------------------------------
+
+// static
+void StackSamplingProfiler::TestAPI::Reset() {
+  SamplingThread::TestAPI::Reset();
+}
+
+// static
+bool StackSamplingProfiler::TestAPI::IsSamplingThreadRunning() {
+  return SamplingThread::GetInstance()->IsRunning();
+}
+
+// static
+void StackSamplingProfiler::TestAPI::DisableIdleShutdown() {
+  SamplingThread::TestAPI::DisableIdleShutdown();
+}
+
+// static
+void StackSamplingProfiler::TestAPI::PerformSamplingThreadIdleShutdown(
+    bool simulate_intervening_start) {
+  SamplingThread::TestAPI::ShutdownAssumingIdle(simulate_intervening_start);
+}
+
+StackSamplingProfiler::StackSamplingProfiler(
+    const SamplingParams& params,
+    std::unique_ptr<ProfileBuilder> profile_builder,
+    NativeStackSamplerTestDelegate* test_delegate)
+    : StackSamplingProfiler(PlatformThread::CurrentId(),
+                            params,
+                            std::move(profile_builder),
+                            test_delegate) {}
+
+StackSamplingProfiler::StackSamplingProfiler(
+    PlatformThreadId thread_id,
+    const SamplingParams& params,
+    std::unique_ptr<ProfileBuilder> profile_builder,
+    NativeStackSamplerTestDelegate* test_delegate)
+    : thread_id_(thread_id),
+      params_(params),
+      profile_builder_(std::move(profile_builder)),
+      // The event starts "signaled" so code knows it's safe to start thread
+      // and "manual" so that it can be waited in multiple places.
+      profiling_inactive_(kResetPolicy, WaitableEvent::InitialState::SIGNALED),
+      profiler_id_(kNullProfilerId),
+      test_delegate_(test_delegate) {
+  DCHECK(profile_builder_);
+}
+
+StackSamplingProfiler::~StackSamplingProfiler() {
+  // Stop returns immediately but the shutdown runs asynchronously. There is a
+  // non-zero probability that one more sample will be taken after this call
+  // returns.
+  Stop();
+
+  // The behavior of sampling a thread that has exited is undefined and could
+  // cause Bad Things(tm) to occur. The safety model provided by this class is
+  // that an instance of this object is expected to live at least as long as
+  // the thread it is sampling. However, because the sampling is performed
+  // asynchronously by the SamplingThread, there is no way to guarantee this
+  // is true without waiting for it to signal that it has finished.
+  //
+  // The wait time should, at most, be only as long as it takes to collect one
+  // sample (~200us) or none at all if sampling has already completed.
+  ThreadRestrictions::ScopedAllowWait allow_wait;
+  profiling_inactive_.Wait();
+}
+
+void StackSamplingProfiler::Start() {
+  // Multiple calls to Start() for a single StackSamplingProfiler object is not
+  // allowed. If profile_builder_ is nullptr, then Start() has been called
+  // already.
+  DCHECK(profile_builder_);
+
+  std::unique_ptr<NativeStackSampler> native_sampler =
+      NativeStackSampler::Create(thread_id_, test_delegate_);
+
+  if (!native_sampler)
+    return;
+
+  // The IsSignaled() check below requires that the WaitableEvent be manually
+  // reset, to avoid signaling the event in IsSignaled() itself.
+  static_assert(kResetPolicy == WaitableEvent::ResetPolicy::MANUAL,
+                "The reset policy must be set to MANUAL");
+
+  // If a previous profiling phase is still winding down, wait for it to
+  // complete. We can't use task posting for this coordination because the
+  // thread owning the profiler may not have a message loop.
+  if (!profiling_inactive_.IsSignaled())
+    profiling_inactive_.Wait();
+  profiling_inactive_.Reset();
+
+  DCHECK_EQ(kNullProfilerId, profiler_id_);
+  profiler_id_ = SamplingThread::GetInstance()->Add(
+      std::make_unique<SamplingThread::CollectionContext>(
+          thread_id_, params_, &profiling_inactive_, std::move(native_sampler),
+          std::move(profile_builder_)));
+  DCHECK_NE(kNullProfilerId, profiler_id_);
+}
+
+void StackSamplingProfiler::Stop() {
+  SamplingThread::GetInstance()->Remove(profiler_id_);
+  profiler_id_ = kNullProfilerId;
+}
+
+// StackSamplingProfiler::Frame global functions ------------------------------
+
+bool operator==(const StackSamplingProfiler::Module& a,
+                const StackSamplingProfiler::Module& b) {
+  return a.base_address == b.base_address && a.id == b.id &&
+         a.filename == b.filename;
+}
+
+bool operator==(const StackSamplingProfiler::Sample& a,
+                const StackSamplingProfiler::Sample& b) {
+  return a.process_milestones == b.process_milestones && a.frames == b.frames;
+}
+
+bool operator!=(const StackSamplingProfiler::Sample& a,
+                const StackSamplingProfiler::Sample& b) {
+  return !(a == b);
+}
+
+bool operator<(const StackSamplingProfiler::Sample& a,
+               const StackSamplingProfiler::Sample& b) {
+  if (a.process_milestones != b.process_milestones)
+    return a.process_milestones < b.process_milestones;
+
+  return a.frames < b.frames;
+}
+
+bool operator==(const StackSamplingProfiler::Frame& a,
+                const StackSamplingProfiler::Frame& b) {
+  return a.instruction_pointer == b.instruction_pointer &&
+         a.module_index == b.module_index;
+}
+
+bool operator<(const StackSamplingProfiler::Frame& a,
+               const StackSamplingProfiler::Frame& b) {
+  if (a.module_index != b.module_index)
+    return a.module_index < b.module_index;
+
+  return a.instruction_pointer < b.instruction_pointer;
+}
+
+}  // namespace base
diff --git a/base/profiler/stack_sampling_profiler.h b/base/profiler/stack_sampling_profiler.h
new file mode 100644
index 0000000..e43349a
--- /dev/null
+++ b/base/profiler/stack_sampling_profiler.h
@@ -0,0 +1,354 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_PROFILER_STACK_SAMPLING_PROFILER_H_
+#define BASE_PROFILER_STACK_SAMPLING_PROFILER_H_
+
+#include <stddef.h>
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/base_export.h"
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "base/strings/string16.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/platform_thread.h"
+#include "base/time/time.h"
+
+namespace base {
+
+// Identifies an unknown module.
+BASE_EXPORT extern const size_t kUnknownModuleIndex;
+
+class NativeStackSamplerTestDelegate;
+
+// StackSamplingProfiler periodically stops a thread to sample its stack, for
+// the purpose of collecting information about which code paths are
+// executing. This information is used in aggregate by UMA to identify hot
+// and/or janky code paths.
+//
+// Sample StackSamplingProfiler usage:
+//
+//   // Create and customize params as desired.
+//   base::StackStackSamplingProfiler::SamplingParams params;
+//
+//   // To process the profiles, use a custom ProfileBuilder subclass:
+//   class SubProfileBuilder :
+//       public base::StackSamplingProfiler::ProfileBuilder{...}
+//   base::StackSamplingProfiler profiler(base::PlatformThread::CurrentId()),
+//       params, std::make_unique<SubProfileBuilder>(...));
+//
+//   profiler.Start();
+//   // ... work being done on the target thread here ...
+//   profiler.Stop();  // optional, stops collection before complete per params
+//
+// The default SamplingParams causes stacks to be recorded in a single profile
+// at a 10Hz interval for a total of 30 seconds. All of these parameters may be
+// altered as desired.
+//
+// When a call stack profile is complete, or the profiler is stopped,
+// ProfileBuilder's OnProfileCompleted function is called from a thread created
+// by the profiler.
+class BASE_EXPORT StackSamplingProfiler {
+ public:
+  // Module represents the module (DLL or exe) corresponding to a stack frame.
+  struct BASE_EXPORT Module {
+    Module();
+    Module(uintptr_t base_address,
+           const std::string& id,
+           const FilePath& filename);
+    ~Module();
+
+    // Points to the base address of the module.
+    uintptr_t base_address;
+
+    // An opaque binary string that uniquely identifies a particular program
+    // version with high probability. This is parsed from headers of the loaded
+    // module.
+    // For binaries generated by GNU tools:
+    //   Contents of the .note.gnu.build-id field.
+    // On Windows:
+    //   GUID + AGE in the debug image headers of a module.
+    std::string id;
+
+    // The filename of the module.
+    FilePath filename;
+  };
+
+  // InternalModule represents the module (DLL or exe) and its validness state.
+  // Different from Module, it has an additional field "is_valid".
+  //
+  // This struct is only used for sampling data transfer from NativeStackSampler
+  // to ProfileBuilder.
+  struct BASE_EXPORT InternalModule {
+    InternalModule();
+    InternalModule(uintptr_t base_address,
+                   const std::string& id,
+                   const FilePath& filename);
+    ~InternalModule();
+
+    // Points to the base address of the module.
+    uintptr_t base_address;
+
+    // An opaque binary string that uniquely identifies a particular program
+    // version with high probability. This is parsed from headers of the loaded
+    // module.
+    // For binaries generated by GNU tools:
+    //   Contents of the .note.gnu.build-id field.
+    // On Windows:
+    //   GUID + AGE in the debug image headers of a module.
+    std::string id;
+
+    // The filename of the module.
+    FilePath filename;
+
+    // The validness of the module.
+    bool is_valid;
+  };
+
+  // Frame represents an individual sampled stack frame with module information.
+  struct BASE_EXPORT Frame {
+    Frame(uintptr_t instruction_pointer, size_t module_index);
+    ~Frame();
+
+    // Default constructor to satisfy IPC macros. Do not use explicitly.
+    Frame();
+
+    // The sampled instruction pointer within the function.
+    uintptr_t instruction_pointer;
+
+    // Index of the module in CallStackProfile::modules. We don't represent
+    // module state directly here to save space.
+    size_t module_index;
+  };
+
+  // InternalFrame represents an individual sampled stack frame with full module
+  // information. This is different from Frame which only contains module index.
+  //
+  // This struct is only used for sampling data transfer from NativeStackSampler
+  // to ProfileBuilder.
+  struct BASE_EXPORT InternalFrame {
+    InternalFrame(uintptr_t instruction_pointer,
+                  InternalModule internal_module);
+    ~InternalFrame();
+
+    // The sampled instruction pointer within the function.
+    uintptr_t instruction_pointer;
+
+    // The module information.
+    InternalModule internal_module;
+  };
+
+  // Sample represents a set of stack frames with some extra information.
+  struct BASE_EXPORT Sample {
+    Sample();
+    Sample(const Sample& sample);
+    ~Sample();
+
+    // These constructors are used only during testing.
+    Sample(const Frame& frame);
+    Sample(const std::vector<Frame>& frames);
+
+    // The entire stack frame when the sample is taken.
+    std::vector<Frame> frames;
+
+    // A bit-field indicating which process milestones have passed. This can be
+    // used to tell where in the process lifetime the samples are taken. Just
+    // as a "lifetime" can only move forward, these bits mark the milestones of
+    // the processes life as they occur. Bits can be set but never reset. The
+    // actual definition of the individual bits is left to the user of this
+    // module.
+    uint32_t process_milestones = 0;
+  };
+
+  // CallStackProfile represents a set of samples.
+  struct BASE_EXPORT CallStackProfile {
+    CallStackProfile();
+    CallStackProfile(CallStackProfile&& other);
+    ~CallStackProfile();
+
+    CallStackProfile& operator=(CallStackProfile&& other);
+
+    CallStackProfile CopyForTesting() const;
+
+    std::vector<Module> modules;
+    std::vector<Sample> samples;
+
+    // Duration of this profile.
+    TimeDelta profile_duration;
+
+    // Time between samples.
+    TimeDelta sampling_period;
+
+   private:
+    // Copying is possible but expensive so disallow it except for internal use
+    // (i.e. CopyForTesting); use std::move instead.
+    CallStackProfile(const CallStackProfile& other);
+
+    DISALLOW_ASSIGN(CallStackProfile);
+  };
+
+  // Represents parameters that configure the sampling.
+  struct BASE_EXPORT SamplingParams {
+    // Time to delay before first samples are taken.
+    TimeDelta initial_delay = TimeDelta::FromMilliseconds(0);
+
+    // Number of samples to record per profile.
+    int samples_per_profile = 300;
+
+    // Interval between samples during a sampling profile. This is the desired
+    // duration from the start of one sample to the start of the next sample.
+    TimeDelta sampling_interval = TimeDelta::FromMilliseconds(100);
+  };
+
+  // Testing support. These methods are static beause they interact with the
+  // sampling thread, a singleton used by all StackSamplingProfiler objects.
+  // These methods can only be called by the same thread that started the
+  // sampling.
+  class BASE_EXPORT TestAPI {
+   public:
+    // Resets the internal state to that of a fresh start. This is necessary
+    // so that tests don't inherit state from previous tests.
+    static void Reset();
+
+    // Returns whether the sampling thread is currently running or not.
+    static bool IsSamplingThreadRunning();
+
+    // Disables inherent idle-shutdown behavior.
+    static void DisableIdleShutdown();
+
+    // Initiates an idle shutdown task, as though the idle timer had expired,
+    // causing the thread to exit. There is no "idle" check so this must be
+    // called only when all sampling tasks have completed. This blocks until
+    // the task has been executed, though the actual stopping of the thread
+    // still happens asynchronously. Watch IsSamplingThreadRunning() to know
+    // when the thread has exited. If |simulate_intervening_start| is true then
+    // this method will make it appear to the shutdown task that a new profiler
+    // was started between when the idle-shutdown was initiated and when it
+    // runs.
+    static void PerformSamplingThreadIdleShutdown(
+        bool simulate_intervening_start);
+  };
+
+  // The ProfileBuilder interface allows the user to record profile information
+  // on the fly in whatever format is desired. Functions are invoked by the
+  // profiler on its own thread so must not block or perform expensive
+  // operations.
+  class BASE_EXPORT ProfileBuilder {
+   public:
+    ProfileBuilder() = default;
+    virtual ~ProfileBuilder() = default;
+
+    // Metadata associated with the sample to be saved off.
+    // The code implementing this method must not do anything that could acquire
+    // a mutex, including allocating memory (which includes LOG messages)
+    // because that mutex could be held by a stopped thread, thus resulting in
+    // deadlock.
+    virtual void RecordAnnotations() = 0;
+
+    // Records a new set of internal frames. Invoked when sampling a sample
+    // completes.
+    virtual void OnSampleCompleted(
+        std::vector<InternalFrame> internal_frames) = 0;
+
+    // Finishes the profile construction with |profile_duration| and
+    // |sampling_period|. Invoked when sampling a profile completes.
+    virtual void OnProfileCompleted(TimeDelta profile_duration,
+                                    TimeDelta sampling_period) = 0;
+
+   private:
+    DISALLOW_COPY_AND_ASSIGN(ProfileBuilder);
+  };
+
+  // Creates a profiler for the CURRENT thread. An optional |test_delegate| can
+  // be supplied by tests. The caller must ensure that this object gets
+  // destroyed before the current thread exits.
+  StackSamplingProfiler(
+      const SamplingParams& params,
+      std::unique_ptr<ProfileBuilder> profile_builder,
+      NativeStackSamplerTestDelegate* test_delegate = nullptr);
+
+  // Creates a profiler for ANOTHER thread. An optional |test_delegate| can be
+  // supplied by tests.
+  //
+  // IMPORTANT: The caller must ensure that the thread being sampled does not
+  // exit before this object gets destructed or Bad Things(tm) may occur.
+  StackSamplingProfiler(
+      PlatformThreadId thread_id,
+      const SamplingParams& params,
+      std::unique_ptr<ProfileBuilder> profile_builder,
+      NativeStackSamplerTestDelegate* test_delegate = nullptr);
+
+  // Stops any profiling currently taking place before destroying the profiler.
+  // This will block until profile_builder_'s OnProfileCompleted function has
+  // executed if profiling has started but not already finished.
+  ~StackSamplingProfiler();
+
+  // Initializes the profiler and starts sampling. Might block on a
+  // WaitableEvent if this StackSamplingProfiler was previously started and
+  // recently stopped, while the previous profiling phase winds down.
+  void Start();
+
+  // Stops the profiler and any ongoing sampling. This method will return
+  // immediately with the profile_builder_'s OnProfileCompleted function being
+  // run asynchronously. At most one more stack sample will be taken after this
+  // method returns. Calling this function is optional; if not invoked profiling
+  // terminates when all the profiling samples specified in the SamplingParams
+  // are completed or the profiler object is destroyed, whichever occurs first.
+  void Stop();
+
+ private:
+  friend class TestAPI;
+
+  // SamplingThread is a separate thread used to suspend and sample stacks from
+  // the target thread.
+  class SamplingThread;
+
+  // The thread whose stack will be sampled.
+  PlatformThreadId thread_id_;
+
+  const SamplingParams params_;
+
+  // Receives the sampling data and builds a CallStackProfile. The ownership of
+  // this object will be transferred to the sampling thread when thread sampling
+  // starts.
+  std::unique_ptr<ProfileBuilder> profile_builder_;
+
+  // This starts "signaled", is reset when sampling begins, and is signaled
+  // when that sampling is complete and the profile_builder_'s
+  // OnProfileCompleted function has executed.
+  WaitableEvent profiling_inactive_;
+
+  // An ID uniquely identifying this profiler to the sampling thread. This
+  // will be an internal "null" value when no collection has been started.
+  int profiler_id_;
+
+  // Stored until it can be passed to the NativeStackSampler created in Start().
+  NativeStackSamplerTestDelegate* const test_delegate_;
+
+  DISALLOW_COPY_AND_ASSIGN(StackSamplingProfiler);
+};
+
+// These operators permit types to be compared and used in a map of Samples, as
+// done in tests and by the metrics provider code.
+BASE_EXPORT bool operator==(const StackSamplingProfiler::Module& a,
+                            const StackSamplingProfiler::Module& b);
+BASE_EXPORT bool operator==(const StackSamplingProfiler::Sample& a,
+                            const StackSamplingProfiler::Sample& b);
+BASE_EXPORT bool operator!=(const StackSamplingProfiler::Sample& a,
+                            const StackSamplingProfiler::Sample& b);
+BASE_EXPORT bool operator<(const StackSamplingProfiler::Sample& a,
+                           const StackSamplingProfiler::Sample& b);
+BASE_EXPORT bool operator==(const StackSamplingProfiler::Frame& a,
+                            const StackSamplingProfiler::Frame& b);
+BASE_EXPORT bool operator<(const StackSamplingProfiler::Frame& a,
+                           const StackSamplingProfiler::Frame& b);
+
+}  // namespace base
+
+#endif  // BASE_PROFILER_STACK_SAMPLING_PROFILER_H_
diff --git a/base/profiler/stack_sampling_profiler_unittest.cc b/base/profiler/stack_sampling_profiler_unittest.cc
new file mode 100644
index 0000000..b0f8836
--- /dev/null
+++ b/base/profiler/stack_sampling_profiler_unittest.cc
@@ -0,0 +1,1522 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <algorithm>
+#include <cstdlib>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/compiler_specific.h"
+#include "base/files/file_util.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/native_library.h"
+#include "base/path_service.h"
+#include "base/profiler/native_stack_sampler.h"
+#include "base/profiler/stack_sampling_profiler.h"
+#include "base/run_loop.h"
+#include "base/scoped_native_library.h"
+#include "base/stl_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/synchronization/lock.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/test/bind_test_util.h"
+#include "base/threading/platform_thread.h"
+#include "base/threading/simple_thread.h"
+#include "base/time/time.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if defined(OS_WIN)
+#include <intrin.h>
+#include <malloc.h>
+#include <windows.h>
+#else
+#include <alloca.h>
+#endif
+
+// STACK_SAMPLING_PROFILER_SUPPORTED is used to conditionally enable the tests
+// below for supported platforms (currently Win x64 and Mac x64).
+#if defined(_WIN64) || (defined(OS_MACOSX) && !defined(OS_IOS))
+#define STACK_SAMPLING_PROFILER_SUPPORTED 1
+#endif
+
+#if defined(OS_WIN)
+#pragma intrinsic(_ReturnAddress)
+#endif
+
+namespace base {
+
+#if defined(STACK_SAMPLING_PROFILER_SUPPORTED)
+#define PROFILER_TEST_F(TestClass, TestName) TEST_F(TestClass, TestName)
+#else
+#define PROFILER_TEST_F(TestClass, TestName) \
+  TEST_F(TestClass, DISABLED_##TestName)
+#endif
+
+using SamplingParams = StackSamplingProfiler::SamplingParams;
+using Frame = StackSamplingProfiler::Frame;
+using Frames = std::vector<Frame>;
+using InternalFrame = StackSamplingProfiler::InternalFrame;
+using InternalFrames = std::vector<InternalFrame>;
+using InternalFrameSets = std::vector<std::vector<InternalFrame>>;
+using Module = StackSamplingProfiler::Module;
+using InternalModule = StackSamplingProfiler::InternalModule;
+using Sample = StackSamplingProfiler::Sample;
+
+namespace {
+
+// Configuration for the frames that appear on the stack.
+struct StackConfiguration {
+  enum Config { NORMAL, WITH_ALLOCA, WITH_OTHER_LIBRARY };
+
+  explicit StackConfiguration(Config config)
+      : StackConfiguration(config, nullptr) {
+    EXPECT_NE(config, WITH_OTHER_LIBRARY);
+  }
+
+  StackConfiguration(Config config, NativeLibrary library)
+      : config(config), library(library) {
+    EXPECT_TRUE(config != WITH_OTHER_LIBRARY || library);
+  }
+
+  Config config;
+
+  // Only used if config == WITH_OTHER_LIBRARY.
+  NativeLibrary library;
+};
+
+// Signature for a target function that is expected to appear in the stack. See
+// SignalAndWaitUntilSignaled() below. The return value should be a program
+// counter pointer near the end of the function.
+using TargetFunction = const void* (*)(WaitableEvent*,
+                                       WaitableEvent*,
+                                       const StackConfiguration*);
+
+// A thread to target for profiling, whose stack is guaranteed to contain
+// SignalAndWaitUntilSignaled() when coordinated with the main thread.
+class TargetThread : public PlatformThread::Delegate {
+ public:
+  explicit TargetThread(const StackConfiguration& stack_config);
+
+  // PlatformThread::Delegate:
+  void ThreadMain() override;
+
+  // Waits for the thread to have started and be executing in
+  // SignalAndWaitUntilSignaled().
+  void WaitForThreadStart();
+
+  // Allows the thread to return from SignalAndWaitUntilSignaled() and finish
+  // execution.
+  void SignalThreadToFinish();
+
+  // This function is guaranteed to be executing between calls to
+  // WaitForThreadStart() and SignalThreadToFinish() when invoked with
+  // |thread_started_event_| and |finish_event_|. Returns a program counter
+  // value near the end of the function. May be invoked with null WaitableEvents
+  // to just return the program counter.
+  //
+  // This function is static so that we can get a straightforward address
+  // for it in one of the tests below, rather than dealing with the complexity
+  // of a member function pointer representation.
+  static const void* SignalAndWaitUntilSignaled(
+      WaitableEvent* thread_started_event,
+      WaitableEvent* finish_event,
+      const StackConfiguration* stack_config);
+
+  // Calls into SignalAndWaitUntilSignaled() after allocating memory on the
+  // stack with alloca.
+  static const void* CallWithAlloca(WaitableEvent* thread_started_event,
+                                    WaitableEvent* finish_event,
+                                    const StackConfiguration* stack_config);
+
+  // Calls into SignalAndWaitUntilSignaled() via a function in
+  // base_profiler_test_support_library.
+  static const void* CallThroughOtherLibrary(
+      WaitableEvent* thread_started_event,
+      WaitableEvent* finish_event,
+      const StackConfiguration* stack_config);
+
+  PlatformThreadId id() const { return id_; }
+
+ private:
+  struct TargetFunctionArgs {
+    WaitableEvent* thread_started_event;
+    WaitableEvent* finish_event;
+    const StackConfiguration* stack_config;
+  };
+
+  // Callback function to be provided when calling through the other library.
+  static void OtherLibraryCallback(void* arg);
+
+  // Returns the current program counter, or a value very close to it.
+  static const void* GetProgramCounter();
+
+  WaitableEvent thread_started_event_;
+  WaitableEvent finish_event_;
+  PlatformThreadId id_;
+  const StackConfiguration stack_config_;
+
+  DISALLOW_COPY_AND_ASSIGN(TargetThread);
+};
+
+TargetThread::TargetThread(const StackConfiguration& stack_config)
+    : thread_started_event_(WaitableEvent::ResetPolicy::AUTOMATIC,
+                            WaitableEvent::InitialState::NOT_SIGNALED),
+      finish_event_(WaitableEvent::ResetPolicy::AUTOMATIC,
+                    WaitableEvent::InitialState::NOT_SIGNALED),
+      id_(0),
+      stack_config_(stack_config) {}
+
+void TargetThread::ThreadMain() {
+  id_ = PlatformThread::CurrentId();
+  switch (stack_config_.config) {
+    case StackConfiguration::NORMAL:
+      SignalAndWaitUntilSignaled(&thread_started_event_, &finish_event_,
+                                 &stack_config_);
+      break;
+
+    case StackConfiguration::WITH_ALLOCA:
+      CallWithAlloca(&thread_started_event_, &finish_event_, &stack_config_);
+      break;
+
+    case StackConfiguration::WITH_OTHER_LIBRARY:
+      CallThroughOtherLibrary(&thread_started_event_, &finish_event_,
+                              &stack_config_);
+      break;
+  }
+}
+
+void TargetThread::WaitForThreadStart() {
+  thread_started_event_.Wait();
+}
+
+void TargetThread::SignalThreadToFinish() {
+  finish_event_.Signal();
+}
+
+// static
+// Disable inlining for this function so that it gets its own stack frame.
+NOINLINE const void* TargetThread::SignalAndWaitUntilSignaled(
+    WaitableEvent* thread_started_event,
+    WaitableEvent* finish_event,
+    const StackConfiguration* stack_config) {
+  if (thread_started_event && finish_event) {
+    thread_started_event->Signal();
+    finish_event->Wait();
+  }
+
+  // Volatile to prevent a tail call to GetProgramCounter().
+  const void* volatile program_counter = GetProgramCounter();
+  return program_counter;
+}
+
+// static
+// Disable inlining for this function so that it gets its own stack frame.
+NOINLINE const void* TargetThread::CallWithAlloca(
+    WaitableEvent* thread_started_event,
+    WaitableEvent* finish_event,
+    const StackConfiguration* stack_config) {
+  const size_t alloca_size = 100;
+  // Memset to 0 to generate a clean failure.
+  std::memset(alloca(alloca_size), 0, alloca_size);
+
+  SignalAndWaitUntilSignaled(thread_started_event, finish_event, stack_config);
+
+  // Volatile to prevent a tail call to GetProgramCounter().
+  const void* volatile program_counter = GetProgramCounter();
+  return program_counter;
+}
+
+// static
+NOINLINE const void* TargetThread::CallThroughOtherLibrary(
+    WaitableEvent* thread_started_event,
+    WaitableEvent* finish_event,
+    const StackConfiguration* stack_config) {
+  if (stack_config) {
+    // A function whose arguments are a function accepting void*, and a void*.
+    using InvokeCallbackFunction = void (*)(void (*)(void*), void*);
+    EXPECT_TRUE(stack_config->library);
+    InvokeCallbackFunction function = reinterpret_cast<InvokeCallbackFunction>(
+        GetFunctionPointerFromNativeLibrary(stack_config->library,
+                                            "InvokeCallbackFunction"));
+    EXPECT_TRUE(function);
+
+    TargetFunctionArgs args = {thread_started_event, finish_event,
+                               stack_config};
+    (*function)(&OtherLibraryCallback, &args);
+  }
+
+  // Volatile to prevent a tail call to GetProgramCounter().
+  const void* volatile program_counter = GetProgramCounter();
+  return program_counter;
+}
+
+// static
+void TargetThread::OtherLibraryCallback(void* arg) {
+  const TargetFunctionArgs* args = static_cast<TargetFunctionArgs*>(arg);
+  SignalAndWaitUntilSignaled(args->thread_started_event, args->finish_event,
+                             args->stack_config);
+  // Prevent tail call.
+  volatile int i = 0;
+  ALLOW_UNUSED_LOCAL(i);
+}
+
+// static
+// Disable inlining for this function so that it gets its own stack frame.
+NOINLINE const void* TargetThread::GetProgramCounter() {
+#if defined(OS_WIN)
+  return _ReturnAddress();
+#else
+  return __builtin_return_address(0);
+#endif
+}
+
+// Profile consists of a set of internal frame sets and other sampling
+// information.
+struct Profile {
+  Profile() = default;
+  Profile(Profile&& other) = default;
+  Profile(const InternalFrameSets& frame_sets,
+          int annotation_count,
+          TimeDelta profile_duration,
+          TimeDelta sampling_period);
+
+  ~Profile() = default;
+
+  Profile& operator=(Profile&& other) = default;
+
+  // The collected internal frame sets.
+  InternalFrameSets frame_sets;
+
+  // The number of invocations of RecordAnnotations().
+  int annotation_count;
+
+  // Duration of this profile.
+  TimeDelta profile_duration;
+
+  // Time between samples.
+  TimeDelta sampling_period;
+};
+
+Profile::Profile(const InternalFrameSets& frame_sets,
+                 int annotation_count,
+                 TimeDelta profile_duration,
+                 TimeDelta sampling_period)
+    : frame_sets(frame_sets),
+      annotation_count(annotation_count),
+      profile_duration(profile_duration),
+      sampling_period(sampling_period) {}
+
+// The callback type used to collect a profile. The passed Profile is move-only.
+// Other threads, including the UI thread, may block on callback completion so
+// this should run as quickly as possible.
+using ProfileCompletedCallback = Callback<void(Profile)>;
+
+// TestProfileBuilder collects internal frames produced by the profiler.
+class TestProfileBuilder : public StackSamplingProfiler::ProfileBuilder {
+ public:
+  TestProfileBuilder(const ProfileCompletedCallback& callback);
+
+  ~TestProfileBuilder() override;
+
+  // StackSamplingProfiler::ProfileBuilder:
+  void RecordAnnotations() override;
+  void OnSampleCompleted(InternalFrames internal_frames) override;
+  void OnProfileCompleted(TimeDelta profile_duration,
+                          TimeDelta sampling_period) override;
+
+ private:
+  // The sets of internal frames recorded.
+  std::vector<InternalFrames> frame_sets_;
+
+  // The number of invocations of RecordAnnotations().
+  int annotation_count_ = 0;
+
+  // Callback made when sampling a profile completes.
+  const ProfileCompletedCallback callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestProfileBuilder);
+};
+
+TestProfileBuilder::TestProfileBuilder(const ProfileCompletedCallback& callback)
+    : callback_(callback) {}
+
+TestProfileBuilder::~TestProfileBuilder() = default;
+
+void TestProfileBuilder::RecordAnnotations() {
+  ++annotation_count_;
+}
+
+void TestProfileBuilder::OnSampleCompleted(InternalFrames internal_frames) {
+  frame_sets_.push_back(std::move(internal_frames));
+}
+
+void TestProfileBuilder::OnProfileCompleted(TimeDelta profile_duration,
+                                            TimeDelta sampling_period) {
+  callback_.Run(Profile(frame_sets_, annotation_count_, profile_duration,
+                        sampling_period));
+}
+
+// Loads the other library, which defines a function to be called in the
+// WITH_OTHER_LIBRARY configuration.
+NativeLibrary LoadOtherLibrary() {
+  // The lambda gymnastics works around the fact that we can't use ASSERT_*
+  // macros in a function returning non-null.
+  const auto load = [](NativeLibrary* library) {
+    FilePath other_library_path;
+    ASSERT_TRUE(PathService::Get(DIR_EXE, &other_library_path));
+    other_library_path = other_library_path.AppendASCII(
+        GetNativeLibraryName("base_profiler_test_support_library"));
+    NativeLibraryLoadError load_error;
+    *library = LoadNativeLibrary(other_library_path, &load_error);
+    ASSERT_TRUE(*library) << "error loading " << other_library_path.value()
+                          << ": " << load_error.ToString();
+  };
+
+  NativeLibrary library = nullptr;
+  load(&library);
+  return library;
+}
+
+// Unloads |library| and returns when it has completed unloading. Unloading a
+// library is asynchronous on Windows, so simply calling UnloadNativeLibrary()
+// is insufficient to ensure it's been unloaded.
+void SynchronousUnloadNativeLibrary(NativeLibrary library) {
+  UnloadNativeLibrary(library);
+#if defined(OS_WIN)
+  // NativeLibrary is a typedef for HMODULE, which is actually the base address
+  // of the module.
+  uintptr_t module_base_address = reinterpret_cast<uintptr_t>(library);
+  HMODULE module_handle;
+  // Keep trying to get the module handle until the call fails.
+  while (::GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
+                                 GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
+                             reinterpret_cast<LPCTSTR>(module_base_address),
+                             &module_handle) ||
+         ::GetLastError() != ERROR_MOD_NOT_FOUND) {
+    PlatformThread::Sleep(TimeDelta::FromMilliseconds(1));
+  }
+#elif defined(OS_MACOSX)
+// Unloading a library on the Mac is synchronous.
+#else
+  NOTIMPLEMENTED();
+#endif
+}
+
+// Executes the function with the target thread running and executing within
+// SignalAndWaitUntilSignaled(). Performs all necessary target thread startup
+// and shutdown work before and afterward.
+template <class Function>
+void WithTargetThread(Function function,
+                      const StackConfiguration& stack_config) {
+  TargetThread target_thread(stack_config);
+  PlatformThreadHandle target_thread_handle;
+  EXPECT_TRUE(PlatformThread::Create(0, &target_thread, &target_thread_handle));
+
+  target_thread.WaitForThreadStart();
+
+  function(target_thread.id());
+
+  target_thread.SignalThreadToFinish();
+
+  PlatformThread::Join(target_thread_handle);
+}
+
+template <class Function>
+void WithTargetThread(Function function) {
+  WithTargetThread(function, StackConfiguration(StackConfiguration::NORMAL));
+}
+
+struct TestProfilerInfo {
+  TestProfilerInfo(PlatformThreadId thread_id,
+                   const SamplingParams& params,
+                   NativeStackSamplerTestDelegate* delegate = nullptr)
+      : completed(WaitableEvent::ResetPolicy::MANUAL,
+                  WaitableEvent::InitialState::NOT_SIGNALED),
+        profiler(thread_id,
+                 params,
+                 std::make_unique<TestProfileBuilder>(
+                     BindLambdaForTesting([this](Profile result_profile) {
+                       profile = std::move(result_profile);
+                       completed.Signal();
+                     })),
+                 delegate) {}
+
+  // The order here is important to ensure objects being referenced don't get
+  // destructed until after the objects referencing them.
+  Profile profile;
+  WaitableEvent completed;
+  StackSamplingProfiler profiler;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TestProfilerInfo);
+};
+
+// Creates multiple profilers based on a vector of parameters.
+std::vector<std::unique_ptr<TestProfilerInfo>> CreateProfilers(
+    PlatformThreadId target_thread_id,
+    const std::vector<SamplingParams>& params) {
+  DCHECK(!params.empty());
+
+  std::vector<std::unique_ptr<TestProfilerInfo>> profilers;
+  for (size_t i = 0; i < params.size(); ++i) {
+    profilers.push_back(
+        std::make_unique<TestProfilerInfo>(target_thread_id, params[i]));
+  }
+
+  return profilers;
+}
+
+// Captures internal frames as specified by |params| on the TargetThread, and
+// returns them. Waits up to |profiler_wait_time| for the profiler to complete.
+InternalFrameSets CaptureFrameSets(const SamplingParams& params,
+                                   TimeDelta profiler_wait_time) {
+  InternalFrameSets frame_sets;
+  WithTargetThread([&params, &frame_sets,
+                    profiler_wait_time](PlatformThreadId target_thread_id) {
+    TestProfilerInfo info(target_thread_id, params);
+    info.profiler.Start();
+    info.completed.TimedWait(profiler_wait_time);
+    info.profiler.Stop();
+    info.completed.Wait();
+    frame_sets = std::move(info.profile.frame_sets);
+  });
+
+  return frame_sets;
+}
+
+// Waits for one of multiple samplings to complete.
+size_t WaitForSamplingComplete(
+    const std::vector<std::unique_ptr<TestProfilerInfo>>& infos) {
+  // Map unique_ptrs to something that WaitMany can accept.
+  std::vector<WaitableEvent*> sampling_completed_rawptrs(infos.size());
+  std::transform(infos.begin(), infos.end(), sampling_completed_rawptrs.begin(),
+                 [](const std::unique_ptr<TestProfilerInfo>& info) {
+                   return &info.get()->completed;
+                 });
+  // Wait for one profiler to finish.
+  return WaitableEvent::WaitMany(sampling_completed_rawptrs.data(),
+                                 sampling_completed_rawptrs.size());
+}
+
+// If this executable was linked with /INCREMENTAL (the default for non-official
+// debug and release builds on Windows), function addresses do not correspond to
+// function code itself, but instead to instructions in the Incremental Link
+// Table that jump to the functions. Checks for a jump instruction and if
+// present does a little decompilation to find the function's actual starting
+// address.
+const void* MaybeFixupFunctionAddressForILT(const void* function_address) {
+#if defined(_WIN64)
+  const unsigned char* opcode =
+      reinterpret_cast<const unsigned char*>(function_address);
+  if (*opcode == 0xe9) {
+    // This is a relative jump instruction. Assume we're in the ILT and compute
+    // the function start address from the instruction offset.
+    const int32_t* offset = reinterpret_cast<const int32_t*>(opcode + 1);
+    const unsigned char* next_instruction =
+        reinterpret_cast<const unsigned char*>(offset + 1);
+    return next_instruction + *offset;
+  }
+#endif
+  return function_address;
+}
+
+// Searches through the frames in |sample|, returning an iterator to the first
+// frame that has an instruction pointer within |target_function|. Returns
+// sample.end() if no such frames are found.
+InternalFrames::const_iterator FindFirstFrameWithinFunction(
+    const InternalFrames& frames,
+    TargetFunction target_function) {
+  uintptr_t function_start =
+      reinterpret_cast<uintptr_t>(MaybeFixupFunctionAddressForILT(
+          reinterpret_cast<const void*>(target_function)));
+  uintptr_t function_end =
+      reinterpret_cast<uintptr_t>(target_function(nullptr, nullptr, nullptr));
+  for (auto it = frames.begin(); it != frames.end(); ++it) {
+    if (it->instruction_pointer >= function_start &&
+        it->instruction_pointer <= function_end) {
+      return it;
+    }
+  }
+  return frames.end();
+}
+
+// Formats a sample into a string that can be output for test diagnostics.
+std::string FormatSampleForDiagnosticOutput(const InternalFrames& frames) {
+  std::string output;
+  for (const auto& frame : frames) {
+    output += StringPrintf(
+        "0x%p %s\n", reinterpret_cast<const void*>(frame.instruction_pointer),
+        frame.internal_module.filename.AsUTF8Unsafe().c_str());
+  }
+  return output;
+}
+
+// Returns a duration that is longer than the test timeout. We would use
+// TimeDelta::Max() but https://crbug.com/465948.
+TimeDelta AVeryLongTimeDelta() {
+  return TimeDelta::FromDays(1);
+}
+
+// Tests the scenario where the library is unloaded after copying the stack, but
+// before walking it. If |wait_until_unloaded| is true, ensures that the
+// asynchronous library loading has completed before walking the stack. If
+// false, the unloading may still be occurring during the stack walk.
+void TestLibraryUnload(bool wait_until_unloaded) {
+  // Test delegate that supports intervening between the copying of the stack
+  // and the walking of the stack.
+  class StackCopiedSignaler : public NativeStackSamplerTestDelegate {
+   public:
+    StackCopiedSignaler(WaitableEvent* stack_copied,
+                        WaitableEvent* start_stack_walk,
+                        bool wait_to_walk_stack)
+        : stack_copied_(stack_copied),
+          start_stack_walk_(start_stack_walk),
+          wait_to_walk_stack_(wait_to_walk_stack) {}
+
+    void OnPreStackWalk() override {
+      stack_copied_->Signal();
+      if (wait_to_walk_stack_)
+        start_stack_walk_->Wait();
+    }
+
+   private:
+    WaitableEvent* const stack_copied_;
+    WaitableEvent* const start_stack_walk_;
+    const bool wait_to_walk_stack_;
+  };
+
+  SamplingParams params;
+  params.sampling_interval = TimeDelta::FromMilliseconds(0);
+  params.samples_per_profile = 1;
+
+  NativeLibrary other_library = LoadOtherLibrary();
+  TargetThread target_thread(StackConfiguration(
+      StackConfiguration::WITH_OTHER_LIBRARY, other_library));
+
+  PlatformThreadHandle target_thread_handle;
+  EXPECT_TRUE(PlatformThread::Create(0, &target_thread, &target_thread_handle));
+
+  target_thread.WaitForThreadStart();
+
+  WaitableEvent sampling_thread_completed(
+      WaitableEvent::ResetPolicy::MANUAL,
+      WaitableEvent::InitialState::NOT_SIGNALED);
+  Profile profile;
+
+  WaitableEvent stack_copied(WaitableEvent::ResetPolicy::MANUAL,
+                             WaitableEvent::InitialState::NOT_SIGNALED);
+  WaitableEvent start_stack_walk(WaitableEvent::ResetPolicy::MANUAL,
+                                 WaitableEvent::InitialState::NOT_SIGNALED);
+  StackCopiedSignaler test_delegate(&stack_copied, &start_stack_walk,
+                                    wait_until_unloaded);
+  StackSamplingProfiler profiler(
+      target_thread.id(), params,
+      std::make_unique<TestProfileBuilder>(BindLambdaForTesting(
+          [&profile, &sampling_thread_completed](Profile result_profile) {
+            profile = std::move(result_profile);
+            sampling_thread_completed.Signal();
+          })),
+      &test_delegate);
+
+  profiler.Start();
+
+  // Wait for the stack to be copied and the target thread to be resumed.
+  stack_copied.Wait();
+
+  // Cause the target thread to finish, so that it's no longer executing code in
+  // the library we're about to unload.
+  target_thread.SignalThreadToFinish();
+  PlatformThread::Join(target_thread_handle);
+
+  // Unload the library now that it's not being used.
+  if (wait_until_unloaded)
+    SynchronousUnloadNativeLibrary(other_library);
+  else
+    UnloadNativeLibrary(other_library);
+
+  // Let the stack walk commence after unloading the library, if we're waiting
+  // on that event.
+  start_stack_walk.Signal();
+
+  // Wait for the sampling thread to complete and fill out |profile|.
+  sampling_thread_completed.Wait();
+
+  // Look up the frames.
+  ASSERT_EQ(1u, profile.frame_sets.size());
+  const InternalFrames& frames = profile.frame_sets[0];
+
+  // Check that the stack contains a frame for
+  // TargetThread::SignalAndWaitUntilSignaled().
+  InternalFrames::const_iterator end_frame = FindFirstFrameWithinFunction(
+      frames, &TargetThread::SignalAndWaitUntilSignaled);
+  ASSERT_TRUE(end_frame != frames.end())
+      << "Function at "
+      << MaybeFixupFunctionAddressForILT(reinterpret_cast<const void*>(
+             &TargetThread::SignalAndWaitUntilSignaled))
+      << " was not found in stack:\n"
+      << FormatSampleForDiagnosticOutput(frames);
+
+  if (wait_until_unloaded) {
+    // The stack should look like this, resulting one frame after
+    // SignalAndWaitUntilSignaled. The frame in the now-unloaded library is
+    // not recorded since we can't get module information.
+    //
+    // ... WaitableEvent and system frames ...
+    // TargetThread::SignalAndWaitUntilSignaled
+    // TargetThread::OtherLibraryCallback
+    EXPECT_EQ(2, frames.end() - end_frame)
+        << "Stack:\n"
+        << FormatSampleForDiagnosticOutput(frames);
+  } else {
+    // We didn't wait for the asynchronous unloading to complete, so the results
+    // are non-deterministic: if the library finished unloading we should have
+    // the same stack as |wait_until_unloaded|, if not we should have the full
+    // stack. The important thing is that we should not crash.
+
+    if (frames.end() - end_frame == 2) {
+      // This is the same case as |wait_until_unloaded|.
+      return;
+    }
+
+    // Check that the stack contains a frame for
+    // TargetThread::CallThroughOtherLibrary().
+    InternalFrames::const_iterator other_library_frame =
+        FindFirstFrameWithinFunction(frames,
+                                     &TargetThread::CallThroughOtherLibrary);
+    ASSERT_TRUE(other_library_frame != frames.end())
+        << "Function at "
+        << MaybeFixupFunctionAddressForILT(reinterpret_cast<const void*>(
+               &TargetThread::CallThroughOtherLibrary))
+        << " was not found in stack:\n"
+        << FormatSampleForDiagnosticOutput(frames);
+
+    // The stack should look like this, resulting in three frames between
+    // SignalAndWaitUntilSignaled and CallThroughOtherLibrary:
+    //
+    // ... WaitableEvent and system frames ...
+    // TargetThread::SignalAndWaitUntilSignaled
+    // TargetThread::OtherLibraryCallback
+    // InvokeCallbackFunction (in other library)
+    // TargetThread::CallThroughOtherLibrary
+    EXPECT_EQ(3, other_library_frame - end_frame)
+        << "Stack:\n"
+        << FormatSampleForDiagnosticOutput(frames);
+  }
+}
+
+// Provide a suitable (and clean) environment for the tests below. All tests
+// must use this class to ensure that proper clean-up is done and thus be
+// usable in a later test.
+class StackSamplingProfilerTest : public testing::Test {
+ public:
+  void SetUp() override {
+    // The idle-shutdown time is too long for convenient (and accurate) testing.
+    // That behavior is checked instead by artificially triggering it through
+    // the TestAPI.
+    StackSamplingProfiler::TestAPI::DisableIdleShutdown();
+  }
+
+  void TearDown() override {
+    // Be a good citizen and clean up after ourselves. This also re-enables the
+    // idle-shutdown behavior.
+    StackSamplingProfiler::TestAPI::Reset();
+  }
+};
+
+}  // namespace
+
+// Checks that the basic expected information is present in sampled internal
+// frames.
+//
+// macOS ASAN is not yet supported - crbug.com/718628.
+#if !(defined(ADDRESS_SANITIZER) && defined(OS_MACOSX))
+#define MAYBE_Basic Basic
+#else
+#define MAYBE_Basic DISABLED_Basic
+#endif
+PROFILER_TEST_F(StackSamplingProfilerTest, MAYBE_Basic) {
+  SamplingParams params;
+  params.sampling_interval = TimeDelta::FromMilliseconds(0);
+  params.samples_per_profile = 1;
+
+  InternalFrameSets frame_sets = CaptureFrameSets(params, AVeryLongTimeDelta());
+
+  // Check that the size of the frame sets are correct.
+  ASSERT_EQ(1u, frame_sets.size());
+  const InternalFrames& frames = frame_sets[0];
+
+  // Check that all the modules are valid.
+  for (const auto& frame : frames)
+    EXPECT_TRUE(frame.internal_module.is_valid);
+
+  // Check that the stack contains a frame for
+  // TargetThread::SignalAndWaitUntilSignaled().
+  InternalFrames::const_iterator loc = FindFirstFrameWithinFunction(
+      frames, &TargetThread::SignalAndWaitUntilSignaled);
+  ASSERT_TRUE(loc != frames.end())
+      << "Function at "
+      << MaybeFixupFunctionAddressForILT(reinterpret_cast<const void*>(
+             &TargetThread::SignalAndWaitUntilSignaled))
+      << " was not found in stack:\n"
+      << FormatSampleForDiagnosticOutput(frames);
+}
+
+// Checks that the profiler handles stacks containing dynamically-allocated
+// stack memory.
+// macOS ASAN is not yet supported - crbug.com/718628.
+#if !(defined(ADDRESS_SANITIZER) && defined(OS_MACOSX))
+#define MAYBE_Alloca Alloca
+#else
+#define MAYBE_Alloca DISABLED_Alloca
+#endif
+PROFILER_TEST_F(StackSamplingProfilerTest, MAYBE_Alloca) {
+  SamplingParams params;
+  params.sampling_interval = TimeDelta::FromMilliseconds(0);
+  params.samples_per_profile = 1;
+
+  Profile profile;
+  WithTargetThread(
+      [&params, &profile](PlatformThreadId target_thread_id) {
+        WaitableEvent sampling_thread_completed(
+            WaitableEvent::ResetPolicy::MANUAL,
+            WaitableEvent::InitialState::NOT_SIGNALED);
+        StackSamplingProfiler profiler(
+            target_thread_id, params,
+            std::make_unique<TestProfileBuilder>(BindLambdaForTesting(
+                [&profile, &sampling_thread_completed](Profile result_profile) {
+                  profile = std::move(result_profile);
+                  sampling_thread_completed.Signal();
+                })));
+        profiler.Start();
+        sampling_thread_completed.Wait();
+      },
+      StackConfiguration(StackConfiguration::WITH_ALLOCA));
+
+  // Look up the frames.
+  ASSERT_EQ(1u, profile.frame_sets.size());
+  const InternalFrames& frames = profile.frame_sets[0];
+
+  // Check that the stack contains a frame for
+  // TargetThread::SignalAndWaitUntilSignaled().
+  InternalFrames::const_iterator end_frame = FindFirstFrameWithinFunction(
+      frames, &TargetThread::SignalAndWaitUntilSignaled);
+  ASSERT_TRUE(end_frame != frames.end())
+      << "Function at "
+      << MaybeFixupFunctionAddressForILT(reinterpret_cast<const void*>(
+             &TargetThread::SignalAndWaitUntilSignaled))
+      << " was not found in stack:\n"
+      << FormatSampleForDiagnosticOutput(frames);
+
+  // Check that the stack contains a frame for TargetThread::CallWithAlloca().
+  InternalFrames::const_iterator alloca_frame =
+      FindFirstFrameWithinFunction(frames, &TargetThread::CallWithAlloca);
+  ASSERT_TRUE(alloca_frame != frames.end())
+      << "Function at "
+      << MaybeFixupFunctionAddressForILT(
+             reinterpret_cast<const void*>(&TargetThread::CallWithAlloca))
+      << " was not found in stack:\n"
+      << FormatSampleForDiagnosticOutput(frames);
+
+  // These frames should be adjacent on the stack.
+  EXPECT_EQ(1, alloca_frame - end_frame)
+      << "Stack:\n"
+      << FormatSampleForDiagnosticOutput(frames);
+}
+
+// Checks that a profiler can stop/destruct without ever having started.
+PROFILER_TEST_F(StackSamplingProfilerTest, StopWithoutStarting) {
+  WithTargetThread([](PlatformThreadId target_thread_id) {
+    SamplingParams params;
+    params.sampling_interval = TimeDelta::FromMilliseconds(0);
+    params.samples_per_profile = 1;
+
+    Profile profile;
+    WaitableEvent sampling_completed(WaitableEvent::ResetPolicy::MANUAL,
+                                     WaitableEvent::InitialState::NOT_SIGNALED);
+
+    StackSamplingProfiler profiler(
+        target_thread_id, params,
+        std::make_unique<TestProfileBuilder>(BindLambdaForTesting(
+            [&profile, &sampling_completed](Profile result_profile) {
+              profile = std::move(result_profile);
+              sampling_completed.Signal();
+            })));
+
+    profiler.Stop();  // Constructed but never started.
+    EXPECT_FALSE(sampling_completed.IsSignaled());
+  });
+}
+
+// Checks that its okay to stop a profiler before it finishes even when the
+// sampling thread continues to run.
+PROFILER_TEST_F(StackSamplingProfilerTest, StopSafely) {
+  // Test delegate that counts samples.
+  class SampleRecordedCounter : public NativeStackSamplerTestDelegate {
+   public:
+    SampleRecordedCounter() = default;
+
+    void OnPreStackWalk() override {
+      AutoLock lock(lock_);
+      ++count_;
+    }
+
+    size_t Get() {
+      AutoLock lock(lock_);
+      return count_;
+    }
+
+   private:
+    Lock lock_;
+    size_t count_ = 0;
+  };
+
+  WithTargetThread([](PlatformThreadId target_thread_id) {
+    SamplingParams params[2];
+
+    // Providing an initial delay makes it more likely that both will be
+    // scheduled before either starts to run. Once started, samples will
+    // run ordered by their scheduled, interleaved times regardless of
+    // whatever interval the thread wakes up.
+    params[0].initial_delay = TimeDelta::FromMilliseconds(10);
+    params[0].sampling_interval = TimeDelta::FromMilliseconds(1);
+    params[0].samples_per_profile = 100000;
+
+    params[1].initial_delay = TimeDelta::FromMilliseconds(10);
+    params[1].sampling_interval = TimeDelta::FromMilliseconds(1);
+    params[1].samples_per_profile = 100000;
+
+    SampleRecordedCounter samples_recorded[size(params)];
+
+    TestProfilerInfo profiler_info0(target_thread_id, params[0],
+                                    &samples_recorded[0]);
+    TestProfilerInfo profiler_info1(target_thread_id, params[1],
+                                    &samples_recorded[1]);
+
+    profiler_info0.profiler.Start();
+    profiler_info1.profiler.Start();
+
+    // Wait for both to start accumulating samples. Using a WaitableEvent is
+    // possible but gets complicated later on because there's no way of knowing
+    // if 0 or 1 additional sample will be taken after Stop() and thus no way
+    // of knowing how many Wait() calls to make on it.
+    while (samples_recorded[0].Get() == 0 || samples_recorded[1].Get() == 0)
+      PlatformThread::Sleep(TimeDelta::FromMilliseconds(1));
+
+    // Ensure that the first sampler can be safely stopped while the second
+    // continues to run. The stopped first profiler will still have a
+    // RecordSampleTask pending that will do nothing when executed because the
+    // collection will have been removed by Stop().
+    profiler_info0.profiler.Stop();
+    profiler_info0.completed.Wait();
+    size_t count0 = samples_recorded[0].Get();
+    size_t count1 = samples_recorded[1].Get();
+
+    // Waiting for the second sampler to collect a couple samples ensures that
+    // the pending RecordSampleTask for the first has executed because tasks are
+    // always ordered by their next scheduled time.
+    while (samples_recorded[1].Get() < count1 + 2)
+      PlatformThread::Sleep(TimeDelta::FromMilliseconds(1));
+
+    // Ensure that the first profiler didn't do anything since it was stopped.
+    EXPECT_EQ(count0, samples_recorded[0].Get());
+  });
+}
+
+// Checks that no internal frames are captured if the profiling is stopped
+// during the initial delay.
+PROFILER_TEST_F(StackSamplingProfilerTest, StopDuringInitialDelay) {
+  SamplingParams params;
+  params.initial_delay = TimeDelta::FromSeconds(60);
+
+  InternalFrameSets frame_sets =
+      CaptureFrameSets(params, TimeDelta::FromMilliseconds(0));
+
+  EXPECT_TRUE(frame_sets.empty());
+}
+
+// Checks that tasks can be stopped before completion and incomplete internal
+// frames are captured.
+PROFILER_TEST_F(StackSamplingProfilerTest, StopDuringInterSampleInterval) {
+  // Test delegate that counts samples.
+  class SampleRecordedEvent : public NativeStackSamplerTestDelegate {
+   public:
+    SampleRecordedEvent()
+        : sample_recorded_(WaitableEvent::ResetPolicy::MANUAL,
+                           WaitableEvent::InitialState::NOT_SIGNALED) {}
+
+    void OnPreStackWalk() override { sample_recorded_.Signal(); }
+
+    void WaitForSample() { sample_recorded_.Wait(); }
+
+   private:
+    WaitableEvent sample_recorded_;
+  };
+
+  WithTargetThread([](PlatformThreadId target_thread_id) {
+    SamplingParams params;
+
+    params.sampling_interval = AVeryLongTimeDelta();
+    params.samples_per_profile = 2;
+
+    SampleRecordedEvent samples_recorded;
+    TestProfilerInfo profiler_info(target_thread_id, params, &samples_recorded);
+
+    profiler_info.profiler.Start();
+
+    // Wait for profiler to start accumulating samples.
+    samples_recorded.WaitForSample();
+
+    // Ensure that it can stop safely.
+    profiler_info.profiler.Stop();
+    profiler_info.completed.Wait();
+
+    EXPECT_EQ(1u, profiler_info.profile.frame_sets.size());
+  });
+}
+
+// Checks that we can destroy the profiler while profiling.
+PROFILER_TEST_F(StackSamplingProfilerTest, DestroyProfilerWhileProfiling) {
+  SamplingParams params;
+  params.sampling_interval = TimeDelta::FromMilliseconds(10);
+
+  Profile profile;
+  WithTargetThread([&params, &profile](PlatformThreadId target_thread_id) {
+    std::unique_ptr<StackSamplingProfiler> profiler;
+    auto profile_builder = std::make_unique<TestProfileBuilder>(
+        BindLambdaForTesting([&profile](Profile result_profile) {
+          profile = std::move(result_profile);
+        }));
+    profiler.reset(new StackSamplingProfiler(target_thread_id, params,
+                                             std::move(profile_builder)));
+    profiler->Start();
+    profiler.reset();
+
+    // Wait longer than a sample interval to catch any use-after-free actions by
+    // the profiler thread.
+    PlatformThread::Sleep(TimeDelta::FromMilliseconds(50));
+  });
+}
+
+// Checks that the different profilers may be run.
+PROFILER_TEST_F(StackSamplingProfilerTest, CanRunMultipleProfilers) {
+  SamplingParams params;
+  params.sampling_interval = TimeDelta::FromMilliseconds(0);
+  params.samples_per_profile = 1;
+
+  InternalFrameSets frame_sets = CaptureFrameSets(params, AVeryLongTimeDelta());
+  ASSERT_EQ(1u, frame_sets.size());
+
+  frame_sets = CaptureFrameSets(params, AVeryLongTimeDelta());
+  ASSERT_EQ(1u, frame_sets.size());
+}
+
+// Checks that a sampler can be started while another is running.
+PROFILER_TEST_F(StackSamplingProfilerTest, MultipleStart) {
+  WithTargetThread([](PlatformThreadId target_thread_id) {
+    std::vector<SamplingParams> params(2);
+
+    params[0].initial_delay = AVeryLongTimeDelta();
+    params[0].samples_per_profile = 1;
+
+    params[1].sampling_interval = TimeDelta::FromMilliseconds(1);
+    params[1].samples_per_profile = 1;
+
+    std::vector<std::unique_ptr<TestProfilerInfo>> profiler_infos =
+        CreateProfilers(target_thread_id, params);
+
+    profiler_infos[0]->profiler.Start();
+    profiler_infos[1]->profiler.Start();
+    profiler_infos[1]->completed.Wait();
+    EXPECT_EQ(1u, profiler_infos[1]->profile.frame_sets.size());
+  });
+}
+
+// Checks that the profile duration and the sampling interval are calculated
+// correctly. Also checks that RecordAnnotations() is invoked each time a sample
+// is recorded.
+PROFILER_TEST_F(StackSamplingProfilerTest, ProfileGeneralInfo) {
+  WithTargetThread([](PlatformThreadId target_thread_id) {
+    SamplingParams params;
+    params.sampling_interval = TimeDelta::FromMilliseconds(1);
+    params.samples_per_profile = 3;
+
+    TestProfilerInfo profiler_info(target_thread_id, params);
+
+    profiler_info.profiler.Start();
+    profiler_info.completed.Wait();
+    EXPECT_EQ(3u, profiler_info.profile.frame_sets.size());
+
+    // The profile duration should be greater than the total sampling intervals.
+    EXPECT_GT(profiler_info.profile.profile_duration,
+              profiler_info.profile.sampling_period * 3);
+
+    EXPECT_EQ(TimeDelta::FromMilliseconds(1),
+              profiler_info.profile.sampling_period);
+
+    // The number of invocations of RecordAnnotations() should be equal to the
+    // number of samples recorded.
+    EXPECT_EQ(3, profiler_info.profile.annotation_count);
+  });
+}
+
+// Checks that the sampling thread can shut down.
+PROFILER_TEST_F(StackSamplingProfilerTest, SamplerIdleShutdown) {
+  SamplingParams params;
+  params.sampling_interval = TimeDelta::FromMilliseconds(0);
+  params.samples_per_profile = 1;
+
+  InternalFrameSets frame_sets = CaptureFrameSets(params, AVeryLongTimeDelta());
+  ASSERT_EQ(1u, frame_sets.size());
+
+  // Capture thread should still be running at this point.
+  ASSERT_TRUE(StackSamplingProfiler::TestAPI::IsSamplingThreadRunning());
+
+  // Initiate an "idle" shutdown and ensure it happens. Idle-shutdown was
+  // disabled by the test fixture so the test will fail due to a timeout if
+  // it does not exit.
+  StackSamplingProfiler::TestAPI::PerformSamplingThreadIdleShutdown(false);
+
+  // While the shutdown has been initiated, the actual exit of the thread still
+  // happens asynchronously. Watch until the thread actually exits. This test
+  // will time-out in the case of failure.
+  while (StackSamplingProfiler::TestAPI::IsSamplingThreadRunning())
+    PlatformThread::Sleep(TimeDelta::FromMilliseconds(1));
+}
+
+// Checks that additional requests will restart a stopped profiler.
+PROFILER_TEST_F(StackSamplingProfilerTest,
+                WillRestartSamplerAfterIdleShutdown) {
+  SamplingParams params;
+  params.sampling_interval = TimeDelta::FromMilliseconds(0);
+  params.samples_per_profile = 1;
+
+  InternalFrameSets frame_sets = CaptureFrameSets(params, AVeryLongTimeDelta());
+  ASSERT_EQ(1u, frame_sets.size());
+
+  // Capture thread should still be running at this point.
+  ASSERT_TRUE(StackSamplingProfiler::TestAPI::IsSamplingThreadRunning());
+
+  // Post a ShutdownTask on the sampling thread which, when executed, will
+  // mark the thread as EXITING and begin shut down of the thread.
+  StackSamplingProfiler::TestAPI::PerformSamplingThreadIdleShutdown(false);
+
+  // Ensure another capture will start the sampling thread and run.
+  frame_sets = CaptureFrameSets(params, AVeryLongTimeDelta());
+  ASSERT_EQ(1u, frame_sets.size());
+  EXPECT_TRUE(StackSamplingProfiler::TestAPI::IsSamplingThreadRunning());
+}
+
+// Checks that it's safe to stop a task after it's completed and the sampling
+// thread has shut-down for being idle.
+PROFILER_TEST_F(StackSamplingProfilerTest, StopAfterIdleShutdown) {
+  WithTargetThread([](PlatformThreadId target_thread_id) {
+    SamplingParams params;
+
+    params.sampling_interval = TimeDelta::FromMilliseconds(1);
+    params.samples_per_profile = 1;
+
+    TestProfilerInfo profiler_info(target_thread_id, params);
+
+    profiler_info.profiler.Start();
+    profiler_info.completed.Wait();
+
+    // Capture thread should still be running at this point.
+    ASSERT_TRUE(StackSamplingProfiler::TestAPI::IsSamplingThreadRunning());
+
+    // Perform an idle shutdown.
+    StackSamplingProfiler::TestAPI::PerformSamplingThreadIdleShutdown(false);
+
+    // Stop should be safe though its impossible to know at this moment if the
+    // sampling thread has completely exited or will just "stop soon".
+    profiler_info.profiler.Stop();
+  });
+}
+
+// Checks that profilers can run both before and after the sampling thread has
+// started.
+PROFILER_TEST_F(StackSamplingProfilerTest,
+                ProfileBeforeAndAfterSamplingThreadRunning) {
+  WithTargetThread([](PlatformThreadId target_thread_id) {
+    std::vector<SamplingParams> params(2);
+
+    params[0].initial_delay = AVeryLongTimeDelta();
+    params[0].sampling_interval = TimeDelta::FromMilliseconds(1);
+    params[0].samples_per_profile = 1;
+
+    params[1].initial_delay = TimeDelta::FromMilliseconds(0);
+    params[1].sampling_interval = TimeDelta::FromMilliseconds(1);
+    params[1].samples_per_profile = 1;
+
+    std::vector<std::unique_ptr<TestProfilerInfo>> profiler_infos =
+        CreateProfilers(target_thread_id, params);
+
+    // First profiler is started when there has never been a sampling thread.
+    EXPECT_FALSE(StackSamplingProfiler::TestAPI::IsSamplingThreadRunning());
+    profiler_infos[0]->profiler.Start();
+    // Second profiler is started when sampling thread is already running.
+    EXPECT_TRUE(StackSamplingProfiler::TestAPI::IsSamplingThreadRunning());
+    profiler_infos[1]->profiler.Start();
+
+    // Only the second profiler should finish before test times out.
+    size_t completed_profiler = WaitForSamplingComplete(profiler_infos);
+    EXPECT_EQ(1U, completed_profiler);
+  });
+}
+
+// Checks that an idle-shutdown task will abort if a new profiler starts
+// between when it was posted and when it runs.
+PROFILER_TEST_F(StackSamplingProfilerTest, IdleShutdownAbort) {
+  WithTargetThread([](PlatformThreadId target_thread_id) {
+    SamplingParams params;
+
+    params.sampling_interval = TimeDelta::FromMilliseconds(1);
+    params.samples_per_profile = 1;
+
+    TestProfilerInfo profiler_info(target_thread_id, params);
+
+    profiler_info.profiler.Start();
+    profiler_info.completed.Wait();
+    EXPECT_EQ(1u, profiler_info.profile.frame_sets.size());
+
+    // Perform an idle shutdown but simulate that a new capture is started
+    // before it can actually run.
+    StackSamplingProfiler::TestAPI::PerformSamplingThreadIdleShutdown(true);
+
+    // Though the shutdown-task has been executed, any actual exit of the
+    // thread is asynchronous so there is no way to detect that *didn't* exit
+    // except to wait a reasonable amount of time and then check. Since the
+    // thread was just running ("perform" blocked until it was), it should
+    // finish almost immediately and without any waiting for tasks or events.
+    PlatformThread::Sleep(TimeDelta::FromMilliseconds(200));
+    EXPECT_TRUE(StackSamplingProfiler::TestAPI::IsSamplingThreadRunning());
+
+    // Ensure that it's still possible to run another sampler.
+    TestProfilerInfo another_info(target_thread_id, params);
+    another_info.profiler.Start();
+    another_info.completed.Wait();
+    EXPECT_EQ(1u, another_info.profile.frame_sets.size());
+  });
+}
+
+// Checks that synchronized multiple sampling requests execute in parallel.
+PROFILER_TEST_F(StackSamplingProfilerTest, ConcurrentProfiling_InSync) {
+  WithTargetThread([](PlatformThreadId target_thread_id) {
+    std::vector<SamplingParams> params(2);
+
+    // Providing an initial delay makes it more likely that both will be
+    // scheduled before either starts to run. Once started, samples will
+    // run ordered by their scheduled, interleaved times regardless of
+    // whatever interval the thread wakes up. Thus, total execution time
+    // will be 10ms (delay) + 10x1ms (sampling) + 1/2 timer minimum interval.
+    params[0].initial_delay = TimeDelta::FromMilliseconds(10);
+    params[0].sampling_interval = TimeDelta::FromMilliseconds(1);
+    params[0].samples_per_profile = 9;
+
+    params[1].initial_delay = TimeDelta::FromMilliseconds(11);
+    params[1].sampling_interval = TimeDelta::FromMilliseconds(1);
+    params[1].samples_per_profile = 8;
+
+    std::vector<std::unique_ptr<TestProfilerInfo>> profiler_infos =
+        CreateProfilers(target_thread_id, params);
+
+    profiler_infos[0]->profiler.Start();
+    profiler_infos[1]->profiler.Start();
+
+    // Wait for one profiler to finish.
+    size_t completed_profiler = WaitForSamplingComplete(profiler_infos);
+
+    size_t other_profiler = 1 - completed_profiler;
+    // Wait for the other profiler to finish.
+    profiler_infos[other_profiler]->completed.Wait();
+
+    // Ensure each got the correct number of frame sets.
+    EXPECT_EQ(9u, profiler_infos[0]->profile.frame_sets.size());
+    EXPECT_EQ(8u, profiler_infos[1]->profile.frame_sets.size());
+  });
+}
+
+// Checks that several mixed sampling requests execute in parallel.
+PROFILER_TEST_F(StackSamplingProfilerTest, ConcurrentProfiling_Mixed) {
+  WithTargetThread([](PlatformThreadId target_thread_id) {
+    std::vector<SamplingParams> params(3);
+
+    params[0].initial_delay = TimeDelta::FromMilliseconds(8);
+    params[0].sampling_interval = TimeDelta::FromMilliseconds(4);
+    params[0].samples_per_profile = 10;
+
+    params[1].initial_delay = TimeDelta::FromMilliseconds(9);
+    params[1].sampling_interval = TimeDelta::FromMilliseconds(3);
+    params[1].samples_per_profile = 10;
+
+    params[2].initial_delay = TimeDelta::FromMilliseconds(10);
+    params[2].sampling_interval = TimeDelta::FromMilliseconds(2);
+    params[2].samples_per_profile = 10;
+
+    std::vector<std::unique_ptr<TestProfilerInfo>> profiler_infos =
+        CreateProfilers(target_thread_id, params);
+
+    for (size_t i = 0; i < profiler_infos.size(); ++i)
+      profiler_infos[i]->profiler.Start();
+
+    // Wait for one profiler to finish.
+    size_t completed_profiler = WaitForSamplingComplete(profiler_infos);
+    EXPECT_EQ(10u,
+              profiler_infos[completed_profiler]->profile.frame_sets.size());
+    // Stop and destroy all profilers, always in the same order. Don't crash.
+    for (size_t i = 0; i < profiler_infos.size(); ++i)
+      profiler_infos[i]->profiler.Stop();
+    for (size_t i = 0; i < profiler_infos.size(); ++i)
+      profiler_infos[i].reset();
+  });
+}
+
+// Checks that a stack that runs through another library produces a stack with
+// the expected functions.
+// macOS ASAN is not yet supported - crbug.com/718628.
+#if !(defined(ADDRESS_SANITIZER) && defined(OS_MACOSX))
+#define MAYBE_OtherLibrary OtherLibrary
+#else
+#define MAYBE_OtherLibrary DISABLED_OtherLibrary
+#endif
+PROFILER_TEST_F(StackSamplingProfilerTest, MAYBE_OtherLibrary) {
+  SamplingParams params;
+  params.sampling_interval = TimeDelta::FromMilliseconds(0);
+  params.samples_per_profile = 1;
+
+  Profile profile;
+  {
+    ScopedNativeLibrary other_library(LoadOtherLibrary());
+    WithTargetThread(
+        [&params, &profile](PlatformThreadId target_thread_id) {
+          WaitableEvent sampling_thread_completed(
+              WaitableEvent::ResetPolicy::MANUAL,
+              WaitableEvent::InitialState::NOT_SIGNALED);
+          StackSamplingProfiler profiler(
+              target_thread_id, params,
+              std::make_unique<TestProfileBuilder>(
+                  BindLambdaForTesting([&profile, &sampling_thread_completed](
+                                           Profile result_profile) {
+                    profile = std::move(result_profile);
+                    sampling_thread_completed.Signal();
+                  })));
+          profiler.Start();
+          sampling_thread_completed.Wait();
+        },
+        StackConfiguration(StackConfiguration::WITH_OTHER_LIBRARY,
+                           other_library.get()));
+  }
+
+  // Look up the frames.
+  ASSERT_EQ(1u, profile.frame_sets.size());
+  const InternalFrames& frames = profile.frame_sets[0];
+
+  // Check that the stack contains a frame for
+  // TargetThread::CallThroughOtherLibrary().
+  InternalFrames::const_iterator other_library_frame =
+      FindFirstFrameWithinFunction(frames,
+                                   &TargetThread::CallThroughOtherLibrary);
+  ASSERT_TRUE(other_library_frame != frames.end())
+      << "Function at "
+      << MaybeFixupFunctionAddressForILT(reinterpret_cast<const void*>(
+             &TargetThread::CallThroughOtherLibrary))
+      << " was not found in stack:\n"
+      << FormatSampleForDiagnosticOutput(frames);
+
+  // Check that the stack contains a frame for
+  // TargetThread::SignalAndWaitUntilSignaled().
+  InternalFrames::const_iterator end_frame = FindFirstFrameWithinFunction(
+      frames, &TargetThread::SignalAndWaitUntilSignaled);
+  ASSERT_TRUE(end_frame != frames.end())
+      << "Function at "
+      << MaybeFixupFunctionAddressForILT(reinterpret_cast<const void*>(
+             &TargetThread::SignalAndWaitUntilSignaled))
+      << " was not found in stack:\n"
+      << FormatSampleForDiagnosticOutput(frames);
+
+  // The stack should look like this, resulting in three frames between
+  // SignalAndWaitUntilSignaled and CallThroughOtherLibrary:
+  //
+  // ... WaitableEvent and system frames ...
+  // TargetThread::SignalAndWaitUntilSignaled
+  // TargetThread::OtherLibraryCallback
+  // InvokeCallbackFunction (in other library)
+  // TargetThread::CallThroughOtherLibrary
+  EXPECT_EQ(3, other_library_frame - end_frame)
+      << "Stack:\n"
+      << FormatSampleForDiagnosticOutput(frames);
+}
+
+// Checks that a stack that runs through a library that is unloading produces a
+// stack, and doesn't crash.
+// Unloading is synchronous on the Mac, so this test is inapplicable.
+#if !defined(OS_MACOSX)
+#define MAYBE_UnloadingLibrary UnloadingLibrary
+#else
+#define MAYBE_UnloadingLibrary DISABLED_UnloadingLibrary
+#endif
+PROFILER_TEST_F(StackSamplingProfilerTest, MAYBE_UnloadingLibrary) {
+  TestLibraryUnload(false);
+}
+
+// Checks that a stack that runs through a library that has been unloaded
+// produces a stack, and doesn't crash.
+// macOS ASAN is not yet supported - crbug.com/718628.
+#if !(defined(ADDRESS_SANITIZER) && defined(OS_MACOSX))
+#define MAYBE_UnloadedLibrary UnloadedLibrary
+#else
+#define MAYBE_UnloadedLibrary DISABLED_UnloadedLibrary
+#endif
+PROFILER_TEST_F(StackSamplingProfilerTest, MAYBE_UnloadedLibrary) {
+  TestLibraryUnload(true);
+}
+
+// Checks that different threads can be sampled in parallel.
+PROFILER_TEST_F(StackSamplingProfilerTest, MultipleSampledThreads) {
+  // Create target threads. The extra parethesis around the StackConfiguration
+  // call are to avoid the most-vexing-parse problem.
+  TargetThread target_thread1((StackConfiguration(StackConfiguration::NORMAL)));
+  TargetThread target_thread2((StackConfiguration(StackConfiguration::NORMAL)));
+  PlatformThreadHandle target_thread_handle1, target_thread_handle2;
+  EXPECT_TRUE(
+      PlatformThread::Create(0, &target_thread1, &target_thread_handle1));
+  EXPECT_TRUE(
+      PlatformThread::Create(0, &target_thread2, &target_thread_handle2));
+  target_thread1.WaitForThreadStart();
+  target_thread2.WaitForThreadStart();
+
+  // Providing an initial delay makes it more likely that both will be
+  // scheduled before either starts to run. Once started, samples will
+  // run ordered by their scheduled, interleaved times regardless of
+  // whatever interval the thread wakes up.
+  SamplingParams params1, params2;
+  params1.initial_delay = TimeDelta::FromMilliseconds(10);
+  params1.sampling_interval = TimeDelta::FromMilliseconds(1);
+  params1.samples_per_profile = 9;
+  params2.initial_delay = TimeDelta::FromMilliseconds(10);
+  params2.sampling_interval = TimeDelta::FromMilliseconds(1);
+  params2.samples_per_profile = 8;
+
+  Profile profile1, profile2;
+
+  WaitableEvent sampling_thread_completed1(
+      WaitableEvent::ResetPolicy::MANUAL,
+      WaitableEvent::InitialState::NOT_SIGNALED);
+  StackSamplingProfiler profiler1(
+      target_thread1.id(), params1,
+      std::make_unique<TestProfileBuilder>(BindLambdaForTesting(
+          [&profile1, &sampling_thread_completed1](Profile result_profile) {
+            profile1 = std::move(result_profile);
+            sampling_thread_completed1.Signal();
+          })));
+
+  WaitableEvent sampling_thread_completed2(
+      WaitableEvent::ResetPolicy::MANUAL,
+      WaitableEvent::InitialState::NOT_SIGNALED);
+  StackSamplingProfiler profiler2(
+      target_thread2.id(), params2,
+      std::make_unique<TestProfileBuilder>(BindLambdaForTesting(
+          [&profile2, &sampling_thread_completed2](Profile result_profile) {
+            profile2 = std::move(result_profile);
+            sampling_thread_completed2.Signal();
+          })));
+
+  // Finally the real work.
+  profiler1.Start();
+  profiler2.Start();
+  sampling_thread_completed1.Wait();
+  sampling_thread_completed2.Wait();
+  EXPECT_EQ(9u, profile1.frame_sets.size());
+  EXPECT_EQ(8u, profile2.frame_sets.size());
+
+  target_thread1.SignalThreadToFinish();
+  target_thread2.SignalThreadToFinish();
+  PlatformThread::Join(target_thread_handle1);
+  PlatformThread::Join(target_thread_handle2);
+}
+
+// A simple thread that runs a profiler on another thread.
+class ProfilerThread : public SimpleThread {
+ public:
+  ProfilerThread(const std::string& name,
+                 PlatformThreadId thread_id,
+                 const SamplingParams& params)
+      : SimpleThread(name, Options()),
+        run_(WaitableEvent::ResetPolicy::MANUAL,
+             WaitableEvent::InitialState::NOT_SIGNALED),
+        completed_(WaitableEvent::ResetPolicy::MANUAL,
+                   WaitableEvent::InitialState::NOT_SIGNALED),
+        profiler_(thread_id,
+                  params,
+                  std::make_unique<TestProfileBuilder>(
+                      BindLambdaForTesting([this](Profile result_profile) {
+                        profile_ = std::move(result_profile);
+                        completed_.Signal();
+                      }))) {}
+
+  void Run() override {
+    run_.Wait();
+    profiler_.Start();
+  }
+
+  void Go() { run_.Signal(); }
+
+  void Wait() { completed_.Wait(); }
+
+  Profile& profile() { return profile_; }
+
+ private:
+  WaitableEvent run_;
+
+  Profile profile_;
+  WaitableEvent completed_;
+  StackSamplingProfiler profiler_;
+};
+
+// Checks that different threads can run samplers in parallel.
+PROFILER_TEST_F(StackSamplingProfilerTest, MultipleProfilerThreads) {
+  WithTargetThread([](PlatformThreadId target_thread_id) {
+    // Providing an initial delay makes it more likely that both will be
+    // scheduled before either starts to run. Once started, samples will
+    // run ordered by their scheduled, interleaved times regardless of
+    // whatever interval the thread wakes up.
+    SamplingParams params1, params2;
+    params1.initial_delay = TimeDelta::FromMilliseconds(10);
+    params1.sampling_interval = TimeDelta::FromMilliseconds(1);
+    params1.samples_per_profile = 9;
+    params2.initial_delay = TimeDelta::FromMilliseconds(10);
+    params2.sampling_interval = TimeDelta::FromMilliseconds(1);
+    params2.samples_per_profile = 8;
+
+    // Start the profiler threads and give them a moment to get going.
+    ProfilerThread profiler_thread1("profiler1", target_thread_id, params1);
+    ProfilerThread profiler_thread2("profiler2", target_thread_id, params2);
+    profiler_thread1.Start();
+    profiler_thread2.Start();
+    PlatformThread::Sleep(TimeDelta::FromMilliseconds(10));
+
+    // This will (approximately) synchronize the two threads.
+    profiler_thread1.Go();
+    profiler_thread2.Go();
+
+    // Wait for them both to finish and validate collection.
+    profiler_thread1.Wait();
+    profiler_thread2.Wait();
+    EXPECT_EQ(9u, profiler_thread1.profile().frame_sets.size());
+    EXPECT_EQ(8u, profiler_thread2.profile().frame_sets.size());
+
+    profiler_thread1.Join();
+    profiler_thread2.Join();
+  });
+}
+
+}  // namespace base
diff --git a/base/profiler/test_support_library.cc b/base/profiler/test_support_library.cc
new file mode 100644
index 0000000..035f8f7
--- /dev/null
+++ b/base/profiler/test_support_library.cc
@@ -0,0 +1,30 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Note: there is intentionally no header file associated with this library so
+// we don't risk implicitly demand loading it by accessing a symbol.
+
+#if defined(WIN32)
+#define BASE_PROFILER_TEST_SUPPORT_LIBRARY_EXPORT __declspec(dllexport)
+#else  // defined(WIN32)
+#define BASE_PROFILER_TEST_SUPPORT_LIBRARY_EXPORT __attribute__((visibility("default")))
+#endif
+
+namespace base {
+
+// Must be defined in an extern "C" block so we can look up the unmangled name.
+extern "C" {
+
+BASE_PROFILER_TEST_SUPPORT_LIBRARY_EXPORT void InvokeCallbackFunction(
+    void (*function)(void*),
+    void* arg) {
+  function(arg);
+  // Prevent tail call.
+  volatile int i = 0;
+  i = 1;
+}
+
+}  // extern "C"
+
+}  // namespace base
diff --git a/base/profiler/win32_stack_frame_unwinder.cc b/base/profiler/win32_stack_frame_unwinder.cc
new file mode 100644
index 0000000..a3f5f74
--- /dev/null
+++ b/base/profiler/win32_stack_frame_unwinder.cc
@@ -0,0 +1,186 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/profiler/win32_stack_frame_unwinder.h"
+
+#include <windows.h>
+
+#include <utility>
+
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+
+namespace base {
+
+// Win32UnwindFunctions -------------------------------------------------------
+
+const HMODULE ModuleHandleTraits::kNonNullModuleForTesting =
+    reinterpret_cast<HMODULE>(static_cast<uintptr_t>(-1));
+
+// static
+bool ModuleHandleTraits::CloseHandle(HMODULE handle) {
+  if (handle == kNonNullModuleForTesting)
+    return true;
+
+  return ::FreeLibrary(handle) != 0;
+}
+
+// static
+bool ModuleHandleTraits::IsHandleValid(HMODULE handle) {
+  return handle != nullptr;
+}
+
+// static
+HMODULE ModuleHandleTraits::NullHandle() {
+  return nullptr;
+}
+
+namespace {
+
+// Implements the UnwindFunctions interface for the corresponding Win32
+// functions.
+class Win32UnwindFunctions : public Win32StackFrameUnwinder::UnwindFunctions {
+public:
+  Win32UnwindFunctions();
+  ~Win32UnwindFunctions() override;
+
+  PRUNTIME_FUNCTION LookupFunctionEntry(DWORD64 program_counter,
+                                        PDWORD64 image_base) override;
+
+  void VirtualUnwind(DWORD64 image_base,
+                     DWORD64 program_counter,
+                     PRUNTIME_FUNCTION runtime_function,
+                     CONTEXT* context) override;
+
+  ScopedModuleHandle GetModuleForProgramCounter(
+      DWORD64 program_counter) override;
+
+private:
+  DISALLOW_COPY_AND_ASSIGN(Win32UnwindFunctions);
+};
+
+Win32UnwindFunctions::Win32UnwindFunctions() {}
+Win32UnwindFunctions::~Win32UnwindFunctions() {}
+
+PRUNTIME_FUNCTION Win32UnwindFunctions::LookupFunctionEntry(
+    DWORD64 program_counter,
+    PDWORD64 image_base) {
+#ifdef _WIN64
+  return ::RtlLookupFunctionEntry(program_counter, image_base, nullptr);
+#else
+  NOTREACHED();
+  return nullptr;
+#endif
+}
+
+void Win32UnwindFunctions::VirtualUnwind(DWORD64 image_base,
+                                         DWORD64 program_counter,
+                                         PRUNTIME_FUNCTION runtime_function,
+                                         CONTEXT* context) {
+#ifdef _WIN64
+  void* handler_data;
+  ULONG64 establisher_frame;
+  KNONVOLATILE_CONTEXT_POINTERS nvcontext = {};
+  ::RtlVirtualUnwind(UNW_FLAG_NHANDLER, image_base, program_counter,
+                     runtime_function, context, &handler_data,
+                     &establisher_frame, &nvcontext);
+#else
+  NOTREACHED();
+#endif
+}
+
+ScopedModuleHandle Win32UnwindFunctions::GetModuleForProgramCounter(
+    DWORD64 program_counter) {
+  HMODULE module_handle = nullptr;
+  // GetModuleHandleEx() increments the module reference count, which is then
+  // managed and ultimately decremented by ScopedModuleHandle.
+  if (!::GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
+                           reinterpret_cast<LPCTSTR>(program_counter),
+                           &module_handle)) {
+    const DWORD error = ::GetLastError();
+    DCHECK_EQ(ERROR_MOD_NOT_FOUND, static_cast<int>(error));
+  }
+  return ScopedModuleHandle(module_handle);
+}
+
+}  // namespace
+
+// Win32StackFrameUnwinder ----------------------------------------------------
+
+Win32StackFrameUnwinder::UnwindFunctions::~UnwindFunctions() {}
+Win32StackFrameUnwinder::UnwindFunctions::UnwindFunctions() {}
+
+Win32StackFrameUnwinder::Win32StackFrameUnwinder()
+    : Win32StackFrameUnwinder(WrapUnique(new Win32UnwindFunctions)) {}
+
+Win32StackFrameUnwinder::~Win32StackFrameUnwinder() {}
+
+bool Win32StackFrameUnwinder::TryUnwind(CONTEXT* context,
+                                        ScopedModuleHandle* module) {
+#ifdef _WIN64
+  ScopedModuleHandle frame_module =
+      unwind_functions_->GetModuleForProgramCounter(context->Rip);
+  if (!frame_module.IsValid()) {
+    // There's no loaded module containing the instruction pointer. This can be
+    // due to executing code that is not in a module. In particular,
+    // runtime-generated code associated with third-party injected DLLs
+    // typically is not in a module. It can also be due to the the module having
+    // been unloaded since we recorded the stack.  In the latter case the
+    // function unwind information was part of the unloaded module, so it's not
+    // possible to unwind further.
+    //
+    // If a module was found, it's still theoretically possible for the detected
+    // module module to be different than the one that was loaded when the stack
+    // was copied (i.e. if the module was unloaded and a different module loaded
+    // in overlapping memory). This likely would cause a crash, but has not been
+    // observed in practice.
+    return false;
+  }
+
+  ULONG64 image_base;
+  // Try to look up unwind metadata for the current function.
+  PRUNTIME_FUNCTION runtime_function =
+      unwind_functions_->LookupFunctionEntry(context->Rip, &image_base);
+
+  if (runtime_function) {
+    unwind_functions_->VirtualUnwind(image_base, context->Rip, runtime_function,
+                                     context);
+    at_top_frame_ = false;
+  } else {
+    if (at_top_frame_) {
+      at_top_frame_ = false;
+
+      // This is a leaf function (i.e. a function that neither calls a function,
+      // nor allocates any stack space itself) so the return address is at RSP.
+      context->Rip = *reinterpret_cast<DWORD64*>(context->Rsp);
+      context->Rsp += 8;
+    } else {
+      // In theory we shouldn't get here, as it means we've encountered a
+      // function without unwind information below the top of the stack, which
+      // is forbidden by the Microsoft x64 calling convention.
+      //
+      // The one known case in Chrome code that executes this path occurs
+      // because of BoringSSL unwind information inconsistent with the actual
+      // function code. See https://crbug.com/542919.
+      //
+      // Note that dodgy third-party generated code that otherwise would enter
+      // this path should be caught by the module check above, since the code
+      // typically is located outside of a module.
+      return false;
+    }
+  }
+
+  module->Set(frame_module.Take());
+  return true;
+#else
+  NOTREACHED();
+  return false;
+#endif
+}
+
+Win32StackFrameUnwinder::Win32StackFrameUnwinder(
+    std::unique_ptr<UnwindFunctions> unwind_functions)
+    : at_top_frame_(true), unwind_functions_(std::move(unwind_functions)) {}
+
+}  // namespace base
diff --git a/base/profiler/win32_stack_frame_unwinder.h b/base/profiler/win32_stack_frame_unwinder.h
new file mode 100644
index 0000000..c92d50c
--- /dev/null
+++ b/base/profiler/win32_stack_frame_unwinder.h
@@ -0,0 +1,102 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_PROFILER_WIN32_STACK_FRAME_UNWINDER_H_
+#define BASE_PROFILER_WIN32_STACK_FRAME_UNWINDER_H_
+
+#include <windows.h>
+
+#include <memory>
+
+#include "base/base_export.h"
+#include "base/macros.h"
+#include "base/win/scoped_handle.h"
+
+namespace base {
+
+#if !defined(_WIN64)
+// Allows code to compile for x86. Actual support for x86 will require either
+// refactoring these interfaces or separate architecture-specific interfaces.
+struct RUNTIME_FUNCTION {
+  DWORD BeginAddress;
+  DWORD EndAddress;
+};
+using PRUNTIME_FUNCTION = RUNTIME_FUNCTION*;
+#endif  // !defined(_WIN64)
+
+// Traits class to adapt GenericScopedHandle for HMODULES.
+class ModuleHandleTraits : public win::HandleTraits {
+ public:
+  using Handle = HMODULE;
+
+  static bool BASE_EXPORT CloseHandle(HMODULE handle);
+  static bool BASE_EXPORT IsHandleValid(HMODULE handle);
+  static HMODULE BASE_EXPORT NullHandle();
+
+  BASE_EXPORT static const HMODULE kNonNullModuleForTesting;
+
+ private:
+  DISALLOW_IMPLICIT_CONSTRUCTORS(ModuleHandleTraits);
+};
+
+// HMODULE is not really a handle, and has reference count semantics, so the
+// standard VerifierTraits does not apply.
+using ScopedModuleHandle =
+    win::GenericScopedHandle<ModuleHandleTraits, win::DummyVerifierTraits>;
+
+// Instances of this class are expected to be created and destroyed for each
+// stack unwinding. This class is not used while the target thread is suspended,
+// so may allocate from the default heap.
+class BASE_EXPORT Win32StackFrameUnwinder {
+ public:
+  // Interface for Win32 unwind-related functionality this class depends
+  // on. Provides a seam for testing.
+  class BASE_EXPORT UnwindFunctions {
+   public:
+    virtual ~UnwindFunctions();
+
+    virtual PRUNTIME_FUNCTION LookupFunctionEntry(DWORD64 program_counter,
+                                                  PDWORD64 image_base) = 0;
+    virtual void VirtualUnwind(DWORD64 image_base,
+                               DWORD64 program_counter,
+                               PRUNTIME_FUNCTION runtime_function,
+                               CONTEXT* context) = 0;
+
+    // Returns the module containing |program_counter|. Can return null if the
+    // module has been unloaded.
+    virtual ScopedModuleHandle GetModuleForProgramCounter(
+        DWORD64 program_counter) = 0;
+
+   protected:
+    UnwindFunctions();
+
+   private:
+    DISALLOW_COPY_AND_ASSIGN(UnwindFunctions);
+  };
+
+  Win32StackFrameUnwinder();
+  ~Win32StackFrameUnwinder();
+
+  // Attempts to unwind the frame represented by the stack and instruction
+  // pointers in |context|. If successful, updates |context| and provides the
+  // module associated with the frame in |module|.
+  bool TryUnwind(CONTEXT* context, ScopedModuleHandle* module);
+
+ private:
+  // This function is for internal and test purposes only.
+  Win32StackFrameUnwinder(std::unique_ptr<UnwindFunctions> unwind_functions);
+  friend class Win32StackFrameUnwinderTest;
+
+  // State associated with each stack unwinding.
+  bool at_top_frame_;
+  bool unwind_info_present_for_all_frames_;
+
+  std::unique_ptr<UnwindFunctions> unwind_functions_;
+
+  DISALLOW_COPY_AND_ASSIGN(Win32StackFrameUnwinder);
+};
+
+}  // namespace base
+
+#endif  // BASE_PROFILER_WIN32_STACK_FRAME_UNWINDER_H_
diff --git a/base/profiler/win32_stack_frame_unwinder_unittest.cc b/base/profiler/win32_stack_frame_unwinder_unittest.cc
new file mode 100644
index 0000000..cecfe22
--- /dev/null
+++ b/base/profiler/win32_stack_frame_unwinder_unittest.cc
@@ -0,0 +1,223 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/profiler/win32_stack_frame_unwinder.h"
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+namespace {
+
+class TestUnwindFunctions : public Win32StackFrameUnwinder::UnwindFunctions {
+ public:
+  TestUnwindFunctions();
+
+  PRUNTIME_FUNCTION LookupFunctionEntry(DWORD64 program_counter,
+                                        PDWORD64 image_base) override;
+  void VirtualUnwind(DWORD64 image_base,
+                     DWORD64 program_counter,
+                     PRUNTIME_FUNCTION runtime_function,
+                     CONTEXT* context) override;
+  ScopedModuleHandle GetModuleForProgramCounter(
+      DWORD64 program_counter) override;
+
+  // Instructs GetModuleForProgramCounter to return null on the next call.
+  void SetUnloadedModule();
+
+  // These functions set whether the next frame will have a RUNTIME_FUNCTION.
+  void SetHasRuntimeFunction(CONTEXT* context);
+  void SetNoRuntimeFunction(CONTEXT* context);
+
+ private:
+  enum { kImageBaseIncrement = 1 << 20 };
+
+  static RUNTIME_FUNCTION* const kInvalidRuntimeFunction;
+
+  bool module_is_loaded_;
+  DWORD64 expected_program_counter_;
+  DWORD64 next_image_base_;
+  DWORD64 expected_image_base_;
+  RUNTIME_FUNCTION* next_runtime_function_;
+  std::vector<RUNTIME_FUNCTION> runtime_functions_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestUnwindFunctions);
+};
+
+RUNTIME_FUNCTION* const TestUnwindFunctions::kInvalidRuntimeFunction =
+    reinterpret_cast<RUNTIME_FUNCTION*>(static_cast<uintptr_t>(-1));
+
+TestUnwindFunctions::TestUnwindFunctions()
+    : module_is_loaded_(true),
+      expected_program_counter_(0),
+      next_image_base_(kImageBaseIncrement),
+      expected_image_base_(0),
+      next_runtime_function_(kInvalidRuntimeFunction) {
+}
+
+PRUNTIME_FUNCTION TestUnwindFunctions::LookupFunctionEntry(
+    DWORD64 program_counter,
+    PDWORD64 image_base) {
+  EXPECT_EQ(expected_program_counter_, program_counter);
+  *image_base = expected_image_base_ = next_image_base_;
+  next_image_base_ += kImageBaseIncrement;
+  RUNTIME_FUNCTION* return_value = next_runtime_function_;
+  next_runtime_function_ = kInvalidRuntimeFunction;
+  return return_value;
+}
+
+void TestUnwindFunctions::VirtualUnwind(DWORD64 image_base,
+                                        DWORD64 program_counter,
+                                        PRUNTIME_FUNCTION runtime_function,
+                                        CONTEXT* context) {
+  ASSERT_NE(kInvalidRuntimeFunction, runtime_function)
+      << "expected call to SetHasRuntimeFunction() or SetNoRuntimeFunction() "
+      << "before invoking TryUnwind()";
+  EXPECT_EQ(expected_image_base_, image_base);
+  expected_image_base_ = 0;
+  EXPECT_EQ(expected_program_counter_, program_counter);
+  expected_program_counter_ = 0;
+  // This function should only be called when LookupFunctionEntry returns
+  // a RUNTIME_FUNCTION.
+  EXPECT_EQ(&runtime_functions_.back(), runtime_function);
+}
+
+ScopedModuleHandle TestUnwindFunctions::GetModuleForProgramCounter(
+    DWORD64 program_counter) {
+  bool return_non_null_value = module_is_loaded_;
+  module_is_loaded_ = true;
+  return ScopedModuleHandle(return_non_null_value ?
+                            ModuleHandleTraits::kNonNullModuleForTesting :
+                            nullptr);
+}
+
+void TestUnwindFunctions::SetUnloadedModule() {
+  module_is_loaded_ = false;
+}
+
+void TestUnwindFunctions::SetHasRuntimeFunction(CONTEXT* context) {
+  RUNTIME_FUNCTION runtime_function = {};
+  runtime_function.BeginAddress = 16;
+  runtime_function.EndAddress = runtime_function.BeginAddress + 256;
+  runtime_functions_.push_back(runtime_function);
+  next_runtime_function_ = &runtime_functions_.back();
+
+  expected_program_counter_ = context->Rip =
+      next_image_base_ + runtime_function.BeginAddress + 8;
+}
+
+void TestUnwindFunctions::SetNoRuntimeFunction(CONTEXT* context) {
+  expected_program_counter_ = context->Rip = 100;
+  next_runtime_function_ = nullptr;
+}
+
+}  // namespace
+
+class Win32StackFrameUnwinderTest : public testing::Test {
+ protected:
+  Win32StackFrameUnwinderTest() {}
+
+  // This exists so that Win32StackFrameUnwinder's constructor can be private
+  // with a single friend declaration of this test fixture.
+  std::unique_ptr<Win32StackFrameUnwinder> CreateUnwinder();
+
+  // Weak pointer to the unwind functions used by last created unwinder.
+  TestUnwindFunctions* unwind_functions_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(Win32StackFrameUnwinderTest);
+};
+
+std::unique_ptr<Win32StackFrameUnwinder>
+Win32StackFrameUnwinderTest::CreateUnwinder() {
+  std::unique_ptr<TestUnwindFunctions> unwind_functions(
+      new TestUnwindFunctions);
+  unwind_functions_ = unwind_functions.get();
+  return WrapUnique(
+      new Win32StackFrameUnwinder(std::move(unwind_functions)));
+}
+
+// Checks the case where all frames have unwind information.
+TEST_F(Win32StackFrameUnwinderTest, FramesWithUnwindInfo) {
+  std::unique_ptr<Win32StackFrameUnwinder> unwinder = CreateUnwinder();
+  CONTEXT context = {0};
+  ScopedModuleHandle module;
+
+  unwind_functions_->SetHasRuntimeFunction(&context);
+  EXPECT_TRUE(unwinder->TryUnwind(&context, &module));
+  EXPECT_TRUE(module.IsValid());
+
+  unwind_functions_->SetHasRuntimeFunction(&context);
+  module.Set(nullptr);
+  EXPECT_TRUE(unwinder->TryUnwind(&context, &module));
+  EXPECT_TRUE(module.IsValid());
+
+  unwind_functions_->SetHasRuntimeFunction(&context);
+  module.Set(nullptr);
+  EXPECT_TRUE(unwinder->TryUnwind(&context, &module));
+  EXPECT_TRUE(module.IsValid());
+}
+
+// Checks that an instruction pointer in an unloaded module fails to unwind.
+TEST_F(Win32StackFrameUnwinderTest, UnloadedModule) {
+  std::unique_ptr<Win32StackFrameUnwinder> unwinder = CreateUnwinder();
+  CONTEXT context = {0};
+  ScopedModuleHandle module;
+
+  unwind_functions_->SetUnloadedModule();
+  EXPECT_FALSE(unwinder->TryUnwind(&context, &module));
+}
+
+// Checks that the CONTEXT's stack pointer gets popped when the top frame has no
+// unwind information.
+TEST_F(Win32StackFrameUnwinderTest, FrameAtTopWithoutUnwindInfo) {
+  std::unique_ptr<Win32StackFrameUnwinder> unwinder = CreateUnwinder();
+  CONTEXT context = {0};
+  ScopedModuleHandle module;
+  DWORD64 next_ip = 0x0123456789abcdef;
+  DWORD64 original_rsp = reinterpret_cast<DWORD64>(&next_ip);
+  context.Rsp = original_rsp;
+
+  unwind_functions_->SetNoRuntimeFunction(&context);
+  EXPECT_TRUE(unwinder->TryUnwind(&context, &module));
+  EXPECT_EQ(next_ip, context.Rip);
+  EXPECT_EQ(original_rsp + 8, context.Rsp);
+  EXPECT_TRUE(module.IsValid());
+
+  unwind_functions_->SetHasRuntimeFunction(&context);
+  module.Set(nullptr);
+  EXPECT_TRUE(unwinder->TryUnwind(&context, &module));
+  EXPECT_TRUE(module.IsValid());
+
+  unwind_functions_->SetHasRuntimeFunction(&context);
+  module.Set(nullptr);
+  EXPECT_TRUE(unwinder->TryUnwind(&context, &module));
+  EXPECT_TRUE(module.IsValid());
+}
+
+// Checks that a frame below the top of the stack with missing unwind info
+// terminates the unwinding.
+TEST_F(Win32StackFrameUnwinderTest, FrameBelowTopWithoutUnwindInfo) {
+  {
+    // First stack, with a bad function below the top of the stack.
+    std::unique_ptr<Win32StackFrameUnwinder> unwinder = CreateUnwinder();
+    CONTEXT context = {0};
+    ScopedModuleHandle module;
+    unwind_functions_->SetHasRuntimeFunction(&context);
+    EXPECT_TRUE(unwinder->TryUnwind(&context, &module));
+    EXPECT_TRUE(module.IsValid());
+
+    unwind_functions_->SetNoRuntimeFunction(&context);
+    EXPECT_FALSE(unwinder->TryUnwind(&context, &module));
+  }
+}
+
+}  // namespace base
diff --git a/base/run_loop_unittest.cc b/base/run_loop_unittest.cc
new file mode 100644
index 0000000..c7db14a
--- /dev/null
+++ b/base/run_loop_unittest.cc
@@ -0,0 +1,636 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/run_loop.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/containers/queue.h"
+#include "base/location.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted.h"
+#include "base/single_thread_task_runner.h"
+#include "base/synchronization/lock.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/test/gtest_util.h"
+#include "base/test/scoped_task_environment.h"
+#include "base/test/test_timeouts.h"
+#include "base/threading/platform_thread.h"
+#include "base/threading/thread.h"
+#include "base/threading/thread_checker_impl.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "build/build_config.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+namespace {
+
+void QuitWhenIdleTask(RunLoop* run_loop, int* counter) {
+  run_loop->QuitWhenIdle();
+  ++(*counter);
+}
+
+void ShouldRunTask(int* counter) {
+  ++(*counter);
+}
+
+void ShouldNotRunTask() {
+  ADD_FAILURE() << "Ran a task that shouldn't run.";
+}
+
+void RunNestedLoopTask(int* counter) {
+  RunLoop nested_run_loop(RunLoop::Type::kNestableTasksAllowed);
+
+  // This task should quit |nested_run_loop| but not the main RunLoop.
+  ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, BindOnce(&QuitWhenIdleTask, Unretained(&nested_run_loop),
+                          Unretained(counter)));
+
+  ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+      FROM_HERE, BindOnce(&ShouldNotRunTask), TimeDelta::FromDays(1));
+
+  nested_run_loop.Run();
+
+  ++(*counter);
+}
+
+// A simple SingleThreadTaskRunner that just queues undelayed tasks (and ignores
+// delayed tasks). Tasks can then be processed one by one by ProcessTask() which
+// will return true if it processed a task and false otherwise.
+class SimpleSingleThreadTaskRunner : public SingleThreadTaskRunner {
+ public:
+  SimpleSingleThreadTaskRunner() = default;
+
+  bool PostDelayedTask(const Location& from_here,
+                       OnceClosure task,
+                       base::TimeDelta delay) override {
+    if (delay > base::TimeDelta())
+      return false;
+    AutoLock auto_lock(tasks_lock_);
+    pending_tasks_.push(std::move(task));
+    return true;
+  }
+
+  bool PostNonNestableDelayedTask(const Location& from_here,
+                                  OnceClosure task,
+                                  base::TimeDelta delay) override {
+    return PostDelayedTask(from_here, std::move(task), delay);
+  }
+
+  bool RunsTasksInCurrentSequence() const override {
+    return origin_thread_checker_.CalledOnValidThread();
+  }
+
+  bool ProcessSingleTask() {
+    OnceClosure task;
+    {
+      AutoLock auto_lock(tasks_lock_);
+      if (pending_tasks_.empty())
+        return false;
+      task = std::move(pending_tasks_.front());
+      pending_tasks_.pop();
+    }
+    // It's important to Run() after pop() and outside the lock as |task| may
+    // run a nested loop which will re-enter ProcessSingleTask().
+    std::move(task).Run();
+    return true;
+  }
+
+ private:
+  ~SimpleSingleThreadTaskRunner() override = default;
+
+  Lock tasks_lock_;
+  base::queue<OnceClosure> pending_tasks_;
+
+  // RunLoop relies on RunsTasksInCurrentSequence() signal. Use a
+  // ThreadCheckerImpl to be able to reliably provide that signal even in
+  // non-dcheck builds.
+  ThreadCheckerImpl origin_thread_checker_;
+
+  DISALLOW_COPY_AND_ASSIGN(SimpleSingleThreadTaskRunner);
+};
+
+// The basis of all TestDelegates, allows safely injecting a OnceClosure to be
+// run in the next idle phase of this delegate's Run() implementation. This can
+// be used to have code run on a thread that is otherwise livelocked in an idle
+// phase (sometimes a simple PostTask() won't do it -- e.g. when processing
+// application tasks is disallowed).
+class InjectableTestDelegate : public RunLoop::Delegate {
+ public:
+  void InjectClosureOnDelegate(OnceClosure closure) {
+    AutoLock auto_lock(closure_lock_);
+    closure_ = std::move(closure);
+  }
+
+  bool RunInjectedClosure() {
+    AutoLock auto_lock(closure_lock_);
+    if (closure_.is_null())
+      return false;
+    std::move(closure_).Run();
+    return true;
+  }
+
+ private:
+  Lock closure_lock_;
+  OnceClosure closure_;
+};
+
+// A simple test RunLoop::Delegate to exercise Runloop logic independent of any
+// other base constructs. BindToCurrentThread() must be called before this
+// TestBoundDelegate is operational.
+class TestBoundDelegate final : public InjectableTestDelegate {
+ public:
+  TestBoundDelegate() = default;
+
+  // Makes this TestBoundDelegate become the RunLoop::Delegate and
+  // ThreadTaskRunnerHandle for this thread.
+  void BindToCurrentThread() {
+    thread_task_runner_handle_ =
+        std::make_unique<ThreadTaskRunnerHandle>(simple_task_runner_);
+    RunLoop::RegisterDelegateForCurrentThread(this);
+  }
+
+ private:
+  void Run(bool application_tasks_allowed) override {
+    if (nested_run_allowing_tasks_incoming_) {
+      EXPECT_TRUE(RunLoop::IsNestedOnCurrentThread());
+      EXPECT_TRUE(application_tasks_allowed);
+    } else if (RunLoop::IsNestedOnCurrentThread()) {
+      EXPECT_FALSE(application_tasks_allowed);
+    }
+    nested_run_allowing_tasks_incoming_ = false;
+
+    while (!should_quit_) {
+      if (application_tasks_allowed && simple_task_runner_->ProcessSingleTask())
+        continue;
+
+      if (ShouldQuitWhenIdle())
+        break;
+
+      if (RunInjectedClosure())
+        continue;
+
+      PlatformThread::YieldCurrentThread();
+    }
+    should_quit_ = false;
+  }
+
+  void Quit() override { should_quit_ = true; }
+
+  void EnsureWorkScheduled() override {
+    nested_run_allowing_tasks_incoming_ = true;
+  }
+
+  // True if the next invocation of Run() is expected to be from a
+  // kNestableTasksAllowed RunLoop.
+  bool nested_run_allowing_tasks_incoming_ = false;
+
+  scoped_refptr<SimpleSingleThreadTaskRunner> simple_task_runner_ =
+      MakeRefCounted<SimpleSingleThreadTaskRunner>();
+
+  std::unique_ptr<ThreadTaskRunnerHandle> thread_task_runner_handle_;
+
+  bool should_quit_ = false;
+};
+
+enum class RunLoopTestType {
+  // Runs all RunLoopTests under a ScopedTaskEnvironment to make sure real world
+  // scenarios work.
+  kRealEnvironment,
+
+  // Runs all RunLoopTests under a test RunLoop::Delegate to make sure the
+  // delegate interface fully works standalone.
+  kTestDelegate,
+};
+
+// The task environment for the RunLoopTest of a given type. A separate class
+// so it can be instantiated on the stack in the RunLoopTest fixture.
+class RunLoopTestEnvironment {
+ public:
+  RunLoopTestEnvironment(RunLoopTestType type) {
+    switch (type) {
+      case RunLoopTestType::kRealEnvironment: {
+        task_environment_ = std::make_unique<test::ScopedTaskEnvironment>();
+        break;
+      }
+      case RunLoopTestType::kTestDelegate: {
+        auto test_delegate = std::make_unique<TestBoundDelegate>();
+        test_delegate->BindToCurrentThread();
+        test_delegate_ = std::move(test_delegate);
+        break;
+      }
+    }
+  }
+
+ private:
+  // Instantiates one or the other based on the RunLoopTestType.
+  std::unique_ptr<test::ScopedTaskEnvironment> task_environment_;
+  std::unique_ptr<InjectableTestDelegate> test_delegate_;
+};
+
+class RunLoopTest : public testing::TestWithParam<RunLoopTestType> {
+ protected:
+  RunLoopTest() : test_environment_(GetParam()) {}
+
+  RunLoopTestEnvironment test_environment_;
+  RunLoop run_loop_;
+  int counter_ = 0;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(RunLoopTest);
+};
+
+}  // namespace
+
+TEST_P(RunLoopTest, QuitWhenIdle) {
+  ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, BindOnce(&QuitWhenIdleTask, Unretained(&run_loop_),
+                          Unretained(&counter_)));
+  ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, BindOnce(&ShouldRunTask, Unretained(&counter_)));
+  ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+      FROM_HERE, BindOnce(&ShouldNotRunTask), TimeDelta::FromDays(1));
+
+  run_loop_.Run();
+  EXPECT_EQ(2, counter_);
+}
+
+TEST_P(RunLoopTest, QuitWhenIdleNestedLoop) {
+  ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, BindOnce(&RunNestedLoopTask, Unretained(&counter_)));
+  ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, BindOnce(&QuitWhenIdleTask, Unretained(&run_loop_),
+                          Unretained(&counter_)));
+  ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, BindOnce(&ShouldRunTask, Unretained(&counter_)));
+  ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+      FROM_HERE, BindOnce(&ShouldNotRunTask), TimeDelta::FromDays(1));
+
+  run_loop_.Run();
+  EXPECT_EQ(4, counter_);
+}
+
+TEST_P(RunLoopTest, QuitWhenIdleClosure) {
+  ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+                                          run_loop_.QuitWhenIdleClosure());
+  ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, BindOnce(&ShouldRunTask, Unretained(&counter_)));
+  ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+      FROM_HERE, BindOnce(&ShouldNotRunTask), TimeDelta::FromDays(1));
+
+  run_loop_.Run();
+  EXPECT_EQ(1, counter_);
+}
+
+// Verify that the QuitWhenIdleClosure() can run after the RunLoop has been
+// deleted. It should have no effect.
+TEST_P(RunLoopTest, QuitWhenIdleClosureAfterRunLoopScope) {
+  Closure quit_when_idle_closure;
+  {
+    RunLoop run_loop;
+    quit_when_idle_closure = run_loop.QuitWhenIdleClosure();
+    run_loop.RunUntilIdle();
+  }
+  quit_when_idle_closure.Run();
+}
+
+// Verify that Quit can be executed from another sequence.
+TEST_P(RunLoopTest, QuitFromOtherSequence) {
+  Thread other_thread("test");
+  other_thread.Start();
+  scoped_refptr<SequencedTaskRunner> other_sequence =
+      other_thread.task_runner();
+
+  // Always expected to run before asynchronous Quit() kicks in.
+  ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(&ShouldRunTask, Unretained(&counter_)));
+
+  WaitableEvent loop_was_quit(WaitableEvent::ResetPolicy::MANUAL,
+                              WaitableEvent::InitialState::NOT_SIGNALED);
+  other_sequence->PostTask(
+      FROM_HERE, base::BindOnce([](RunLoop* run_loop) { run_loop->Quit(); },
+                                Unretained(&run_loop_)));
+  other_sequence->PostTask(
+      FROM_HERE,
+      base::BindOnce(&WaitableEvent::Signal, base::Unretained(&loop_was_quit)));
+
+  // Anything that's posted after the Quit closure was posted back to this
+  // sequence shouldn't get a chance to run.
+  loop_was_quit.Wait();
+  ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+                                          base::BindOnce(&ShouldNotRunTask));
+
+  run_loop_.Run();
+
+  EXPECT_EQ(1, counter_);
+}
+
+// Verify that QuitClosure can be executed from another sequence.
+TEST_P(RunLoopTest, QuitFromOtherSequenceWithClosure) {
+  Thread other_thread("test");
+  other_thread.Start();
+  scoped_refptr<SequencedTaskRunner> other_sequence =
+      other_thread.task_runner();
+
+  // Always expected to run before asynchronous Quit() kicks in.
+  ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(&ShouldRunTask, Unretained(&counter_)));
+
+  WaitableEvent loop_was_quit(WaitableEvent::ResetPolicy::MANUAL,
+                              WaitableEvent::InitialState::NOT_SIGNALED);
+  other_sequence->PostTask(FROM_HERE, run_loop_.QuitClosure());
+  other_sequence->PostTask(
+      FROM_HERE,
+      base::BindOnce(&WaitableEvent::Signal, base::Unretained(&loop_was_quit)));
+
+  // Anything that's posted after the Quit closure was posted back to this
+  // sequence shouldn't get a chance to run.
+  loop_was_quit.Wait();
+  ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+                                          base::BindOnce(&ShouldNotRunTask));
+
+  run_loop_.Run();
+
+  EXPECT_EQ(1, counter_);
+}
+
+// Verify that Quit can be executed from another sequence even when the
+// Quit is racing with Run() -- i.e. forgo the WaitableEvent used above.
+TEST_P(RunLoopTest, QuitFromOtherSequenceRacy) {
+  Thread other_thread("test");
+  other_thread.Start();
+  scoped_refptr<SequencedTaskRunner> other_sequence =
+      other_thread.task_runner();
+
+  // Always expected to run before asynchronous Quit() kicks in.
+  ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(&ShouldRunTask, Unretained(&counter_)));
+
+  other_sequence->PostTask(
+      FROM_HERE, base::BindOnce([](RunLoop* run_loop) { run_loop->Quit(); },
+                                Unretained(&run_loop_)));
+
+  run_loop_.Run();
+
+  EXPECT_EQ(1, counter_);
+}
+
+// Verify that QuitClosure can be executed from another sequence even when the
+// Quit is racing with Run() -- i.e. forgo the WaitableEvent used above.
+TEST_P(RunLoopTest, QuitFromOtherSequenceRacyWithClosure) {
+  Thread other_thread("test");
+  other_thread.Start();
+  scoped_refptr<SequencedTaskRunner> other_sequence =
+      other_thread.task_runner();
+
+  // Always expected to run before asynchronous Quit() kicks in.
+  ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(&ShouldRunTask, Unretained(&counter_)));
+
+  other_sequence->PostTask(FROM_HERE, run_loop_.QuitClosure());
+
+  run_loop_.Run();
+
+  EXPECT_EQ(1, counter_);
+}
+
+// Verify that QuitWhenIdle can be executed from another sequence.
+TEST_P(RunLoopTest, QuitWhenIdleFromOtherSequence) {
+  Thread other_thread("test");
+  other_thread.Start();
+  scoped_refptr<SequencedTaskRunner> other_sequence =
+      other_thread.task_runner();
+
+  ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(&ShouldRunTask, Unretained(&counter_)));
+
+  other_sequence->PostTask(
+      FROM_HERE,
+      base::BindOnce([](RunLoop* run_loop) { run_loop->QuitWhenIdle(); },
+                     Unretained(&run_loop_)));
+
+  ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(&ShouldRunTask, Unretained(&counter_)));
+
+  run_loop_.Run();
+
+  // Regardless of the outcome of the race this thread shouldn't have been idle
+  // until the counter was ticked twice.
+  EXPECT_EQ(2, counter_);
+}
+
+// Verify that QuitWhenIdleClosure can be executed from another sequence.
+TEST_P(RunLoopTest, QuitWhenIdleFromOtherSequenceWithClosure) {
+  Thread other_thread("test");
+  other_thread.Start();
+  scoped_refptr<SequencedTaskRunner> other_sequence =
+      other_thread.task_runner();
+
+  ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(&ShouldRunTask, Unretained(&counter_)));
+
+  other_sequence->PostTask(FROM_HERE, run_loop_.QuitWhenIdleClosure());
+
+  ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(&ShouldRunTask, Unretained(&counter_)));
+
+  run_loop_.Run();
+
+  // Regardless of the outcome of the race this thread shouldn't have been idle
+  // until the counter was ticked twice.
+  EXPECT_EQ(2, counter_);
+}
+
+TEST_P(RunLoopTest, IsRunningOnCurrentThread) {
+  EXPECT_FALSE(RunLoop::IsRunningOnCurrentThread());
+  ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE,
+      BindOnce([]() { EXPECT_TRUE(RunLoop::IsRunningOnCurrentThread()); }));
+  ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, run_loop_.QuitClosure());
+  run_loop_.Run();
+}
+
+TEST_P(RunLoopTest, IsNestedOnCurrentThread) {
+  EXPECT_FALSE(RunLoop::IsNestedOnCurrentThread());
+
+  ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, BindOnce([]() {
+        EXPECT_FALSE(RunLoop::IsNestedOnCurrentThread());
+
+        RunLoop nested_run_loop(RunLoop::Type::kNestableTasksAllowed);
+
+        ThreadTaskRunnerHandle::Get()->PostTask(
+            FROM_HERE, BindOnce([]() {
+              EXPECT_TRUE(RunLoop::IsNestedOnCurrentThread());
+            }));
+        ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+                                                nested_run_loop.QuitClosure());
+
+        EXPECT_FALSE(RunLoop::IsNestedOnCurrentThread());
+        nested_run_loop.Run();
+        EXPECT_FALSE(RunLoop::IsNestedOnCurrentThread());
+      }));
+
+  ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, run_loop_.QuitClosure());
+  run_loop_.Run();
+}
+
+namespace {
+
+class MockNestingObserver : public RunLoop::NestingObserver {
+ public:
+  MockNestingObserver() = default;
+
+  // RunLoop::NestingObserver:
+  MOCK_METHOD0(OnBeginNestedRunLoop, void());
+  MOCK_METHOD0(OnExitNestedRunLoop, void());
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockNestingObserver);
+};
+
+class MockTask {
+ public:
+  MockTask() = default;
+  MOCK_METHOD0(Task, void());
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockTask);
+};
+
+}  // namespace
+
+TEST_P(RunLoopTest, NestingObservers) {
+  testing::StrictMock<MockNestingObserver> nesting_observer;
+  testing::StrictMock<MockTask> mock_task_a;
+  testing::StrictMock<MockTask> mock_task_b;
+
+  RunLoop::AddNestingObserverOnCurrentThread(&nesting_observer);
+
+  const RepeatingClosure run_nested_loop = Bind([]() {
+    RunLoop nested_run_loop(RunLoop::Type::kNestableTasksAllowed);
+    ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+                                            nested_run_loop.QuitClosure());
+    nested_run_loop.Run();
+  });
+
+  // Generate a stack of nested RunLoops. OnBeginNestedRunLoop() is expected
+  // when beginning each nesting depth and OnExitNestedRunLoop() is expected
+  // when exiting each nesting depth. Each one of these tasks is ahead of the
+  // QuitClosures as those are only posted at the end of the queue when
+  // |run_nested_loop| is executed.
+  ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, run_nested_loop);
+  ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE,
+      base::BindOnce(&MockTask::Task, base::Unretained(&mock_task_a)));
+  ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, run_nested_loop);
+  ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE,
+      base::BindOnce(&MockTask::Task, base::Unretained(&mock_task_b)));
+
+  {
+    testing::InSequence in_sequence;
+    EXPECT_CALL(nesting_observer, OnBeginNestedRunLoop());
+    EXPECT_CALL(mock_task_a, Task());
+    EXPECT_CALL(nesting_observer, OnBeginNestedRunLoop());
+    EXPECT_CALL(mock_task_b, Task());
+    EXPECT_CALL(nesting_observer, OnExitNestedRunLoop()).Times(2);
+  }
+  run_loop_.RunUntilIdle();
+
+  RunLoop::RemoveNestingObserverOnCurrentThread(&nesting_observer);
+}
+
+TEST_P(RunLoopTest, DisallowRunningForTesting) {
+  RunLoop::ScopedDisallowRunningForTesting disallow_running;
+  EXPECT_DCHECK_DEATH({ run_loop_.RunUntilIdle(); });
+}
+
+TEST_P(RunLoopTest, ExpiredDisallowRunningForTesting) {
+  { RunLoop::ScopedDisallowRunningForTesting disallow_running; }
+  // Running should be fine after |disallow_running| goes out of scope.
+  run_loop_.RunUntilIdle();
+}
+
+INSTANTIATE_TEST_CASE_P(Real,
+                        RunLoopTest,
+                        testing::Values(RunLoopTestType::kRealEnvironment));
+INSTANTIATE_TEST_CASE_P(Mock,
+                        RunLoopTest,
+                        testing::Values(RunLoopTestType::kTestDelegate));
+
+TEST(RunLoopDeathTest, MustRegisterBeforeInstantiating) {
+  TestBoundDelegate unbound_test_delegate_;
+  // RunLoop::RunLoop() should CHECK fetching the ThreadTaskRunnerHandle.
+  EXPECT_DEATH_IF_SUPPORTED({ RunLoop(); }, "");
+}
+
+TEST(RunLoopDelegateTest, NestableTasksDontRunInDefaultNestedLoops) {
+  TestBoundDelegate test_delegate;
+  test_delegate.BindToCurrentThread();
+
+  base::Thread other_thread("test");
+  other_thread.Start();
+
+  RunLoop main_loop;
+  // A nested run loop which isn't kNestableTasksAllowed.
+  RunLoop nested_run_loop(RunLoop::Type::kDefault);
+
+  bool nested_run_loop_ended = false;
+
+  // The first task on the main loop will result in a nested run loop. Since
+  // it's not kNestableTasksAllowed, no further task should be processed until
+  // it's quit.
+  ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE,
+      BindOnce([](RunLoop* nested_run_loop) { nested_run_loop->Run(); },
+               Unretained(&nested_run_loop)));
+
+  // Post a task that will fail if it runs inside the nested run loop.
+  ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, BindOnce(
+                     [](const bool& nested_run_loop_ended,
+                        OnceClosure continuation_callback) {
+                       EXPECT_TRUE(nested_run_loop_ended);
+                       EXPECT_FALSE(RunLoop::IsNestedOnCurrentThread());
+                       std::move(continuation_callback).Run();
+                     },
+                     ConstRef(nested_run_loop_ended), main_loop.QuitClosure()));
+
+  // Post a task flipping the boolean bit for extra verification right before
+  // quitting |nested_run_loop|.
+  other_thread.task_runner()->PostDelayedTask(
+      FROM_HERE,
+      BindOnce(
+          [](bool* nested_run_loop_ended) {
+            EXPECT_FALSE(*nested_run_loop_ended);
+            *nested_run_loop_ended = true;
+          },
+          Unretained(&nested_run_loop_ended)),
+      TestTimeouts::tiny_timeout());
+  // Post an async delayed task to exit the run loop when idle. This confirms
+  // that (1) the test task only ran in the main loop after the nested loop
+  // exited and (2) the nested run loop actually considers itself idle while
+  // spinning. Note: The quit closure needs to be injected directly on the
+  // delegate as invoking QuitWhenIdle() off-thread results in a thread bounce
+  // which will not processed because of the very logic under test (nestable
+  // tasks don't run in |nested_run_loop|).
+  other_thread.task_runner()->PostDelayedTask(
+      FROM_HERE,
+      BindOnce(
+          [](TestBoundDelegate* test_delegate, OnceClosure injected_closure) {
+            test_delegate->InjectClosureOnDelegate(std::move(injected_closure));
+          },
+          Unretained(&test_delegate), nested_run_loop.QuitWhenIdleClosure()),
+      TestTimeouts::tiny_timeout());
+
+  main_loop.Run();
+}
+
+}  // namespace base
diff --git a/base/safe_numerics_unittest.cc b/base/safe_numerics_unittest.cc
new file mode 100644
index 0000000..44675cf
--- /dev/null
+++ b/base/safe_numerics_unittest.cc
@@ -0,0 +1,1640 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <limits>
+#include <type_traits>
+
+#include "base/compiler_specific.h"
+
+// WARNING: This block must come before the base/numerics headers are included.
+// These tests deliberately cause arithmetic boundary errors. If the compiler is
+// aggressive enough, it can const detect these errors, so we disable warnings.
+#if defined(OS_WIN)
+#pragma warning(disable : 4756)  // Arithmetic overflow.
+#pragma warning(disable : 4293)  // Invalid shift.
+#endif
+
+// This may not need to come before the base/numerics headers, but let's keep
+// it close to the MSVC equivalent.
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Winteger-overflow"
+#endif
+
+#include "base/logging.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/numerics/safe_math.h"
+#include "base/test/gtest_util.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if defined(COMPILER_MSVC) && defined(ARCH_CPU_32_BITS)
+#include <mmintrin.h>
+#endif
+
+namespace base {
+namespace internal {
+
+using std::numeric_limits;
+
+// This is a helper function for finding the maximum value in Src that can be
+// wholy represented as the destination floating-point type.
+template <typename Dst, typename Src>
+Dst GetMaxConvertibleToFloat() {
+  using DstLimits = numeric_limits<Dst>;
+  using SrcLimits = numeric_limits<Src>;
+  static_assert(SrcLimits::is_specialized, "Source must be numeric.");
+  static_assert(DstLimits::is_specialized, "Destination must be numeric.");
+  CHECK(DstLimits::is_iec559);
+
+  if (SrcLimits::digits <= DstLimits::digits &&
+      MaxExponent<Src>::value <= MaxExponent<Dst>::value)
+    return SrcLimits::max();
+  Src max = SrcLimits::max() / 2 + (SrcLimits::is_integer ? 1 : 0);
+  while (max != static_cast<Src>(static_cast<Dst>(max))) {
+    max /= 2;
+  }
+  return static_cast<Dst>(max);
+}
+
+// Test corner case promotions used
+static_assert(IsIntegerArithmeticSafe<int32_t, int8_t, int8_t>::value, "");
+static_assert(IsIntegerArithmeticSafe<int32_t, int16_t, int8_t>::value, "");
+static_assert(IsIntegerArithmeticSafe<int32_t, int8_t, int16_t>::value, "");
+static_assert(!IsIntegerArithmeticSafe<int32_t, int32_t, int8_t>::value, "");
+static_assert(BigEnoughPromotion<int16_t, int8_t>::is_contained, "");
+static_assert(BigEnoughPromotion<int32_t, uint32_t>::is_contained, "");
+static_assert(BigEnoughPromotion<intmax_t, int8_t>::is_contained, "");
+static_assert(!BigEnoughPromotion<uintmax_t, int8_t>::is_contained, "");
+static_assert(
+    std::is_same<BigEnoughPromotion<int16_t, int8_t>::type, int16_t>::value,
+    "");
+static_assert(
+    std::is_same<BigEnoughPromotion<int32_t, uint32_t>::type, int64_t>::value,
+    "");
+static_assert(
+    std::is_same<BigEnoughPromotion<intmax_t, int8_t>::type, intmax_t>::value,
+    "");
+static_assert(
+    std::is_same<BigEnoughPromotion<uintmax_t, int8_t>::type, uintmax_t>::value,
+    "");
+static_assert(BigEnoughPromotion<int16_t, int8_t>::is_contained, "");
+static_assert(BigEnoughPromotion<int32_t, uint32_t>::is_contained, "");
+static_assert(BigEnoughPromotion<intmax_t, int8_t>::is_contained, "");
+static_assert(!BigEnoughPromotion<uintmax_t, int8_t>::is_contained, "");
+static_assert(
+    std::is_same<FastIntegerArithmeticPromotion<int16_t, int8_t>::type,
+                 int32_t>::value,
+    "");
+static_assert(
+    std::is_same<FastIntegerArithmeticPromotion<int32_t, uint32_t>::type,
+                 int64_t>::value,
+    "");
+static_assert(
+    std::is_same<FastIntegerArithmeticPromotion<intmax_t, int8_t>::type,
+                 intmax_t>::value,
+    "");
+static_assert(
+    std::is_same<FastIntegerArithmeticPromotion<uintmax_t, int8_t>::type,
+                 uintmax_t>::value,
+    "");
+static_assert(FastIntegerArithmeticPromotion<int16_t, int8_t>::is_contained,
+              "");
+static_assert(FastIntegerArithmeticPromotion<int32_t, uint32_t>::is_contained,
+              "");
+static_assert(!FastIntegerArithmeticPromotion<intmax_t, int8_t>::is_contained,
+              "");
+static_assert(!FastIntegerArithmeticPromotion<uintmax_t, int8_t>::is_contained,
+              "");
+
+template <typename U>
+U GetNumericValueForTest(const CheckedNumeric<U>& src) {
+  return src.state_.value();
+}
+
+template <typename U>
+U GetNumericValueForTest(const ClampedNumeric<U>& src) {
+  return static_cast<U>(src);
+}
+
+template <typename U>
+U GetNumericValueForTest(const U& src) {
+  return src;
+}
+
+// Logs the ValueOrDie() failure instead of crashing.
+struct LogOnFailure {
+  template <typename T>
+  static T HandleFailure() {
+    LOG(WARNING) << "ValueOrDie() failed unexpectedly.";
+    return T();
+  }
+};
+
+template <typename T>
+constexpr T GetValue(const T& src) {
+  return src;
+}
+
+template <typename T, typename U>
+constexpr T GetValueAsDest(const U& src) {
+  return static_cast<T>(src);
+}
+
+template <typename T>
+constexpr T GetValue(const CheckedNumeric<T>& src) {
+  return src.template ValueOrDie<T, LogOnFailure>();
+}
+
+template <typename T, typename U>
+constexpr T GetValueAsDest(const CheckedNumeric<U>& src) {
+  return src.template ValueOrDie<T, LogOnFailure>();
+}
+
+template <typename T>
+constexpr T GetValue(const ClampedNumeric<T>& src) {
+  return static_cast<T>(src);
+}
+
+template <typename T, typename U>
+constexpr T GetValueAsDest(const ClampedNumeric<U>& src) {
+  return static_cast<T>(src);
+}
+
+// Helper macros to wrap displaying the conversion types and line numbers.
+#define TEST_EXPECTED_VALIDITY(expected, actual)                           \
+  EXPECT_EQ(expected, (actual).template Cast<Dst>().IsValid())             \
+      << "Result test: Value " << GetNumericValueForTest(actual) << " as " \
+      << dst << " on line " << line
+
+#define TEST_EXPECTED_SUCCESS(actual) TEST_EXPECTED_VALIDITY(true, actual)
+#define TEST_EXPECTED_FAILURE(actual) TEST_EXPECTED_VALIDITY(false, actual)
+
+// We have to handle promotions, so infer the underlying type below from actual.
+#define TEST_EXPECTED_VALUE(expected, actual)                               \
+  EXPECT_EQ(GetValue(expected), GetValueAsDest<decltype(expected)>(actual)) \
+      << "Result test: Value " << GetNumericValueForTest(actual) << " as "  \
+      << dst << " on line " << line
+
+// Test the simple pointer arithmetic overrides.
+template <typename Dst>
+void TestStrictPointerMath() {
+  Dst dummy_value = 0;
+  Dst* dummy_ptr = &dummy_value;
+  static const Dst kDummyOffset = 2;  // Don't want to go too far.
+  EXPECT_EQ(dummy_ptr + kDummyOffset,
+            dummy_ptr + StrictNumeric<Dst>(kDummyOffset));
+  EXPECT_EQ(dummy_ptr - kDummyOffset,
+            dummy_ptr - StrictNumeric<Dst>(kDummyOffset));
+  EXPECT_NE(dummy_ptr, dummy_ptr + StrictNumeric<Dst>(kDummyOffset));
+  EXPECT_NE(dummy_ptr, dummy_ptr - StrictNumeric<Dst>(kDummyOffset));
+  EXPECT_DEATH_IF_SUPPORTED(
+      dummy_ptr + StrictNumeric<size_t>(std::numeric_limits<size_t>::max()),
+      "");
+}
+
+// Signed integer arithmetic.
+template <typename Dst>
+static void TestSpecializedArithmetic(
+    const char* dst,
+    int line,
+    typename std::enable_if<numeric_limits<Dst>::is_integer &&
+                                numeric_limits<Dst>::is_signed,
+                            int>::type = 0) {
+  using DstLimits = SaturationDefaultLimits<Dst>;
+  TEST_EXPECTED_FAILURE(-CheckedNumeric<Dst>(DstLimits::lowest()));
+  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::lowest()).Abs());
+  TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(-1).Abs());
+  TEST_EXPECTED_VALUE(DstLimits::max(),
+                      MakeCheckedNum(-DstLimits::max()).Abs());
+
+  TEST_EXPECTED_VALUE(DstLimits::Overflow(),
+                      -ClampedNumeric<Dst>(DstLimits::lowest()));
+  TEST_EXPECTED_VALUE(DstLimits::Overflow(),
+                      ClampedNumeric<Dst>(DstLimits::lowest()).Abs());
+  TEST_EXPECTED_VALUE(1, ClampedNumeric<Dst>(-1).Abs());
+  TEST_EXPECTED_VALUE(DstLimits::max(),
+                      MakeClampedNum(-DstLimits::max()).Abs());
+
+  TEST_EXPECTED_SUCCESS(CheckedNumeric<Dst>(DstLimits::max()) + -1);
+  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::lowest()) + -1);
+  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::lowest()) +
+                        DstLimits::lowest());
+
+  TEST_EXPECTED_VALUE(DstLimits::max() - 1,
+                      ClampedNumeric<Dst>(DstLimits::max()) + -1);
+  TEST_EXPECTED_VALUE(DstLimits::Underflow(),
+                      ClampedNumeric<Dst>(DstLimits::lowest()) + -1);
+  TEST_EXPECTED_VALUE(
+      DstLimits::Underflow(),
+      ClampedNumeric<Dst>(DstLimits::lowest()) + DstLimits::lowest());
+
+  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::lowest()) - 1);
+  TEST_EXPECTED_SUCCESS(CheckedNumeric<Dst>(DstLimits::lowest()) - -1);
+  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::max()) -
+                        DstLimits::lowest());
+  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::lowest()) -
+                        DstLimits::max());
+
+  TEST_EXPECTED_VALUE(DstLimits::Underflow(),
+                      ClampedNumeric<Dst>(DstLimits::lowest()) - 1);
+  TEST_EXPECTED_VALUE(DstLimits::lowest() + 1,
+                      ClampedNumeric<Dst>(DstLimits::lowest()) - -1);
+  TEST_EXPECTED_VALUE(
+      DstLimits::Overflow(),
+      ClampedNumeric<Dst>(DstLimits::max()) - DstLimits::lowest());
+  TEST_EXPECTED_VALUE(
+      DstLimits::Underflow(),
+      ClampedNumeric<Dst>(DstLimits::lowest()) - DstLimits::max());
+
+  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::lowest()) * 2);
+  TEST_EXPECTED_VALUE(DstLimits::Underflow(),
+                      ClampedNumeric<Dst>(DstLimits::lowest()) * 2);
+
+  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::lowest()) / -1);
+  TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(-1) / 2);
+  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::lowest()) * -1);
+  TEST_EXPECTED_VALUE(DstLimits::max(),
+                      CheckedNumeric<Dst>(DstLimits::lowest() + 1) * Dst(-1));
+  TEST_EXPECTED_VALUE(DstLimits::max(),
+                      CheckedNumeric<Dst>(-1) * Dst(DstLimits::lowest() + 1));
+  TEST_EXPECTED_VALUE(DstLimits::lowest(),
+                      CheckedNumeric<Dst>(DstLimits::lowest()) * Dst(1));
+  TEST_EXPECTED_VALUE(DstLimits::lowest(),
+                      CheckedNumeric<Dst>(1) * Dst(DstLimits::lowest()));
+  TEST_EXPECTED_VALUE(
+      typename std::make_unsigned<Dst>::type(0) - DstLimits::lowest(),
+      MakeCheckedNum(DstLimits::lowest()).UnsignedAbs());
+  TEST_EXPECTED_VALUE(DstLimits::max(),
+                      MakeCheckedNum(DstLimits::max()).UnsignedAbs());
+  TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(0).UnsignedAbs());
+  TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(1).UnsignedAbs());
+  TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(-1).UnsignedAbs());
+
+  TEST_EXPECTED_VALUE(DstLimits::Overflow(),
+                      ClampedNumeric<Dst>(DstLimits::lowest()) / -1);
+  TEST_EXPECTED_VALUE(0, ClampedNumeric<Dst>(-1) / 2);
+  TEST_EXPECTED_VALUE(DstLimits::Overflow(),
+                      ClampedNumeric<Dst>(DstLimits::lowest()) * -1);
+  TEST_EXPECTED_VALUE(DstLimits::max(),
+                      ClampedNumeric<Dst>(DstLimits::lowest() + 1) * Dst(-1));
+  TEST_EXPECTED_VALUE(DstLimits::max(),
+                      ClampedNumeric<Dst>(-1) * Dst(DstLimits::lowest() + 1));
+  TEST_EXPECTED_VALUE(DstLimits::lowest(),
+                      ClampedNumeric<Dst>(DstLimits::lowest()) * Dst(1));
+  TEST_EXPECTED_VALUE(DstLimits::lowest(),
+                      ClampedNumeric<Dst>(1) * Dst(DstLimits::lowest()));
+  TEST_EXPECTED_VALUE(
+      typename std::make_unsigned<Dst>::type(0) - DstLimits::lowest(),
+      MakeClampedNum(DstLimits::lowest()).UnsignedAbs());
+  TEST_EXPECTED_VALUE(DstLimits::max(),
+                      MakeClampedNum(DstLimits::max()).UnsignedAbs());
+  TEST_EXPECTED_VALUE(0, ClampedNumeric<Dst>(0).UnsignedAbs());
+  TEST_EXPECTED_VALUE(1, ClampedNumeric<Dst>(1).UnsignedAbs());
+  TEST_EXPECTED_VALUE(1, ClampedNumeric<Dst>(-1).UnsignedAbs());
+
+  // Modulus is legal only for integers.
+  TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>() % 1);
+  TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) % 1);
+  TEST_EXPECTED_VALUE(-1, CheckedNumeric<Dst>(-1) % 2);
+  TEST_EXPECTED_VALUE(-1, CheckedNumeric<Dst>(-1) % -2);
+  TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(DstLimits::lowest()) % 2);
+  TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(DstLimits::max()) % 2);
+  // Test all the different modulus combinations.
+  TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) % CheckedNumeric<Dst>(1));
+  TEST_EXPECTED_VALUE(0, 1 % CheckedNumeric<Dst>(1));
+  TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) % 1);
+  CheckedNumeric<Dst> checked_dst = 1;
+  TEST_EXPECTED_VALUE(0, checked_dst %= 1);
+  // Test that div by 0 is avoided but returns invalid result.
+  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(1) % 0);
+  // Test bit shifts.
+  volatile Dst negative_one = -1;
+  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(1) << negative_one);
+  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(1)
+                        << (IntegerBitsPlusSign<Dst>::value - 1));
+  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(0)
+                        << IntegerBitsPlusSign<Dst>::value);
+  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::max()) << 1);
+  TEST_EXPECTED_VALUE(
+      static_cast<Dst>(1) << (IntegerBitsPlusSign<Dst>::value - 2),
+      CheckedNumeric<Dst>(1) << (IntegerBitsPlusSign<Dst>::value - 2));
+  TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(0)
+                             << (IntegerBitsPlusSign<Dst>::value - 1));
+  TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(1) << 0);
+  TEST_EXPECTED_VALUE(2, CheckedNumeric<Dst>(1) << 1);
+  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(1) >>
+                        IntegerBitsPlusSign<Dst>::value);
+  TEST_EXPECTED_VALUE(
+      0, CheckedNumeric<Dst>(1) >> (IntegerBitsPlusSign<Dst>::value - 1));
+  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(1) >> negative_one);
+
+  // Modulus is legal only for integers.
+  TEST_EXPECTED_VALUE(0, ClampedNumeric<Dst>() % 1);
+  TEST_EXPECTED_VALUE(0, ClampedNumeric<Dst>(1) % 1);
+  TEST_EXPECTED_VALUE(-1, ClampedNumeric<Dst>(-1) % 2);
+  TEST_EXPECTED_VALUE(-1, ClampedNumeric<Dst>(-1) % -2);
+  TEST_EXPECTED_VALUE(0, ClampedNumeric<Dst>(DstLimits::lowest()) % 2);
+  TEST_EXPECTED_VALUE(1, ClampedNumeric<Dst>(DstLimits::max()) % 2);
+  // Test all the different modulus combinations.
+  TEST_EXPECTED_VALUE(0, ClampedNumeric<Dst>(1) % ClampedNumeric<Dst>(1));
+  TEST_EXPECTED_VALUE(0, 1 % ClampedNumeric<Dst>(1));
+  TEST_EXPECTED_VALUE(0, ClampedNumeric<Dst>(1) % 1);
+  ClampedNumeric<Dst> clamped_dst = 1;
+  TEST_EXPECTED_VALUE(0, clamped_dst %= 1);
+  TEST_EXPECTED_VALUE(Dst(1), ClampedNumeric<Dst>(1) % 0);
+  // Test bit shifts.
+  TEST_EXPECTED_VALUE(DstLimits::Overflow(),
+                      ClampedNumeric<Dst>(1)
+                          << (IntegerBitsPlusSign<Dst>::value - 1U));
+  TEST_EXPECTED_VALUE(Dst(0), ClampedNumeric<Dst>(0)
+                                  << (IntegerBitsPlusSign<Dst>::value + 0U));
+  TEST_EXPECTED_VALUE(DstLimits::Overflow(),
+                      ClampedNumeric<Dst>(DstLimits::max()) << 1U);
+  TEST_EXPECTED_VALUE(
+      static_cast<Dst>(1) << (IntegerBitsPlusSign<Dst>::value - 2U),
+      ClampedNumeric<Dst>(1) << (IntegerBitsPlusSign<Dst>::value - 2U));
+  TEST_EXPECTED_VALUE(0, ClampedNumeric<Dst>(0)
+                             << (IntegerBitsPlusSign<Dst>::value - 1U));
+  TEST_EXPECTED_VALUE(1, ClampedNumeric<Dst>(1) << 0U);
+  TEST_EXPECTED_VALUE(2, ClampedNumeric<Dst>(1) << 1U);
+  TEST_EXPECTED_VALUE(
+      0, ClampedNumeric<Dst>(1) >> (IntegerBitsPlusSign<Dst>::value + 0U));
+  TEST_EXPECTED_VALUE(
+      0, ClampedNumeric<Dst>(1) >> (IntegerBitsPlusSign<Dst>::value - 1U));
+  TEST_EXPECTED_VALUE(
+      -1, ClampedNumeric<Dst>(-1) >> (IntegerBitsPlusSign<Dst>::value - 1U));
+  TEST_EXPECTED_VALUE(-1, ClampedNumeric<Dst>(DstLimits::lowest()) >>
+                              (IntegerBitsPlusSign<Dst>::value - 0U));
+
+  TestStrictPointerMath<Dst>();
+}
+
+// Unsigned integer arithmetic.
+template <typename Dst>
+static void TestSpecializedArithmetic(
+    const char* dst,
+    int line,
+    typename std::enable_if<numeric_limits<Dst>::is_integer &&
+                                !numeric_limits<Dst>::is_signed,
+                            int>::type = 0) {
+  using DstLimits = SaturationDefaultLimits<Dst>;
+  TEST_EXPECTED_SUCCESS(-CheckedNumeric<Dst>(DstLimits::lowest()));
+  TEST_EXPECTED_SUCCESS(CheckedNumeric<Dst>(DstLimits::lowest()).Abs());
+  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::lowest()) + -1);
+  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::lowest()) - 1);
+  TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(DstLimits::lowest()) * 2);
+  TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) / 2);
+  TEST_EXPECTED_SUCCESS(CheckedNumeric<Dst>(DstLimits::lowest()).UnsignedAbs());
+  TEST_EXPECTED_SUCCESS(
+      CheckedNumeric<typename std::make_signed<Dst>::type>(
+          std::numeric_limits<typename std::make_signed<Dst>::type>::lowest())
+          .UnsignedAbs());
+  TEST_EXPECTED_VALUE(DstLimits::lowest(),
+                      MakeCheckedNum(DstLimits::lowest()).UnsignedAbs());
+  TEST_EXPECTED_VALUE(DstLimits::max(),
+                      MakeCheckedNum(DstLimits::max()).UnsignedAbs());
+  TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(0).UnsignedAbs());
+  TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(1).UnsignedAbs());
+
+  TEST_EXPECTED_VALUE(0, -ClampedNumeric<Dst>(DstLimits::lowest()));
+  TEST_EXPECTED_VALUE(0, ClampedNumeric<Dst>(DstLimits::lowest()).Abs());
+  TEST_EXPECTED_VALUE(DstLimits::Underflow(),
+                      ClampedNumeric<Dst>(DstLimits::lowest()) + -1);
+  TEST_EXPECTED_VALUE(DstLimits::Underflow(),
+                      ClampedNumeric<Dst>(DstLimits::lowest()) - 1);
+  TEST_EXPECTED_VALUE(0, ClampedNumeric<Dst>(DstLimits::lowest()) * 2);
+  TEST_EXPECTED_VALUE(0, ClampedNumeric<Dst>(1) / 2);
+  TEST_EXPECTED_VALUE(0,
+                      ClampedNumeric<Dst>(DstLimits::lowest()).UnsignedAbs());
+  TEST_EXPECTED_VALUE(
+      as_unsigned(
+          std::numeric_limits<typename std::make_signed<Dst>::type>::lowest()),
+      ClampedNumeric<typename std::make_signed<Dst>::type>(
+          std::numeric_limits<typename std::make_signed<Dst>::type>::lowest())
+          .UnsignedAbs());
+  TEST_EXPECTED_VALUE(DstLimits::lowest(),
+                      MakeClampedNum(DstLimits::lowest()).UnsignedAbs());
+  TEST_EXPECTED_VALUE(DstLimits::max(),
+                      MakeClampedNum(DstLimits::max()).UnsignedAbs());
+  TEST_EXPECTED_VALUE(0, ClampedNumeric<Dst>(0).UnsignedAbs());
+  TEST_EXPECTED_VALUE(1, ClampedNumeric<Dst>(1).UnsignedAbs());
+
+  // Modulus is legal only for integers.
+  TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>() % 1);
+  TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) % 1);
+  TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(1) % 2);
+  TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(DstLimits::lowest()) % 2);
+  TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(DstLimits::max()) % 2);
+  // Test all the different modulus combinations.
+  TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) % CheckedNumeric<Dst>(1));
+  TEST_EXPECTED_VALUE(0, 1 % CheckedNumeric<Dst>(1));
+  TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) % 1);
+  CheckedNumeric<Dst> checked_dst = 1;
+  TEST_EXPECTED_VALUE(0, checked_dst %= 1);
+  // Test that div by 0 is avoided but returns invalid result.
+  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(1) % 0);
+  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(1)
+                        << IntegerBitsPlusSign<Dst>::value);
+  // Test bit shifts.
+  volatile int negative_one = -1;
+  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(1) << negative_one);
+  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(1)
+                        << IntegerBitsPlusSign<Dst>::value);
+  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(0)
+                        << IntegerBitsPlusSign<Dst>::value);
+  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::max()) << 1);
+  TEST_EXPECTED_VALUE(
+      static_cast<Dst>(1) << (IntegerBitsPlusSign<Dst>::value - 1),
+      CheckedNumeric<Dst>(1) << (IntegerBitsPlusSign<Dst>::value - 1));
+  TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(1) << 0);
+  TEST_EXPECTED_VALUE(2, CheckedNumeric<Dst>(1) << 1);
+  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(1) >>
+                        IntegerBitsPlusSign<Dst>::value);
+  TEST_EXPECTED_VALUE(
+      0, CheckedNumeric<Dst>(1) >> (IntegerBitsPlusSign<Dst>::value - 1));
+  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(1) >> negative_one);
+  TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(1) & 1);
+  TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) & 0);
+  TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(0) & 1);
+  TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) & 0);
+  TEST_EXPECTED_VALUE(std::numeric_limits<Dst>::max(),
+                      MakeCheckedNum(DstLimits::max()) & -1);
+  TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(1) | 1);
+  TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(1) | 0);
+  TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(0) | 1);
+  TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(0) | 0);
+  TEST_EXPECTED_VALUE(std::numeric_limits<Dst>::max(),
+                      CheckedNumeric<Dst>(0) | static_cast<Dst>(-1));
+  TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) ^ 1);
+  TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(1) ^ 0);
+  TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(0) ^ 1);
+  TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(0) ^ 0);
+  TEST_EXPECTED_VALUE(std::numeric_limits<Dst>::max(),
+                      CheckedNumeric<Dst>(0) ^ static_cast<Dst>(-1));
+  TEST_EXPECTED_VALUE(DstLimits::max(), ~CheckedNumeric<Dst>(0));
+
+  // Modulus is legal only for integers.
+  TEST_EXPECTED_VALUE(0, ClampedNumeric<Dst>() % 1);
+  TEST_EXPECTED_VALUE(0, ClampedNumeric<Dst>(1) % 1);
+  TEST_EXPECTED_VALUE(1, ClampedNumeric<Dst>(1) % 2);
+  TEST_EXPECTED_VALUE(0, ClampedNumeric<Dst>(DstLimits::lowest()) % 2);
+  TEST_EXPECTED_VALUE(1, ClampedNumeric<Dst>(DstLimits::max()) % 2);
+  // Test all the different modulus combinations.
+  TEST_EXPECTED_VALUE(0, ClampedNumeric<Dst>(1) % ClampedNumeric<Dst>(1));
+  TEST_EXPECTED_VALUE(0, 1 % ClampedNumeric<Dst>(1));
+  TEST_EXPECTED_VALUE(0, ClampedNumeric<Dst>(1) % 1);
+  ClampedNumeric<Dst> clamped_dst = 1;
+  TEST_EXPECTED_VALUE(0, clamped_dst %= 1);
+  // Test that div by 0 is avoided but returns invalid result.
+  TEST_EXPECTED_VALUE(Dst(1), ClampedNumeric<Dst>(1) % 0);
+  // Test bit shifts.
+  TEST_EXPECTED_VALUE(DstLimits::Overflow(),
+                      ClampedNumeric<Dst>(1)
+                          << as_unsigned(IntegerBitsPlusSign<Dst>::value));
+  TEST_EXPECTED_VALUE(Dst(0), ClampedNumeric<Dst>(0) << as_unsigned(
+                                  IntegerBitsPlusSign<Dst>::value));
+  TEST_EXPECTED_VALUE(DstLimits::Overflow(),
+                      ClampedNumeric<Dst>(DstLimits::max()) << 1U);
+  TEST_EXPECTED_VALUE(
+      static_cast<Dst>(1) << (IntegerBitsPlusSign<Dst>::value - 1U),
+      ClampedNumeric<Dst>(1) << (IntegerBitsPlusSign<Dst>::value - 1U));
+  TEST_EXPECTED_VALUE(1, ClampedNumeric<Dst>(1) << 0U);
+  TEST_EXPECTED_VALUE(2, ClampedNumeric<Dst>(1) << 1U);
+  TEST_EXPECTED_VALUE(0, ClampedNumeric<Dst>(1) >>
+                             as_unsigned(IntegerBitsPlusSign<Dst>::value));
+  TEST_EXPECTED_VALUE(
+      0, ClampedNumeric<Dst>(1) >> (IntegerBitsPlusSign<Dst>::value - 1U));
+  TEST_EXPECTED_VALUE(1, ClampedNumeric<Dst>(1) & 1);
+  TEST_EXPECTED_VALUE(0, ClampedNumeric<Dst>(1) & 0);
+  TEST_EXPECTED_VALUE(0, ClampedNumeric<Dst>(0) & 1);
+  TEST_EXPECTED_VALUE(0, ClampedNumeric<Dst>(1) & 0);
+  TEST_EXPECTED_VALUE(std::numeric_limits<Dst>::max(),
+                      MakeClampedNum(DstLimits::max()) & -1);
+  TEST_EXPECTED_VALUE(1, ClampedNumeric<Dst>(1) | 1);
+  TEST_EXPECTED_VALUE(1, ClampedNumeric<Dst>(1) | 0);
+  TEST_EXPECTED_VALUE(1, ClampedNumeric<Dst>(0) | 1);
+  TEST_EXPECTED_VALUE(0, ClampedNumeric<Dst>(0) | 0);
+  TEST_EXPECTED_VALUE(std::numeric_limits<Dst>::max(),
+                      ClampedNumeric<Dst>(0) | static_cast<Dst>(-1));
+  TEST_EXPECTED_VALUE(0, ClampedNumeric<Dst>(1) ^ 1);
+  TEST_EXPECTED_VALUE(1, ClampedNumeric<Dst>(1) ^ 0);
+  TEST_EXPECTED_VALUE(1, ClampedNumeric<Dst>(0) ^ 1);
+  TEST_EXPECTED_VALUE(0, ClampedNumeric<Dst>(0) ^ 0);
+  TEST_EXPECTED_VALUE(std::numeric_limits<Dst>::max(),
+                      ClampedNumeric<Dst>(0) ^ static_cast<Dst>(-1));
+  TEST_EXPECTED_VALUE(DstLimits::max(), ~ClampedNumeric<Dst>(0));
+
+  TestStrictPointerMath<Dst>();
+}
+
+// Floating point arithmetic.
+template <typename Dst>
+void TestSpecializedArithmetic(
+    const char* dst,
+    int line,
+    typename std::enable_if<numeric_limits<Dst>::is_iec559, int>::type = 0) {
+  using DstLimits = SaturationDefaultLimits<Dst>;
+  TEST_EXPECTED_SUCCESS(-CheckedNumeric<Dst>(DstLimits::lowest()));
+
+  TEST_EXPECTED_SUCCESS(CheckedNumeric<Dst>(DstLimits::lowest()).Abs());
+  TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(-1).Abs());
+
+  TEST_EXPECTED_SUCCESS(CheckedNumeric<Dst>(DstLimits::lowest()) + -1);
+  TEST_EXPECTED_SUCCESS(CheckedNumeric<Dst>(DstLimits::max()) + 1);
+  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::lowest()) +
+                        DstLimits::lowest());
+
+  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::max()) -
+                        DstLimits::lowest());
+  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::lowest()) -
+                        DstLimits::max());
+
+  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::lowest()) * 2);
+
+  TEST_EXPECTED_VALUE(-0.5, CheckedNumeric<Dst>(-1.0) / 2);
+
+  TEST_EXPECTED_VALUE(DstLimits::max(),
+                      -ClampedNumeric<Dst>(DstLimits::lowest()));
+
+  TEST_EXPECTED_VALUE(DstLimits::max(),
+                      ClampedNumeric<Dst>(DstLimits::lowest()).Abs());
+  TEST_EXPECTED_VALUE(1, ClampedNumeric<Dst>(-1).Abs());
+
+  TEST_EXPECTED_VALUE(DstLimits::lowest() - 1,
+                      ClampedNumeric<Dst>(DstLimits::lowest()) + -1);
+  TEST_EXPECTED_VALUE(DstLimits::max() + 1,
+                      ClampedNumeric<Dst>(DstLimits::max()) + 1);
+  TEST_EXPECTED_VALUE(
+      DstLimits::Underflow(),
+      ClampedNumeric<Dst>(DstLimits::lowest()) + DstLimits::lowest());
+
+  TEST_EXPECTED_VALUE(
+      DstLimits::Overflow(),
+      ClampedNumeric<Dst>(DstLimits::max()) - DstLimits::lowest());
+  TEST_EXPECTED_VALUE(
+      DstLimits::Underflow(),
+      ClampedNumeric<Dst>(DstLimits::lowest()) - DstLimits::max());
+
+  TEST_EXPECTED_VALUE(DstLimits::Underflow(),
+                      ClampedNumeric<Dst>(DstLimits::lowest()) * 2);
+
+  TEST_EXPECTED_VALUE(-0.5, ClampedNumeric<Dst>(-1.0) / 2);
+}
+
+// Generic arithmetic tests.
+template <typename Dst>
+static void TestArithmetic(const char* dst, int line) {
+  using DstLimits = SaturationDefaultLimits<Dst>;
+
+  EXPECT_EQ(true, CheckedNumeric<Dst>().IsValid());
+  EXPECT_EQ(false, CheckedNumeric<Dst>(CheckedNumeric<Dst>(DstLimits::max()) *
+                                       DstLimits::max())
+                       .IsValid());
+  EXPECT_EQ(static_cast<Dst>(0), CheckedNumeric<Dst>().ValueOrDie());
+  EXPECT_EQ(static_cast<Dst>(0), CheckedNumeric<Dst>().ValueOrDefault(1));
+  EXPECT_EQ(static_cast<Dst>(1),
+            CheckedNumeric<Dst>(CheckedNumeric<Dst>(DstLimits::max()) *
+                                DstLimits::max())
+                .ValueOrDefault(1));
+
+  // Test the operator combinations.
+  TEST_EXPECTED_VALUE(2, CheckedNumeric<Dst>(1) + CheckedNumeric<Dst>(1));
+  TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) - CheckedNumeric<Dst>(1));
+  TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(1) * CheckedNumeric<Dst>(1));
+  TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(1) / CheckedNumeric<Dst>(1));
+  TEST_EXPECTED_VALUE(2, 1 + CheckedNumeric<Dst>(1));
+  TEST_EXPECTED_VALUE(0, 1 - CheckedNumeric<Dst>(1));
+  TEST_EXPECTED_VALUE(1, 1 * CheckedNumeric<Dst>(1));
+  TEST_EXPECTED_VALUE(1, 1 / CheckedNumeric<Dst>(1));
+  TEST_EXPECTED_VALUE(2, CheckedNumeric<Dst>(1) + 1);
+  TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) - 1);
+  TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(1) * 1);
+  TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(1) / 1);
+  CheckedNumeric<Dst> checked_dst = 1;
+  TEST_EXPECTED_VALUE(2, checked_dst += 1);
+  checked_dst = 1;
+  TEST_EXPECTED_VALUE(0, checked_dst -= 1);
+  checked_dst = 1;
+  TEST_EXPECTED_VALUE(1, checked_dst *= 1);
+  checked_dst = 1;
+  TEST_EXPECTED_VALUE(1, checked_dst /= 1);
+
+  TEST_EXPECTED_VALUE(2, ClampedNumeric<Dst>(1) + ClampedNumeric<Dst>(1));
+  TEST_EXPECTED_VALUE(0, ClampedNumeric<Dst>(1) - ClampedNumeric<Dst>(1));
+  TEST_EXPECTED_VALUE(1, ClampedNumeric<Dst>(1) * ClampedNumeric<Dst>(1));
+  TEST_EXPECTED_VALUE(1, ClampedNumeric<Dst>(1) / ClampedNumeric<Dst>(1));
+  TEST_EXPECTED_VALUE(2, 1 + ClampedNumeric<Dst>(1));
+  TEST_EXPECTED_VALUE(0, 1 - ClampedNumeric<Dst>(1));
+  TEST_EXPECTED_VALUE(1, 1 * ClampedNumeric<Dst>(1));
+  TEST_EXPECTED_VALUE(1, 1 / ClampedNumeric<Dst>(1));
+  TEST_EXPECTED_VALUE(2, ClampedNumeric<Dst>(1) + 1);
+  TEST_EXPECTED_VALUE(0, ClampedNumeric<Dst>(1) - 1);
+  TEST_EXPECTED_VALUE(1, ClampedNumeric<Dst>(1) * 1);
+  TEST_EXPECTED_VALUE(1, ClampedNumeric<Dst>(1) / 1);
+  ClampedNumeric<Dst> clamped_dst = 1;
+  TEST_EXPECTED_VALUE(2, clamped_dst += 1);
+  clamped_dst = 1;
+  TEST_EXPECTED_VALUE(0, clamped_dst -= 1);
+  clamped_dst = 1;
+  TEST_EXPECTED_VALUE(1, clamped_dst *= 1);
+  clamped_dst = 1;
+  TEST_EXPECTED_VALUE(1, clamped_dst /= 1);
+
+  // Generic negation.
+  if (DstLimits::is_signed) {
+    TEST_EXPECTED_VALUE(0, -CheckedNumeric<Dst>());
+    TEST_EXPECTED_VALUE(-1, -CheckedNumeric<Dst>(1));
+    TEST_EXPECTED_VALUE(1, -CheckedNumeric<Dst>(-1));
+    TEST_EXPECTED_VALUE(static_cast<Dst>(DstLimits::max() * -1),
+                        -CheckedNumeric<Dst>(DstLimits::max()));
+
+    TEST_EXPECTED_VALUE(0, -ClampedNumeric<Dst>());
+    TEST_EXPECTED_VALUE(-1, -ClampedNumeric<Dst>(1));
+    TEST_EXPECTED_VALUE(1, -ClampedNumeric<Dst>(-1));
+    TEST_EXPECTED_VALUE(static_cast<Dst>(DstLimits::max() * -1),
+                        -ClampedNumeric<Dst>(DstLimits::max()));
+
+    // The runtime paths for saturated negation differ significantly from what
+    // gets evaluated at compile-time. Making this test volatile forces the
+    // compiler to generate code rather than fold constant expressions.
+    volatile Dst value = Dst(0);
+    TEST_EXPECTED_VALUE(0, -MakeClampedNum(value));
+    value = Dst(1);
+    TEST_EXPECTED_VALUE(-1, -MakeClampedNum(value));
+    value = Dst(2);
+    TEST_EXPECTED_VALUE(-2, -MakeClampedNum(value));
+    value = Dst(-1);
+    TEST_EXPECTED_VALUE(1, -MakeClampedNum(value));
+    value = Dst(-2);
+    TEST_EXPECTED_VALUE(2, -MakeClampedNum(value));
+    value = DstLimits::max();
+    TEST_EXPECTED_VALUE(Dst(DstLimits::max() * -1), -MakeClampedNum(value));
+    value = Dst(-1 * DstLimits::max());
+    TEST_EXPECTED_VALUE(DstLimits::max(), -MakeClampedNum(value));
+    value = DstLimits::lowest();
+    TEST_EXPECTED_VALUE(DstLimits::max(), -MakeClampedNum(value));
+  }
+
+  // Generic absolute value.
+  TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>().Abs());
+  TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(1).Abs());
+  TEST_EXPECTED_VALUE(DstLimits::max(),
+                      CheckedNumeric<Dst>(DstLimits::max()).Abs());
+
+  TEST_EXPECTED_VALUE(0, ClampedNumeric<Dst>().Abs());
+  TEST_EXPECTED_VALUE(1, ClampedNumeric<Dst>(1).Abs());
+  TEST_EXPECTED_VALUE(DstLimits::max(),
+                      ClampedNumeric<Dst>(DstLimits::max()).Abs());
+
+  // Generic addition.
+  TEST_EXPECTED_VALUE(1, (CheckedNumeric<Dst>() + 1));
+  TEST_EXPECTED_VALUE(2, (CheckedNumeric<Dst>(1) + 1));
+  if (numeric_limits<Dst>::is_signed)
+    TEST_EXPECTED_VALUE(0, (CheckedNumeric<Dst>(-1) + 1));
+  TEST_EXPECTED_SUCCESS(CheckedNumeric<Dst>(DstLimits::lowest()) + 1);
+  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::max()) +
+                        DstLimits::max());
+
+  TEST_EXPECTED_VALUE(1, (ClampedNumeric<Dst>() + 1));
+  TEST_EXPECTED_VALUE(2, (ClampedNumeric<Dst>(1) + 1));
+  if (numeric_limits<Dst>::is_signed)
+    TEST_EXPECTED_VALUE(0, (ClampedNumeric<Dst>(-1) + 1));
+  TEST_EXPECTED_VALUE(DstLimits::lowest() + 1,
+                      ClampedNumeric<Dst>(DstLimits::lowest()) + 1);
+  TEST_EXPECTED_VALUE(DstLimits::Overflow(),
+                      ClampedNumeric<Dst>(DstLimits::max()) + DstLimits::max());
+
+  // Generic subtraction.
+  TEST_EXPECTED_VALUE(0, (CheckedNumeric<Dst>(1) - 1));
+  TEST_EXPECTED_SUCCESS(CheckedNumeric<Dst>(DstLimits::max()) - 1);
+  if (numeric_limits<Dst>::is_signed) {
+    TEST_EXPECTED_VALUE(-1, (CheckedNumeric<Dst>() - 1));
+    TEST_EXPECTED_VALUE(-2, (CheckedNumeric<Dst>(-1) - 1));
+  } else {
+    TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::max()) - -1);
+  }
+
+  TEST_EXPECTED_VALUE(0, (ClampedNumeric<Dst>(1) - 1));
+  TEST_EXPECTED_VALUE(DstLimits::max() - 1,
+                      ClampedNumeric<Dst>(DstLimits::max()) - 1);
+  if (numeric_limits<Dst>::is_signed) {
+    TEST_EXPECTED_VALUE(-1, (ClampedNumeric<Dst>() - 1));
+    TEST_EXPECTED_VALUE(-2, (ClampedNumeric<Dst>(-1) - 1));
+  } else {
+    TEST_EXPECTED_VALUE(DstLimits::max(),
+                        ClampedNumeric<Dst>(DstLimits::max()) - -1);
+  }
+
+  // Generic multiplication.
+  TEST_EXPECTED_VALUE(0, (CheckedNumeric<Dst>() * 1));
+  TEST_EXPECTED_VALUE(1, (CheckedNumeric<Dst>(1) * 1));
+  TEST_EXPECTED_VALUE(0, (CheckedNumeric<Dst>(0) * 0));
+  if (numeric_limits<Dst>::is_signed) {
+    TEST_EXPECTED_VALUE(0, (CheckedNumeric<Dst>(-1) * 0));
+    TEST_EXPECTED_VALUE(0, (CheckedNumeric<Dst>(0) * -1));
+    TEST_EXPECTED_VALUE(-2, (CheckedNumeric<Dst>(-1) * 2));
+  } else {
+    TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::max()) * -2);
+    TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::max()) *
+                          CheckedNumeric<uintmax_t>(-2));
+  }
+  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::max()) *
+                        DstLimits::max());
+
+  TEST_EXPECTED_VALUE(0, (ClampedNumeric<Dst>() * 1));
+  TEST_EXPECTED_VALUE(1, (ClampedNumeric<Dst>(1) * 1));
+  TEST_EXPECTED_VALUE(0, (ClampedNumeric<Dst>(0) * 0));
+  if (numeric_limits<Dst>::is_signed) {
+    TEST_EXPECTED_VALUE(0, (ClampedNumeric<Dst>(-1) * 0));
+    TEST_EXPECTED_VALUE(0, (ClampedNumeric<Dst>(0) * -1));
+    TEST_EXPECTED_VALUE(-2, (ClampedNumeric<Dst>(-1) * 2));
+  } else {
+    TEST_EXPECTED_VALUE(DstLimits::Underflow(),
+                        ClampedNumeric<Dst>(DstLimits::max()) * -2);
+    TEST_EXPECTED_VALUE(0, ClampedNumeric<Dst>(DstLimits::max()) *
+                               ClampedNumeric<uintmax_t>(-2));
+  }
+  TEST_EXPECTED_VALUE(DstLimits::Overflow(),
+                      ClampedNumeric<Dst>(DstLimits::max()) * DstLimits::max());
+
+  // Generic division.
+  TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>() / 1);
+  TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(1) / 1);
+  TEST_EXPECTED_VALUE(DstLimits::lowest() / 2,
+                      CheckedNumeric<Dst>(DstLimits::lowest()) / 2);
+  TEST_EXPECTED_VALUE(DstLimits::max() / 2,
+                      CheckedNumeric<Dst>(DstLimits::max()) / 2);
+  TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(1) / 0);
+
+  TEST_EXPECTED_VALUE(0, ClampedNumeric<Dst>() / 1);
+  TEST_EXPECTED_VALUE(1, ClampedNumeric<Dst>(1) / 1);
+  TEST_EXPECTED_VALUE(DstLimits::lowest() / 2,
+                      ClampedNumeric<Dst>(DstLimits::lowest()) / 2);
+  TEST_EXPECTED_VALUE(DstLimits::max() / 2,
+                      ClampedNumeric<Dst>(DstLimits::max()) / 2);
+  TEST_EXPECTED_VALUE(DstLimits::Overflow(), ClampedNumeric<Dst>(1) / 0);
+  TEST_EXPECTED_VALUE(DstLimits::Underflow(), ClampedNumeric<Dst>(-1) / 0);
+  TEST_EXPECTED_VALUE(0, ClampedNumeric<Dst>(0) / 0);
+
+  TestSpecializedArithmetic<Dst>(dst, line);
+}
+
+// Helper macro to wrap displaying the conversion types and line numbers.
+#define TEST_ARITHMETIC(Dst) TestArithmetic<Dst>(#Dst, __LINE__)
+
+TEST(SafeNumerics, SignedIntegerMath) {
+  TEST_ARITHMETIC(int8_t);
+  TEST_ARITHMETIC(int16_t);
+  TEST_ARITHMETIC(int);
+  TEST_ARITHMETIC(intptr_t);
+  TEST_ARITHMETIC(intmax_t);
+}
+
+TEST(SafeNumerics, UnsignedIntegerMath) {
+  TEST_ARITHMETIC(uint8_t);
+  TEST_ARITHMETIC(uint16_t);
+  TEST_ARITHMETIC(unsigned int);
+  TEST_ARITHMETIC(uintptr_t);
+  TEST_ARITHMETIC(uintmax_t);
+}
+
+TEST(SafeNumerics, FloatingPointMath) {
+  TEST_ARITHMETIC(float);
+  TEST_ARITHMETIC(double);
+}
+
+// Enumerates the five different conversions types we need to test.
+enum NumericConversionType {
+  SIGN_PRESERVING_VALUE_PRESERVING,
+  SIGN_PRESERVING_NARROW,
+  SIGN_TO_UNSIGN_WIDEN_OR_EQUAL,
+  SIGN_TO_UNSIGN_NARROW,
+  UNSIGN_TO_SIGN_NARROW_OR_EQUAL,
+};
+
+// Template covering the different conversion tests.
+template <typename Dst, typename Src, NumericConversionType conversion>
+struct TestNumericConversion {};
+
+enum RangeConstraint {
+  RANGE_VALID = 0x0,      // Value can be represented by the destination type.
+  RANGE_UNDERFLOW = 0x1,  // Value would underflow.
+  RANGE_OVERFLOW = 0x2,   // Value would overflow.
+  RANGE_INVALID = RANGE_UNDERFLOW | RANGE_OVERFLOW  // Invalid (i.e. NaN).
+};
+
+// These are some wrappers to make the tests a bit cleaner.
+constexpr RangeConstraint RangeCheckToEnum(const RangeCheck constraint) {
+  return static_cast<RangeConstraint>(
+      static_cast<int>(constraint.IsOverflowFlagSet()) << 1 |
+      static_cast<int>(constraint.IsUnderflowFlagSet()));
+}
+
+// EXPECT_EQ wrappers providing specific detail on test failures.
+#define TEST_EXPECTED_RANGE(expected, actual)                               \
+  EXPECT_EQ(expected,                                                       \
+            RangeCheckToEnum(DstRangeRelationToSrcRange<Dst>(actual)))      \
+      << "Conversion test: " << src << " value " << actual << " to " << dst \
+      << " on line " << line
+
+template <typename Dst, typename Src>
+void TestStrictComparison(const char* dst, const char* src, int line) {
+  using DstLimits = numeric_limits<Dst>;
+  using SrcLimits = numeric_limits<Src>;
+  static_assert(StrictNumeric<Src>(SrcLimits::lowest()) < DstLimits::max(), "");
+  static_assert(StrictNumeric<Src>(SrcLimits::lowest()) < SrcLimits::max(), "");
+  static_assert(!(StrictNumeric<Src>(SrcLimits::lowest()) >= DstLimits::max()),
+                "");
+  static_assert(!(StrictNumeric<Src>(SrcLimits::lowest()) >= SrcLimits::max()),
+                "");
+  static_assert(StrictNumeric<Src>(SrcLimits::lowest()) <= DstLimits::max(),
+                "");
+  static_assert(StrictNumeric<Src>(SrcLimits::lowest()) <= SrcLimits::max(),
+                "");
+  static_assert(!(StrictNumeric<Src>(SrcLimits::lowest()) > DstLimits::max()),
+                "");
+  static_assert(!(StrictNumeric<Src>(SrcLimits::lowest()) > SrcLimits::max()),
+                "");
+  static_assert(StrictNumeric<Src>(SrcLimits::max()) > DstLimits::lowest(), "");
+  static_assert(StrictNumeric<Src>(SrcLimits::max()) > SrcLimits::lowest(), "");
+  static_assert(!(StrictNumeric<Src>(SrcLimits::max()) <= DstLimits::lowest()),
+                "");
+  static_assert(!(StrictNumeric<Src>(SrcLimits::max()) <= SrcLimits::lowest()),
+                "");
+  static_assert(StrictNumeric<Src>(SrcLimits::max()) >= DstLimits::lowest(),
+                "");
+  static_assert(StrictNumeric<Src>(SrcLimits::max()) >= SrcLimits::lowest(),
+                "");
+  static_assert(!(StrictNumeric<Src>(SrcLimits::max()) < DstLimits::lowest()),
+                "");
+  static_assert(!(StrictNumeric<Src>(SrcLimits::max()) < SrcLimits::lowest()),
+                "");
+  static_assert(StrictNumeric<Src>(static_cast<Src>(1)) == static_cast<Dst>(1),
+                "");
+  static_assert(StrictNumeric<Src>(static_cast<Src>(1)) != static_cast<Dst>(0),
+                "");
+  static_assert(StrictNumeric<Src>(SrcLimits::max()) != static_cast<Dst>(0),
+                "");
+  static_assert(StrictNumeric<Src>(SrcLimits::max()) != DstLimits::lowest(),
+                "");
+  static_assert(
+      !(StrictNumeric<Src>(static_cast<Src>(1)) != static_cast<Dst>(1)), "");
+  static_assert(
+      !(StrictNumeric<Src>(static_cast<Src>(1)) == static_cast<Dst>(0)), "");
+
+  // Due to differences in float handling between compilers, these aren't
+  // compile-time constants everywhere. So, we use run-time tests.
+  EXPECT_EQ(
+      SrcLimits::max(),
+      MakeCheckedNum(SrcLimits::max()).Max(DstLimits::lowest()).ValueOrDie());
+  EXPECT_EQ(
+      DstLimits::max(),
+      MakeCheckedNum(SrcLimits::lowest()).Max(DstLimits::max()).ValueOrDie());
+  EXPECT_EQ(
+      DstLimits::lowest(),
+      MakeCheckedNum(SrcLimits::max()).Min(DstLimits::lowest()).ValueOrDie());
+  EXPECT_EQ(
+      SrcLimits::lowest(),
+      MakeCheckedNum(SrcLimits::lowest()).Min(DstLimits::max()).ValueOrDie());
+  EXPECT_EQ(SrcLimits::lowest(), CheckMin(MakeStrictNum(1), MakeCheckedNum(0),
+                                          DstLimits::max(), SrcLimits::lowest())
+                                     .ValueOrDie());
+  EXPECT_EQ(DstLimits::max(), CheckMax(MakeStrictNum(1), MakeCheckedNum(0),
+                                       DstLimits::max(), SrcLimits::lowest())
+                                  .ValueOrDie());
+
+  EXPECT_EQ(SrcLimits::max(),
+            MakeClampedNum(SrcLimits::max()).Max(DstLimits::lowest()));
+  EXPECT_EQ(DstLimits::max(),
+            MakeClampedNum(SrcLimits::lowest()).Max(DstLimits::max()));
+  EXPECT_EQ(DstLimits::lowest(),
+            MakeClampedNum(SrcLimits::max()).Min(DstLimits::lowest()));
+  EXPECT_EQ(SrcLimits::lowest(),
+            MakeClampedNum(SrcLimits::lowest()).Min(DstLimits::max()));
+  EXPECT_EQ(SrcLimits::lowest(),
+            ClampMin(MakeStrictNum(1), MakeClampedNum(0), DstLimits::max(),
+                     SrcLimits::lowest()));
+  EXPECT_EQ(DstLimits::max(), ClampMax(MakeStrictNum(1), MakeClampedNum(0),
+                                       DstLimits::max(), SrcLimits::lowest()));
+
+  if (IsValueInRangeForNumericType<Dst>(SrcLimits::max())) {
+    TEST_EXPECTED_VALUE(Dst(SrcLimits::max()), (CommonMax<Dst, Src>()));
+    TEST_EXPECTED_VALUE(Dst(SrcLimits::max()),
+                        (CommonMaxOrMin<Dst, Src>(false)));
+  } else {
+    TEST_EXPECTED_VALUE(DstLimits::max(), (CommonMax<Dst, Src>()));
+    TEST_EXPECTED_VALUE(DstLimits::max(), (CommonMaxOrMin<Dst, Src>(false)));
+  }
+
+  if (IsValueInRangeForNumericType<Dst>(SrcLimits::lowest())) {
+    TEST_EXPECTED_VALUE(Dst(SrcLimits::lowest()), (CommonMin<Dst, Src>()));
+    TEST_EXPECTED_VALUE(Dst(SrcLimits::lowest()),
+                        (CommonMaxOrMin<Dst, Src>(true)));
+  } else {
+    TEST_EXPECTED_VALUE(DstLimits::lowest(), (CommonMin<Dst, Src>()));
+    TEST_EXPECTED_VALUE(DstLimits::lowest(), (CommonMaxOrMin<Dst, Src>(true)));
+  }
+}
+
+template <typename Dst, typename Src>
+struct TestNumericConversion<Dst, Src, SIGN_PRESERVING_VALUE_PRESERVING> {
+  static void Test(const char* dst, const char* src, int line) {
+    using SrcLimits = SaturationDefaultLimits<Src>;
+    using DstLimits = SaturationDefaultLimits<Dst>;
+    // Integral to floating.
+    static_assert((DstLimits::is_iec559 && SrcLimits::is_integer) ||
+                      // Not floating to integral and...
+                      (!(DstLimits::is_integer && SrcLimits::is_iec559) &&
+                       // Same sign, same numeric, source is narrower or same.
+                       ((SrcLimits::is_signed == DstLimits::is_signed &&
+                         MaxExponent<Dst>::value >= MaxExponent<Src>::value) ||
+                        // Or signed destination and source is smaller
+                        (DstLimits::is_signed &&
+                         MaxExponent<Dst>::value >= MaxExponent<Src>::value))),
+                  "Comparison must be sign preserving and value preserving");
+
+    TestStrictComparison<Dst, Src>(dst, src, line);
+
+    const CheckedNumeric<Dst> checked_dst = SrcLimits::max();
+    const ClampedNumeric<Dst> clamped_dst = SrcLimits::max();
+    TEST_EXPECTED_SUCCESS(checked_dst);
+    TEST_EXPECTED_VALUE(Dst(SrcLimits::max()), clamped_dst);
+    if (MaxExponent<Dst>::value > MaxExponent<Src>::value) {
+      if (MaxExponent<Dst>::value >= MaxExponent<Src>::value * 2 - 1) {
+        // At least twice larger type.
+        TEST_EXPECTED_SUCCESS(SrcLimits::max() * checked_dst);
+        TEST_EXPECTED_VALUE(SrcLimits::max() * clamped_dst,
+                            Dst(SrcLimits::max()) * SrcLimits::max());
+      } else {  // Larger, but not at least twice as large.
+        TEST_EXPECTED_FAILURE(SrcLimits::max() * checked_dst);
+        TEST_EXPECTED_SUCCESS(checked_dst + 1);
+        TEST_EXPECTED_VALUE(DstLimits::Overflow(),
+                            SrcLimits::max() * clamped_dst);
+        TEST_EXPECTED_VALUE(Dst(SrcLimits::max()) + Dst(1),
+                            clamped_dst + Dst(1));
+      }
+    } else {  // Same width type.
+      TEST_EXPECTED_FAILURE(checked_dst + 1);
+      TEST_EXPECTED_VALUE(DstLimits::Overflow(), clamped_dst + Dst(1));
+    }
+
+    TEST_EXPECTED_RANGE(RANGE_VALID, SrcLimits::max());
+    TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(1));
+    if (SrcLimits::is_iec559) {
+      TEST_EXPECTED_RANGE(RANGE_VALID, SrcLimits::max() * static_cast<Src>(-1));
+      TEST_EXPECTED_RANGE(RANGE_OVERFLOW, SrcLimits::infinity());
+      TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, SrcLimits::infinity() * -1);
+      TEST_EXPECTED_RANGE(RANGE_INVALID, SrcLimits::quiet_NaN());
+    } else if (numeric_limits<Src>::is_signed) {
+      // This block reverses the Src to Dst relationship so we don't have to
+      // complicate the test macros.
+      if (!std::is_same<Src, Dst>::value) {
+        TEST_EXPECTED_SUCCESS(CheckDiv(SrcLimits::lowest(), Dst(-1)));
+      }
+      TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(-1));
+      TEST_EXPECTED_RANGE(RANGE_VALID, SrcLimits::lowest());
+    }
+  }
+};
+
+template <typename Dst, typename Src>
+struct TestNumericConversion<Dst, Src, SIGN_PRESERVING_NARROW> {
+  static void Test(const char* dst, const char* src, int line) {
+    using SrcLimits = SaturationDefaultLimits<Src>;
+    using DstLimits = SaturationDefaultLimits<Dst>;
+    static_assert(SrcLimits::is_signed == DstLimits::is_signed,
+                  "Destination and source sign must be the same");
+    static_assert(MaxExponent<Dst>::value <= MaxExponent<Src>::value,
+                  "Destination must be narrower than source");
+
+    TestStrictComparison<Dst, Src>(dst, src, line);
+
+    const CheckedNumeric<Dst> checked_dst;
+    TEST_EXPECTED_FAILURE(checked_dst + SrcLimits::max());
+    TEST_EXPECTED_VALUE(1, checked_dst + Src(1));
+    TEST_EXPECTED_FAILURE(checked_dst - SrcLimits::max());
+
+    ClampedNumeric<Dst> clamped_dst;
+    TEST_EXPECTED_VALUE(DstLimits::Overflow(), clamped_dst + SrcLimits::max());
+    TEST_EXPECTED_VALUE(1, clamped_dst + Src(1));
+    TEST_EXPECTED_VALUE(DstLimits::Underflow(), clamped_dst - SrcLimits::max());
+    clamped_dst += SrcLimits::max();
+    TEST_EXPECTED_VALUE(DstLimits::Overflow(), clamped_dst);
+    clamped_dst = DstLimits::max();
+    clamped_dst += SrcLimits::max();
+    TEST_EXPECTED_VALUE(DstLimits::Overflow(), clamped_dst);
+    clamped_dst = DstLimits::max();
+    clamped_dst -= SrcLimits::max();
+    TEST_EXPECTED_VALUE(DstLimits::Underflow(), clamped_dst);
+    clamped_dst = 0;
+
+    TEST_EXPECTED_RANGE(RANGE_OVERFLOW, SrcLimits::max());
+    TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(1));
+    if (SrcLimits::is_iec559) {
+      TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, SrcLimits::max() * -1);
+      TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(-1));
+      TEST_EXPECTED_RANGE(RANGE_OVERFLOW, SrcLimits::infinity());
+      TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, SrcLimits::infinity() * -1);
+      TEST_EXPECTED_RANGE(RANGE_INVALID, SrcLimits::quiet_NaN());
+      if (DstLimits::is_integer) {
+        if (SrcLimits::digits < DstLimits::digits) {
+          TEST_EXPECTED_RANGE(RANGE_OVERFLOW,
+                              static_cast<Src>(DstLimits::max()));
+        } else {
+          TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(DstLimits::max()));
+        }
+        TEST_EXPECTED_RANGE(
+            RANGE_VALID,
+            static_cast<Src>(GetMaxConvertibleToFloat<Src, Dst>()));
+        TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(DstLimits::lowest()));
+      }
+    } else if (SrcLimits::is_signed) {
+      TEST_EXPECTED_VALUE(-1, checked_dst - static_cast<Src>(1));
+      TEST_EXPECTED_VALUE(-1, clamped_dst - static_cast<Src>(1));
+      TEST_EXPECTED_VALUE(Src(Src(0) - DstLimits::lowest()),
+                          ClampDiv(DstLimits::lowest(), Src(-1)));
+      TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, SrcLimits::lowest());
+      TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(-1));
+    } else {
+      TEST_EXPECTED_FAILURE(checked_dst - static_cast<Src>(1));
+      TEST_EXPECTED_VALUE(Dst(0), clamped_dst - static_cast<Src>(1));
+      TEST_EXPECTED_RANGE(RANGE_VALID, SrcLimits::lowest());
+    }
+  }
+};
+
+template <typename Dst, typename Src>
+struct TestNumericConversion<Dst, Src, SIGN_TO_UNSIGN_WIDEN_OR_EQUAL> {
+  static void Test(const char* dst, const char* src, int line) {
+    using SrcLimits = SaturationDefaultLimits<Src>;
+    using DstLimits = SaturationDefaultLimits<Dst>;
+    static_assert(MaxExponent<Dst>::value >= MaxExponent<Src>::value,
+                  "Destination must be equal or wider than source.");
+    static_assert(SrcLimits::is_signed, "Source must be signed");
+    static_assert(!DstLimits::is_signed, "Destination must be unsigned");
+
+    TestStrictComparison<Dst, Src>(dst, src, line);
+
+    const CheckedNumeric<Dst> checked_dst;
+    TEST_EXPECTED_VALUE(SrcLimits::max(), checked_dst + SrcLimits::max());
+    TEST_EXPECTED_FAILURE(checked_dst + static_cast<Src>(-1));
+    TEST_EXPECTED_SUCCESS(checked_dst * static_cast<Src>(-1));
+    TEST_EXPECTED_FAILURE(checked_dst + SrcLimits::lowest());
+    TEST_EXPECTED_VALUE(Dst(0), CheckDiv(Dst(0), Src(-1)));
+
+    const ClampedNumeric<Dst> clamped_dst;
+    TEST_EXPECTED_VALUE(SrcLimits::max(), clamped_dst + SrcLimits::max());
+    TEST_EXPECTED_VALUE(DstLimits::Underflow(),
+                        clamped_dst + static_cast<Src>(-1));
+    TEST_EXPECTED_VALUE(0, clamped_dst * static_cast<Src>(-1));
+    TEST_EXPECTED_VALUE(DstLimits::Underflow(),
+                        clamped_dst + SrcLimits::lowest());
+
+    TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, SrcLimits::lowest());
+    TEST_EXPECTED_RANGE(RANGE_VALID, SrcLimits::max());
+    TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(1));
+    TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, static_cast<Src>(-1));
+  }
+};
+
+template <typename Dst, typename Src>
+struct TestNumericConversion<Dst, Src, SIGN_TO_UNSIGN_NARROW> {
+  static void Test(const char* dst, const char* src, int line) {
+    using SrcLimits = SaturationDefaultLimits<Src>;
+    using DstLimits = SaturationDefaultLimits<Dst>;
+    static_assert(MaxExponent<Dst>::value < MaxExponent<Src>::value,
+                  "Destination must be narrower than source.");
+    static_assert(SrcLimits::is_signed, "Source must be signed.");
+    static_assert(!DstLimits::is_signed, "Destination must be unsigned.");
+
+    TestStrictComparison<Dst, Src>(dst, src, line);
+
+    const CheckedNumeric<Dst> checked_dst;
+    TEST_EXPECTED_VALUE(1, checked_dst + static_cast<Src>(1));
+    TEST_EXPECTED_FAILURE(checked_dst + SrcLimits::max());
+    TEST_EXPECTED_FAILURE(checked_dst + static_cast<Src>(-1));
+    TEST_EXPECTED_FAILURE(checked_dst + SrcLimits::lowest());
+
+    ClampedNumeric<Dst> clamped_dst;
+    TEST_EXPECTED_VALUE(1, clamped_dst + static_cast<Src>(1));
+    TEST_EXPECTED_VALUE(DstLimits::Overflow(), clamped_dst + SrcLimits::max());
+    TEST_EXPECTED_VALUE(DstLimits::Underflow(),
+                        clamped_dst + static_cast<Src>(-1));
+    TEST_EXPECTED_VALUE(DstLimits::Underflow(),
+                        clamped_dst + SrcLimits::lowest());
+    clamped_dst += SrcLimits::max();
+    TEST_EXPECTED_VALUE(DstLimits::Overflow(), clamped_dst);
+    clamped_dst = DstLimits::max();
+    clamped_dst += SrcLimits::max();
+    TEST_EXPECTED_VALUE(DstLimits::Overflow(), clamped_dst);
+    clamped_dst = DstLimits::max();
+    clamped_dst -= SrcLimits::max();
+    TEST_EXPECTED_VALUE(DstLimits::Underflow(), clamped_dst);
+    clamped_dst = 0;
+
+    TEST_EXPECTED_RANGE(RANGE_OVERFLOW, SrcLimits::max());
+    TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(1));
+    TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, static_cast<Src>(-1));
+
+    // Additional saturation tests.
+    EXPECT_EQ(DstLimits::max(), saturated_cast<Dst>(SrcLimits::max()));
+    EXPECT_EQ(DstLimits::lowest(), saturated_cast<Dst>(SrcLimits::lowest()));
+
+    if (SrcLimits::is_iec559) {
+      EXPECT_EQ(Dst(0), saturated_cast<Dst>(SrcLimits::quiet_NaN()));
+
+      TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, SrcLimits::max() * -1);
+      TEST_EXPECTED_RANGE(RANGE_OVERFLOW, SrcLimits::infinity());
+      TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, SrcLimits::infinity() * -1);
+      TEST_EXPECTED_RANGE(RANGE_INVALID, SrcLimits::quiet_NaN());
+      if (DstLimits::is_integer) {
+        if (SrcLimits::digits < DstLimits::digits) {
+          TEST_EXPECTED_RANGE(RANGE_OVERFLOW,
+                              static_cast<Src>(DstLimits::max()));
+        } else {
+          TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(DstLimits::max()));
+        }
+        TEST_EXPECTED_RANGE(
+            RANGE_VALID,
+            static_cast<Src>(GetMaxConvertibleToFloat<Src, Dst>()));
+        TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(DstLimits::lowest()));
+      }
+    } else {
+      TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, SrcLimits::lowest());
+    }
+  }
+};
+
+template <typename Dst, typename Src>
+struct TestNumericConversion<Dst, Src, UNSIGN_TO_SIGN_NARROW_OR_EQUAL> {
+  static void Test(const char* dst, const char* src, int line) {
+    using SrcLimits = SaturationDefaultLimits<Src>;
+    using DstLimits = SaturationDefaultLimits<Dst>;
+    static_assert(MaxExponent<Dst>::value <= MaxExponent<Src>::value,
+                  "Destination must be narrower or equal to source.");
+    static_assert(!SrcLimits::is_signed, "Source must be unsigned.");
+    static_assert(DstLimits::is_signed, "Destination must be signed.");
+
+    TestStrictComparison<Dst, Src>(dst, src, line);
+
+    const CheckedNumeric<Dst> checked_dst;
+    TEST_EXPECTED_VALUE(1, checked_dst + static_cast<Src>(1));
+    TEST_EXPECTED_FAILURE(checked_dst + SrcLimits::max());
+    TEST_EXPECTED_VALUE(SrcLimits::lowest(), checked_dst + SrcLimits::lowest());
+
+    const ClampedNumeric<Dst> clamped_dst;
+    TEST_EXPECTED_VALUE(1, clamped_dst + static_cast<Src>(1));
+    TEST_EXPECTED_VALUE(DstLimits::Overflow(), clamped_dst + SrcLimits::max());
+    TEST_EXPECTED_VALUE(SrcLimits::lowest(), clamped_dst + SrcLimits::lowest());
+
+    TEST_EXPECTED_RANGE(RANGE_VALID, SrcLimits::lowest());
+    TEST_EXPECTED_RANGE(RANGE_OVERFLOW, SrcLimits::max());
+    TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(1));
+
+    // Additional saturation tests.
+    EXPECT_EQ(DstLimits::max(), saturated_cast<Dst>(SrcLimits::max()));
+    EXPECT_EQ(Dst(0), saturated_cast<Dst>(SrcLimits::lowest()));
+  }
+};
+
+// Helper macro to wrap displaying the conversion types and line numbers
+#define TEST_NUMERIC_CONVERSION(d, s, t) \
+  TestNumericConversion<d, s, t>::Test(#d, #s, __LINE__)
+
+TEST(SafeNumerics, IntMinOperations) {
+  TEST_NUMERIC_CONVERSION(int8_t, int8_t, SIGN_PRESERVING_VALUE_PRESERVING);
+  TEST_NUMERIC_CONVERSION(uint8_t, uint8_t, SIGN_PRESERVING_VALUE_PRESERVING);
+
+  TEST_NUMERIC_CONVERSION(int8_t, int16_t, SIGN_PRESERVING_NARROW);
+  TEST_NUMERIC_CONVERSION(int8_t, int, SIGN_PRESERVING_NARROW);
+  TEST_NUMERIC_CONVERSION(uint8_t, uint16_t, SIGN_PRESERVING_NARROW);
+  TEST_NUMERIC_CONVERSION(uint8_t, unsigned int, SIGN_PRESERVING_NARROW);
+  TEST_NUMERIC_CONVERSION(int8_t, float, SIGN_PRESERVING_NARROW);
+
+  TEST_NUMERIC_CONVERSION(uint8_t, int8_t, SIGN_TO_UNSIGN_WIDEN_OR_EQUAL);
+
+  TEST_NUMERIC_CONVERSION(uint8_t, int16_t, SIGN_TO_UNSIGN_NARROW);
+  TEST_NUMERIC_CONVERSION(uint8_t, int, SIGN_TO_UNSIGN_NARROW);
+  TEST_NUMERIC_CONVERSION(uint8_t, intmax_t, SIGN_TO_UNSIGN_NARROW);
+  TEST_NUMERIC_CONVERSION(uint8_t, float, SIGN_TO_UNSIGN_NARROW);
+
+  TEST_NUMERIC_CONVERSION(int8_t, uint16_t, UNSIGN_TO_SIGN_NARROW_OR_EQUAL);
+  TEST_NUMERIC_CONVERSION(int8_t, unsigned int, UNSIGN_TO_SIGN_NARROW_OR_EQUAL);
+  TEST_NUMERIC_CONVERSION(int8_t, uintmax_t, UNSIGN_TO_SIGN_NARROW_OR_EQUAL);
+}
+
+TEST(SafeNumerics, Int16Operations) {
+  TEST_NUMERIC_CONVERSION(int16_t, int16_t, SIGN_PRESERVING_VALUE_PRESERVING);
+  TEST_NUMERIC_CONVERSION(uint16_t, uint16_t, SIGN_PRESERVING_VALUE_PRESERVING);
+
+  TEST_NUMERIC_CONVERSION(int16_t, int, SIGN_PRESERVING_NARROW);
+  TEST_NUMERIC_CONVERSION(uint16_t, unsigned int, SIGN_PRESERVING_NARROW);
+  TEST_NUMERIC_CONVERSION(int16_t, float, SIGN_PRESERVING_NARROW);
+
+  TEST_NUMERIC_CONVERSION(uint16_t, int16_t, SIGN_TO_UNSIGN_WIDEN_OR_EQUAL);
+
+  TEST_NUMERIC_CONVERSION(uint16_t, int, SIGN_TO_UNSIGN_NARROW);
+  TEST_NUMERIC_CONVERSION(uint16_t, intmax_t, SIGN_TO_UNSIGN_NARROW);
+  TEST_NUMERIC_CONVERSION(uint16_t, float, SIGN_TO_UNSIGN_NARROW);
+
+  TEST_NUMERIC_CONVERSION(int16_t, unsigned int,
+                          UNSIGN_TO_SIGN_NARROW_OR_EQUAL);
+  TEST_NUMERIC_CONVERSION(int16_t, uintmax_t, UNSIGN_TO_SIGN_NARROW_OR_EQUAL);
+}
+
+TEST(SafeNumerics, IntOperations) {
+  TEST_NUMERIC_CONVERSION(int, int, SIGN_PRESERVING_VALUE_PRESERVING);
+  TEST_NUMERIC_CONVERSION(unsigned int, unsigned int,
+                          SIGN_PRESERVING_VALUE_PRESERVING);
+  TEST_NUMERIC_CONVERSION(int, int8_t, SIGN_PRESERVING_VALUE_PRESERVING);
+  TEST_NUMERIC_CONVERSION(unsigned int, uint8_t,
+                          SIGN_PRESERVING_VALUE_PRESERVING);
+  TEST_NUMERIC_CONVERSION(int, uint8_t, SIGN_PRESERVING_VALUE_PRESERVING);
+
+  TEST_NUMERIC_CONVERSION(int, intmax_t, SIGN_PRESERVING_NARROW);
+  TEST_NUMERIC_CONVERSION(unsigned int, uintmax_t, SIGN_PRESERVING_NARROW);
+  TEST_NUMERIC_CONVERSION(int, float, SIGN_PRESERVING_NARROW);
+  TEST_NUMERIC_CONVERSION(int, double, SIGN_PRESERVING_NARROW);
+
+  TEST_NUMERIC_CONVERSION(unsigned int, int, SIGN_TO_UNSIGN_WIDEN_OR_EQUAL);
+  TEST_NUMERIC_CONVERSION(unsigned int, int8_t, SIGN_TO_UNSIGN_WIDEN_OR_EQUAL);
+
+  TEST_NUMERIC_CONVERSION(unsigned int, intmax_t, SIGN_TO_UNSIGN_NARROW);
+  TEST_NUMERIC_CONVERSION(unsigned int, float, SIGN_TO_UNSIGN_NARROW);
+  TEST_NUMERIC_CONVERSION(unsigned int, double, SIGN_TO_UNSIGN_NARROW);
+
+  TEST_NUMERIC_CONVERSION(int, unsigned int, UNSIGN_TO_SIGN_NARROW_OR_EQUAL);
+  TEST_NUMERIC_CONVERSION(int, uintmax_t, UNSIGN_TO_SIGN_NARROW_OR_EQUAL);
+}
+
+TEST(SafeNumerics, IntMaxOperations) {
+  TEST_NUMERIC_CONVERSION(intmax_t, intmax_t, SIGN_PRESERVING_VALUE_PRESERVING);
+  TEST_NUMERIC_CONVERSION(uintmax_t, uintmax_t,
+                          SIGN_PRESERVING_VALUE_PRESERVING);
+  TEST_NUMERIC_CONVERSION(intmax_t, int, SIGN_PRESERVING_VALUE_PRESERVING);
+  TEST_NUMERIC_CONVERSION(uintmax_t, unsigned int,
+                          SIGN_PRESERVING_VALUE_PRESERVING);
+  TEST_NUMERIC_CONVERSION(intmax_t, unsigned int,
+                          SIGN_PRESERVING_VALUE_PRESERVING);
+  TEST_NUMERIC_CONVERSION(intmax_t, uint8_t, SIGN_PRESERVING_VALUE_PRESERVING);
+
+  TEST_NUMERIC_CONVERSION(intmax_t, float, SIGN_PRESERVING_NARROW);
+  TEST_NUMERIC_CONVERSION(intmax_t, double, SIGN_PRESERVING_NARROW);
+
+  TEST_NUMERIC_CONVERSION(uintmax_t, int, SIGN_TO_UNSIGN_WIDEN_OR_EQUAL);
+  TEST_NUMERIC_CONVERSION(uintmax_t, int8_t, SIGN_TO_UNSIGN_WIDEN_OR_EQUAL);
+
+  TEST_NUMERIC_CONVERSION(uintmax_t, float, SIGN_TO_UNSIGN_NARROW);
+  TEST_NUMERIC_CONVERSION(uintmax_t, double, SIGN_TO_UNSIGN_NARROW);
+
+  TEST_NUMERIC_CONVERSION(intmax_t, uintmax_t, UNSIGN_TO_SIGN_NARROW_OR_EQUAL);
+}
+
+TEST(SafeNumerics, FloatOperations) {
+  TEST_NUMERIC_CONVERSION(float, intmax_t, SIGN_PRESERVING_VALUE_PRESERVING);
+  TEST_NUMERIC_CONVERSION(float, uintmax_t, SIGN_PRESERVING_VALUE_PRESERVING);
+  TEST_NUMERIC_CONVERSION(float, int, SIGN_PRESERVING_VALUE_PRESERVING);
+  TEST_NUMERIC_CONVERSION(float, unsigned int,
+                          SIGN_PRESERVING_VALUE_PRESERVING);
+
+  TEST_NUMERIC_CONVERSION(float, double, SIGN_PRESERVING_NARROW);
+}
+
+TEST(SafeNumerics, DoubleOperations) {
+  TEST_NUMERIC_CONVERSION(double, intmax_t, SIGN_PRESERVING_VALUE_PRESERVING);
+  TEST_NUMERIC_CONVERSION(double, uintmax_t, SIGN_PRESERVING_VALUE_PRESERVING);
+  TEST_NUMERIC_CONVERSION(double, int, SIGN_PRESERVING_VALUE_PRESERVING);
+  TEST_NUMERIC_CONVERSION(double, unsigned int,
+                          SIGN_PRESERVING_VALUE_PRESERVING);
+}
+
+TEST(SafeNumerics, SizeTOperations) {
+  TEST_NUMERIC_CONVERSION(size_t, int, SIGN_TO_UNSIGN_WIDEN_OR_EQUAL);
+  TEST_NUMERIC_CONVERSION(int, size_t, UNSIGN_TO_SIGN_NARROW_OR_EQUAL);
+}
+
+// A one-off test to ensure StrictNumeric won't resolve to an incorrect type.
+// If this fails we'll just get a compiler error on an ambiguous overload.
+int TestOverload(int) {  // Overload fails.
+  return 0;
+}
+uint8_t TestOverload(uint8_t) {  // Overload fails.
+  return 0;
+}
+size_t TestOverload(size_t) {  // Overload succeeds.
+  return 0;
+}
+
+static_assert(
+    std::is_same<decltype(TestOverload(StrictNumeric<int>())), int>::value,
+    "");
+static_assert(std::is_same<decltype(TestOverload(StrictNumeric<size_t>())),
+                           size_t>::value,
+              "");
+
+template <typename T>
+struct CastTest1 {
+  static constexpr T NaN() { return -1; }
+  static constexpr T max() { return numeric_limits<T>::max() - 1; }
+  static constexpr T Overflow() { return max(); }
+  static constexpr T lowest() { return numeric_limits<T>::lowest() + 1; }
+  static constexpr T Underflow() { return lowest(); }
+};
+
+template <typename T>
+struct CastTest2 {
+  static constexpr T NaN() { return 11; }
+  static constexpr T max() { return 10; }
+  static constexpr T Overflow() { return max(); }
+  static constexpr T lowest() { return 1; }
+  static constexpr T Underflow() { return lowest(); }
+};
+
+TEST(SafeNumerics, CastTests) {
+// MSVC catches and warns that we're forcing saturation in these tests.
+// Since that's intentional, we need to shut this warning off.
+#if defined(COMPILER_MSVC)
+#pragma warning(disable : 4756)
+#endif
+
+  int small_positive = 1;
+  int small_negative = -1;
+  double double_small = 1.0;
+  double double_large = numeric_limits<double>::max();
+  double double_infinity = numeric_limits<float>::infinity();
+  double double_large_int = numeric_limits<int>::max();
+  double double_small_int = numeric_limits<int>::lowest();
+
+  // Just test that the casts compile, since the other tests cover logic.
+  EXPECT_EQ(0, checked_cast<int>(static_cast<size_t>(0)));
+  EXPECT_EQ(0, strict_cast<int>(static_cast<char>(0)));
+  EXPECT_EQ(0, strict_cast<int>(static_cast<unsigned char>(0)));
+  EXPECT_EQ(0U, strict_cast<unsigned>(static_cast<unsigned char>(0)));
+  EXPECT_EQ(1ULL, static_cast<uint64_t>(StrictNumeric<size_t>(1U)));
+  EXPECT_EQ(1ULL, static_cast<uint64_t>(SizeT(1U)));
+  EXPECT_EQ(1U, static_cast<size_t>(StrictNumeric<unsigned>(1U)));
+
+  EXPECT_TRUE(CheckedNumeric<uint64_t>(StrictNumeric<unsigned>(1U)).IsValid());
+  EXPECT_TRUE(CheckedNumeric<int>(StrictNumeric<unsigned>(1U)).IsValid());
+  EXPECT_FALSE(CheckedNumeric<unsigned>(StrictNumeric<int>(-1)).IsValid());
+
+  EXPECT_TRUE(IsValueNegative(-1));
+  EXPECT_TRUE(IsValueNegative(numeric_limits<int>::lowest()));
+  EXPECT_FALSE(IsValueNegative(numeric_limits<unsigned>::lowest()));
+  EXPECT_TRUE(IsValueNegative(numeric_limits<double>::lowest()));
+  EXPECT_FALSE(IsValueNegative(0));
+  EXPECT_FALSE(IsValueNegative(1));
+  EXPECT_FALSE(IsValueNegative(0u));
+  EXPECT_FALSE(IsValueNegative(1u));
+  EXPECT_FALSE(IsValueNegative(numeric_limits<int>::max()));
+  EXPECT_FALSE(IsValueNegative(numeric_limits<unsigned>::max()));
+  EXPECT_FALSE(IsValueNegative(numeric_limits<double>::max()));
+
+  // These casts and coercions will fail to compile:
+  // EXPECT_EQ(0, strict_cast<int>(static_cast<size_t>(0)));
+  // EXPECT_EQ(0, strict_cast<size_t>(static_cast<int>(0)));
+  // EXPECT_EQ(1ULL, StrictNumeric<size_t>(1));
+  // EXPECT_EQ(1, StrictNumeric<size_t>(1U));
+
+  // Test various saturation corner cases.
+  EXPECT_EQ(saturated_cast<int>(small_negative),
+            static_cast<int>(small_negative));
+  EXPECT_EQ(saturated_cast<int>(small_positive),
+            static_cast<int>(small_positive));
+  EXPECT_EQ(saturated_cast<unsigned>(small_negative), static_cast<unsigned>(0));
+  EXPECT_EQ(saturated_cast<int>(double_small), static_cast<int>(double_small));
+  EXPECT_EQ(saturated_cast<int>(double_large), numeric_limits<int>::max());
+  EXPECT_EQ(saturated_cast<float>(double_large), double_infinity);
+  EXPECT_EQ(saturated_cast<float>(-double_large), -double_infinity);
+  EXPECT_EQ(numeric_limits<int>::lowest(),
+            saturated_cast<int>(double_small_int));
+  EXPECT_EQ(numeric_limits<int>::max(), saturated_cast<int>(double_large_int));
+
+  // Test the saturated cast overrides.
+  using FloatLimits = numeric_limits<float>;
+  using IntLimits = numeric_limits<int>;
+  EXPECT_EQ(-1, (saturated_cast<int, CastTest1>(FloatLimits::quiet_NaN())));
+  EXPECT_EQ(CastTest1<int>::max(),
+            (saturated_cast<int, CastTest1>(FloatLimits::infinity())));
+  EXPECT_EQ(CastTest1<int>::max(),
+            (saturated_cast<int, CastTest1>(FloatLimits::max())));
+  EXPECT_EQ(CastTest1<int>::max(),
+            (saturated_cast<int, CastTest1>(float(IntLimits::max()))));
+  EXPECT_EQ(CastTest1<int>::lowest(),
+            (saturated_cast<int, CastTest1>(-FloatLimits::infinity())));
+  EXPECT_EQ(CastTest1<int>::lowest(),
+            (saturated_cast<int, CastTest1>(FloatLimits::lowest())));
+  EXPECT_EQ(0, (saturated_cast<int, CastTest1>(0.0)));
+  EXPECT_EQ(1, (saturated_cast<int, CastTest1>(1.0)));
+  EXPECT_EQ(-1, (saturated_cast<int, CastTest1>(-1.0)));
+  EXPECT_EQ(0, (saturated_cast<int, CastTest1>(0)));
+  EXPECT_EQ(1, (saturated_cast<int, CastTest1>(1)));
+  EXPECT_EQ(-1, (saturated_cast<int, CastTest1>(-1)));
+  EXPECT_EQ(CastTest1<int>::lowest(),
+            (saturated_cast<int, CastTest1>(float(IntLimits::lowest()))));
+  EXPECT_EQ(11, (saturated_cast<int, CastTest2>(FloatLimits::quiet_NaN())));
+  EXPECT_EQ(10, (saturated_cast<int, CastTest2>(FloatLimits::infinity())));
+  EXPECT_EQ(10, (saturated_cast<int, CastTest2>(FloatLimits::max())));
+  EXPECT_EQ(1, (saturated_cast<int, CastTest2>(-FloatLimits::infinity())));
+  EXPECT_EQ(1, (saturated_cast<int, CastTest2>(FloatLimits::lowest())));
+  EXPECT_EQ(1, (saturated_cast<int, CastTest2>(0U)));
+
+  float not_a_number = std::numeric_limits<float>::infinity() -
+                       std::numeric_limits<float>::infinity();
+  EXPECT_TRUE(std::isnan(not_a_number));
+  EXPECT_EQ(0, saturated_cast<int>(not_a_number));
+
+  // Test the CheckedNumeric value extractions functions.
+  auto int8_min = MakeCheckedNum(numeric_limits<int8_t>::lowest());
+  auto int8_max = MakeCheckedNum(numeric_limits<int8_t>::max());
+  auto double_max = MakeCheckedNum(numeric_limits<double>::max());
+  static_assert(
+      std::is_same<int16_t,
+                   decltype(int8_min.ValueOrDie<int16_t>())::type>::value,
+      "ValueOrDie returning incorrect type.");
+  static_assert(
+      std::is_same<int16_t,
+                   decltype(int8_min.ValueOrDefault<int16_t>(0))::type>::value,
+      "ValueOrDefault returning incorrect type.");
+  EXPECT_FALSE(IsValidForType<uint8_t>(int8_min));
+  EXPECT_TRUE(IsValidForType<uint8_t>(int8_max));
+  EXPECT_EQ(static_cast<int>(numeric_limits<int8_t>::lowest()),
+            ValueOrDieForType<int>(int8_min));
+  EXPECT_TRUE(IsValidForType<uint32_t>(int8_max));
+  EXPECT_EQ(static_cast<int>(numeric_limits<int8_t>::max()),
+            ValueOrDieForType<int>(int8_max));
+  EXPECT_EQ(0, ValueOrDefaultForType<int>(double_max, 0));
+  uint8_t uint8_dest = 0;
+  int16_t int16_dest = 0;
+  double double_dest = 0;
+  EXPECT_TRUE(int8_max.AssignIfValid(&uint8_dest));
+  EXPECT_EQ(static_cast<uint8_t>(numeric_limits<int8_t>::max()), uint8_dest);
+  EXPECT_FALSE(int8_min.AssignIfValid(&uint8_dest));
+  EXPECT_TRUE(int8_max.AssignIfValid(&int16_dest));
+  EXPECT_EQ(static_cast<int16_t>(numeric_limits<int8_t>::max()), int16_dest);
+  EXPECT_TRUE(int8_min.AssignIfValid(&int16_dest));
+  EXPECT_EQ(static_cast<int16_t>(numeric_limits<int8_t>::lowest()), int16_dest);
+  EXPECT_FALSE(double_max.AssignIfValid(&uint8_dest));
+  EXPECT_FALSE(double_max.AssignIfValid(&int16_dest));
+  EXPECT_TRUE(double_max.AssignIfValid(&double_dest));
+  EXPECT_EQ(numeric_limits<double>::max(), double_dest);
+  EXPECT_EQ(1, checked_cast<int>(StrictNumeric<int>(1)));
+  EXPECT_EQ(1, saturated_cast<int>(StrictNumeric<int>(1)));
+  EXPECT_EQ(1, strict_cast<int>(StrictNumeric<int>(1)));
+
+  enum class EnumTest { kOne = 1 };
+  EXPECT_EQ(1, checked_cast<int>(EnumTest::kOne));
+  EXPECT_EQ(1, saturated_cast<int>(EnumTest::kOne));
+  EXPECT_EQ(1, strict_cast<int>(EnumTest::kOne));
+}
+
+TEST(SafeNumerics, IsValueInRangeForNumericType) {
+  EXPECT_TRUE(IsValueInRangeForNumericType<uint32_t>(0));
+  EXPECT_TRUE(IsValueInRangeForNumericType<uint32_t>(1));
+  EXPECT_TRUE(IsValueInRangeForNumericType<uint32_t>(2));
+  EXPECT_FALSE(IsValueInRangeForNumericType<uint32_t>(-1));
+  EXPECT_TRUE(IsValueInRangeForNumericType<uint32_t>(0xffffffffu));
+  EXPECT_TRUE(IsValueInRangeForNumericType<uint32_t>(UINT64_C(0xffffffff)));
+  EXPECT_FALSE(IsValueInRangeForNumericType<uint32_t>(UINT64_C(0x100000000)));
+  EXPECT_FALSE(IsValueInRangeForNumericType<uint32_t>(UINT64_C(0x100000001)));
+  EXPECT_FALSE(IsValueInRangeForNumericType<uint32_t>(
+      std::numeric_limits<int32_t>::lowest()));
+  EXPECT_FALSE(IsValueInRangeForNumericType<uint32_t>(
+      std::numeric_limits<int64_t>::lowest()));
+
+  EXPECT_TRUE(IsValueInRangeForNumericType<int32_t>(0));
+  EXPECT_TRUE(IsValueInRangeForNumericType<int32_t>(1));
+  EXPECT_TRUE(IsValueInRangeForNumericType<int32_t>(2));
+  EXPECT_TRUE(IsValueInRangeForNumericType<int32_t>(-1));
+  EXPECT_TRUE(IsValueInRangeForNumericType<int32_t>(0x7fffffff));
+  EXPECT_TRUE(IsValueInRangeForNumericType<int32_t>(0x7fffffffu));
+  EXPECT_FALSE(IsValueInRangeForNumericType<int32_t>(0x80000000u));
+  EXPECT_FALSE(IsValueInRangeForNumericType<int32_t>(0xffffffffu));
+  EXPECT_FALSE(IsValueInRangeForNumericType<int32_t>(INT64_C(0x80000000)));
+  EXPECT_FALSE(IsValueInRangeForNumericType<int32_t>(INT64_C(0xffffffff)));
+  EXPECT_FALSE(IsValueInRangeForNumericType<int32_t>(INT64_C(0x100000000)));
+  EXPECT_TRUE(IsValueInRangeForNumericType<int32_t>(
+      std::numeric_limits<int32_t>::lowest()));
+  EXPECT_TRUE(IsValueInRangeForNumericType<int32_t>(
+      static_cast<int64_t>(std::numeric_limits<int32_t>::lowest())));
+  EXPECT_FALSE(IsValueInRangeForNumericType<int32_t>(
+      static_cast<int64_t>(std::numeric_limits<int32_t>::lowest()) - 1));
+  EXPECT_FALSE(IsValueInRangeForNumericType<int32_t>(
+      std::numeric_limits<int64_t>::lowest()));
+
+  EXPECT_TRUE(IsValueInRangeForNumericType<uint64_t>(0));
+  EXPECT_TRUE(IsValueInRangeForNumericType<uint64_t>(1));
+  EXPECT_TRUE(IsValueInRangeForNumericType<uint64_t>(2));
+  EXPECT_FALSE(IsValueInRangeForNumericType<uint64_t>(-1));
+  EXPECT_TRUE(IsValueInRangeForNumericType<uint64_t>(0xffffffffu));
+  EXPECT_TRUE(IsValueInRangeForNumericType<uint64_t>(UINT64_C(0xffffffff)));
+  EXPECT_TRUE(IsValueInRangeForNumericType<uint64_t>(UINT64_C(0x100000000)));
+  EXPECT_TRUE(IsValueInRangeForNumericType<uint64_t>(UINT64_C(0x100000001)));
+  EXPECT_FALSE(IsValueInRangeForNumericType<uint64_t>(
+      std::numeric_limits<int32_t>::lowest()));
+  EXPECT_FALSE(IsValueInRangeForNumericType<uint64_t>(INT64_C(-1)));
+  EXPECT_FALSE(IsValueInRangeForNumericType<uint64_t>(
+      std::numeric_limits<int64_t>::lowest()));
+
+  EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>(0));
+  EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>(1));
+  EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>(2));
+  EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>(-1));
+  EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>(0x7fffffff));
+  EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>(0x7fffffffu));
+  EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>(0x80000000u));
+  EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>(0xffffffffu));
+  EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>(INT64_C(0x80000000)));
+  EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>(INT64_C(0xffffffff)));
+  EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>(INT64_C(0x100000000)));
+  EXPECT_TRUE(
+      IsValueInRangeForNumericType<int64_t>(INT64_C(0x7fffffffffffffff)));
+  EXPECT_TRUE(
+      IsValueInRangeForNumericType<int64_t>(UINT64_C(0x7fffffffffffffff)));
+  EXPECT_FALSE(
+      IsValueInRangeForNumericType<int64_t>(UINT64_C(0x8000000000000000)));
+  EXPECT_FALSE(
+      IsValueInRangeForNumericType<int64_t>(UINT64_C(0xffffffffffffffff)));
+  EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>(
+      std::numeric_limits<int32_t>::lowest()));
+  EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>(
+      static_cast<int64_t>(std::numeric_limits<int32_t>::lowest())));
+  EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>(
+      std::numeric_limits<int64_t>::lowest()));
+}
+
+TEST(SafeNumerics, CompoundNumericOperations) {
+  CheckedNumeric<int> a = 1;
+  CheckedNumeric<int> b = 2;
+  CheckedNumeric<int> c = 3;
+  CheckedNumeric<int> d = 4;
+  a += b;
+  EXPECT_EQ(3, a.ValueOrDie());
+  a -= c;
+  EXPECT_EQ(0, a.ValueOrDie());
+  d /= b;
+  EXPECT_EQ(2, d.ValueOrDie());
+  d *= d;
+  EXPECT_EQ(4, d.ValueOrDie());
+
+  CheckedNumeric<int> too_large = std::numeric_limits<int>::max();
+  EXPECT_TRUE(too_large.IsValid());
+  too_large += d;
+  EXPECT_FALSE(too_large.IsValid());
+  too_large -= d;
+  EXPECT_FALSE(too_large.IsValid());
+  too_large /= d;
+  EXPECT_FALSE(too_large.IsValid());
+}
+
+TEST(SafeNumerics, VariadicNumericOperations) {
+  {  // Synthetic scope to avoid variable naming collisions.
+    auto a = CheckAdd(1, 2UL, MakeCheckedNum(3LL), 4).ValueOrDie();
+    EXPECT_EQ(static_cast<decltype(a)::type>(10), a);
+    auto b = CheckSub(MakeCheckedNum(20.0), 2UL, 4).ValueOrDie();
+    EXPECT_EQ(static_cast<decltype(b)::type>(14.0), b);
+    auto c = CheckMul(20.0, MakeCheckedNum(1), 5, 3UL).ValueOrDie();
+    EXPECT_EQ(static_cast<decltype(c)::type>(300.0), c);
+    auto d = CheckDiv(20.0, 2.0, MakeCheckedNum(5LL), -4).ValueOrDie();
+    EXPECT_EQ(static_cast<decltype(d)::type>(-.5), d);
+    auto e = CheckMod(MakeCheckedNum(20), 3).ValueOrDie();
+    EXPECT_EQ(static_cast<decltype(e)::type>(2), e);
+    auto f = CheckLsh(1, MakeCheckedNum(2)).ValueOrDie();
+    EXPECT_EQ(static_cast<decltype(f)::type>(4), f);
+    auto g = CheckRsh(4, MakeCheckedNum(2)).ValueOrDie();
+    EXPECT_EQ(static_cast<decltype(g)::type>(1), g);
+    auto h = CheckRsh(CheckAdd(1, 1, 1, 1), CheckSub(4, 2)).ValueOrDie();
+    EXPECT_EQ(static_cast<decltype(h)::type>(1), h);
+  }
+
+  {
+    auto a = ClampAdd(1, 2UL, MakeClampedNum(3LL), 4);
+    EXPECT_EQ(static_cast<decltype(a)::type>(10), a);
+    auto b = ClampSub(MakeClampedNum(20.0), 2UL, 4);
+    EXPECT_EQ(static_cast<decltype(b)::type>(14.0), b);
+    auto c = ClampMul(20.0, MakeClampedNum(1), 5, 3UL);
+    EXPECT_EQ(static_cast<decltype(c)::type>(300.0), c);
+    auto d = ClampDiv(20.0, 2.0, MakeClampedNum(5LL), -4);
+    EXPECT_EQ(static_cast<decltype(d)::type>(-.5), d);
+    auto e = ClampMod(MakeClampedNum(20), 3);
+    EXPECT_EQ(static_cast<decltype(e)::type>(2), e);
+    auto f = ClampLsh(1, MakeClampedNum(2U));
+    EXPECT_EQ(static_cast<decltype(f)::type>(4), f);
+    auto g = ClampRsh(4, MakeClampedNum(2U));
+    EXPECT_EQ(static_cast<decltype(g)::type>(1), g);
+    auto h = ClampRsh(ClampAdd(1, 1, 1, 1), ClampSub(4U, 2));
+    EXPECT_EQ(static_cast<decltype(h)::type>(1), h);
+  }
+}
+
+#if defined(__clang__)
+#pragma clang diagnostic pop  // -Winteger-overflow
+#endif
+
+}  // namespace internal
+}  // namespace base
diff --git a/base/scoped_native_library_unittest.cc b/base/scoped_native_library_unittest.cc
new file mode 100644
index 0000000..763b45f
--- /dev/null
+++ b/base/scoped_native_library_unittest.cc
@@ -0,0 +1,48 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/scoped_native_library.h"
+
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if defined(OS_WIN)
+#include "base/files/file_path.h"
+#include "base/strings/utf_string_conversions.h"
+#endif
+
+namespace base {
+
+// Tests whether or not a function pointer retrieved via ScopedNativeLibrary
+// is available only in a scope.
+TEST(ScopedNativeLibrary, Basic) {
+#if defined(OS_WIN)
+  // Get the pointer to DirectDrawCreate() from "ddraw.dll" and verify it
+  // is valid only in this scope.
+  // FreeLibrary() doesn't actually unload a DLL until its reference count
+  // becomes zero, i.e. function pointer is still valid if the DLL used
+  // in this test is also used by another part of this executable.
+  // So, this test uses "ddraw.dll", which is not used by Chrome at all but
+  // installed on all versions of Windows.
+  const char kFunctionName[] = "DirectDrawCreate";
+  NativeLibrary native_library;
+  {
+    FilePath path(FilePath::FromUTF8Unsafe(GetNativeLibraryName("ddraw")));
+    native_library = LoadNativeLibrary(path, nullptr);
+    ScopedNativeLibrary library(native_library);
+    EXPECT_TRUE(library.is_valid());
+    EXPECT_EQ(native_library, library.get());
+    FARPROC test_function =
+        reinterpret_cast<FARPROC>(library.GetFunctionPointer(kFunctionName));
+    EXPECT_EQ(0, IsBadCodePtr(test_function));
+    EXPECT_EQ(
+        GetFunctionPointerFromNativeLibrary(native_library, kFunctionName),
+        test_function);
+  }
+  EXPECT_FALSE(
+      GetFunctionPointerFromNativeLibrary(native_library, kFunctionName));
+#endif
+}
+
+}  // namespace base
diff --git a/base/sequenced_task_runner_unittest.cc b/base/sequenced_task_runner_unittest.cc
new file mode 100644
index 0000000..4dcc7e5
--- /dev/null
+++ b/base/sequenced_task_runner_unittest.cc
@@ -0,0 +1,104 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/sequenced_task_runner.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/gtest_prod_util.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/threading/thread.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace {
+
+class FlagOnDelete {
+ public:
+  FlagOnDelete(bool* deleted,
+               scoped_refptr<SequencedTaskRunner> expected_deletion_sequence)
+      : deleted_(deleted),
+        expected_deletion_sequence_(std::move(expected_deletion_sequence)) {}
+
+ private:
+  friend class DeleteHelper<FlagOnDelete>;
+  FRIEND_TEST_ALL_PREFIXES(SequencedTaskRunnerTest,
+                           OnTaskRunnerDeleterTargetStoppedEarly);
+
+  ~FlagOnDelete() {
+    EXPECT_FALSE(*deleted_);
+    *deleted_ = true;
+    if (expected_deletion_sequence_)
+      EXPECT_TRUE(expected_deletion_sequence_->RunsTasksInCurrentSequence());
+  }
+
+  bool* deleted_;
+  const scoped_refptr<SequencedTaskRunner> expected_deletion_sequence_;
+
+  DISALLOW_COPY_AND_ASSIGN(FlagOnDelete);
+};
+
+class SequencedTaskRunnerTest : public testing::Test {
+ protected:
+  SequencedTaskRunnerTest() : foreign_thread_("foreign") {}
+
+  void SetUp() override {
+    main_runner_ = message_loop_.task_runner();
+
+    foreign_thread_.Start();
+    foreign_runner_ = foreign_thread_.task_runner();
+  }
+
+  scoped_refptr<SequencedTaskRunner> main_runner_;
+  scoped_refptr<SequencedTaskRunner> foreign_runner_;
+
+  Thread foreign_thread_;
+
+ private:
+  MessageLoop message_loop_;
+
+  DISALLOW_COPY_AND_ASSIGN(SequencedTaskRunnerTest);
+};
+
+using SequenceBoundUniquePtr =
+    std::unique_ptr<FlagOnDelete, OnTaskRunnerDeleter>;
+
+TEST_F(SequencedTaskRunnerTest, OnTaskRunnerDeleterOnMainThread) {
+  bool deleted_on_main_thread = false;
+  SequenceBoundUniquePtr ptr(
+      new FlagOnDelete(&deleted_on_main_thread, main_runner_),
+      OnTaskRunnerDeleter(main_runner_));
+  EXPECT_FALSE(deleted_on_main_thread);
+  foreign_runner_->PostTask(
+      FROM_HERE, BindOnce([](SequenceBoundUniquePtr) {}, std::move(ptr)));
+
+  {
+    RunLoop run_loop;
+    foreign_runner_->PostTaskAndReply(FROM_HERE, BindOnce([] {}),
+                                      run_loop.QuitClosure());
+    run_loop.Run();
+  }
+  EXPECT_TRUE(deleted_on_main_thread);
+}
+
+TEST_F(SequencedTaskRunnerTest, OnTaskRunnerDeleterTargetStoppedEarly) {
+  bool deleted_on_main_thread = false;
+  FlagOnDelete* raw = new FlagOnDelete(&deleted_on_main_thread, main_runner_);
+  SequenceBoundUniquePtr ptr(raw, OnTaskRunnerDeleter(foreign_runner_));
+  EXPECT_FALSE(deleted_on_main_thread);
+
+  // Stopping the target ahead of deleting |ptr| should make its
+  // OnTaskRunnerDeleter no-op.
+  foreign_thread_.Stop();
+  ptr = nullptr;
+  EXPECT_FALSE(deleted_on_main_thread);
+
+  delete raw;
+  EXPECT_TRUE(deleted_on_main_thread);
+}
+
+}  // namespace
+}  // namespace base
diff --git a/base/strings/latin1_string_conversions.cc b/base/strings/latin1_string_conversions.cc
new file mode 100644
index 0000000..dca62ce
--- /dev/null
+++ b/base/strings/latin1_string_conversions.cc
@@ -0,0 +1,19 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/strings/latin1_string_conversions.h"
+
+namespace base {
+
+string16 Latin1OrUTF16ToUTF16(size_t length,
+                              const Latin1Char* latin1,
+                              const char16* utf16) {
+  if (!length)
+    return string16();
+  if (latin1)
+    return string16(latin1, latin1 + length);
+  return string16(utf16, utf16 + length);
+}
+
+}  // namespace base
diff --git a/base/strings/latin1_string_conversions.h b/base/strings/latin1_string_conversions.h
new file mode 100644
index 0000000..42113ef
--- /dev/null
+++ b/base/strings/latin1_string_conversions.h
@@ -0,0 +1,34 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_STRINGS_LATIN1_STRING_CONVERSIONS_H_
+#define BASE_STRINGS_LATIN1_STRING_CONVERSIONS_H_
+
+#include <stddef.h>
+
+#include <string>
+
+#include "base/base_export.h"
+#include "base/strings/string16.h"
+
+namespace base {
+
+// This definition of Latin1Char matches the definition of LChar in Blink. We
+// use unsigned char rather than char to make less tempting to mix and match
+// Latin-1 and UTF-8 characters..
+typedef unsigned char Latin1Char;
+
+// This somewhat odd function is designed to help us convert from Blink Strings
+// to string16. A Blink string is either backed by an array of Latin-1
+// characters or an array of UTF-16 characters. This function is called by
+// WebString::operator string16() to convert one or the other character array
+// to string16. This function is defined here rather than in WebString.h to
+// avoid binary bloat in all the callers of the conversion operator.
+BASE_EXPORT string16 Latin1OrUTF16ToUTF16(size_t length,
+                                          const Latin1Char* latin1,
+                                          const char16* utf16);
+
+}  // namespace base
+
+#endif  // BASE_STRINGS_LATIN1_STRING_CONVERSIONS_H_
diff --git a/base/strings/utf_offset_string_conversions.cc b/base/strings/utf_offset_string_conversions.cc
new file mode 100644
index 0000000..b91ee03
--- /dev/null
+++ b/base/strings/utf_offset_string_conversions.cc
@@ -0,0 +1,268 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/strings/utf_offset_string_conversions.h"
+
+#include <stdint.h>
+
+#include <algorithm>
+#include <memory>
+
+#include "base/logging.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/utf_string_conversion_utils.h"
+
+namespace base {
+
+OffsetAdjuster::Adjustment::Adjustment(size_t original_offset,
+                                       size_t original_length,
+                                       size_t output_length)
+    : original_offset(original_offset),
+      original_length(original_length),
+      output_length(output_length) {
+}
+
+// static
+void OffsetAdjuster::AdjustOffsets(const Adjustments& adjustments,
+                                   std::vector<size_t>* offsets_for_adjustment,
+                                   size_t limit) {
+  DCHECK(offsets_for_adjustment);
+  for (std::vector<size_t>::iterator i(offsets_for_adjustment->begin());
+       i != offsets_for_adjustment->end(); ++i)
+    AdjustOffset(adjustments, &(*i), limit);
+}
+
+// static
+void OffsetAdjuster::AdjustOffset(const Adjustments& adjustments,
+                                  size_t* offset,
+                                  size_t limit) {
+  DCHECK(offset);
+  if (*offset == string16::npos)
+    return;
+  int adjustment = 0;
+  for (Adjustments::const_iterator i = adjustments.begin();
+       i != adjustments.end(); ++i) {
+    if (*offset <= i->original_offset)
+      break;
+    if (*offset < (i->original_offset + i->original_length)) {
+      *offset = string16::npos;
+      return;
+    }
+    adjustment += static_cast<int>(i->original_length - i->output_length);
+  }
+  *offset -= adjustment;
+
+  if (*offset > limit)
+    *offset = string16::npos;
+}
+
+// static
+void OffsetAdjuster::UnadjustOffsets(
+    const Adjustments& adjustments,
+    std::vector<size_t>* offsets_for_unadjustment) {
+  if (!offsets_for_unadjustment || adjustments.empty())
+    return;
+  for (std::vector<size_t>::iterator i(offsets_for_unadjustment->begin());
+       i != offsets_for_unadjustment->end(); ++i)
+    UnadjustOffset(adjustments, &(*i));
+}
+
+// static
+void OffsetAdjuster::UnadjustOffset(const Adjustments& adjustments,
+                                    size_t* offset) {
+  if (*offset == string16::npos)
+    return;
+  int adjustment = 0;
+  for (Adjustments::const_iterator i = adjustments.begin();
+       i != adjustments.end(); ++i) {
+    if (*offset + adjustment <= i->original_offset)
+      break;
+    adjustment += static_cast<int>(i->original_length - i->output_length);
+    if ((*offset + adjustment) <
+        (i->original_offset + i->original_length)) {
+      *offset = string16::npos;
+      return;
+    }
+  }
+  *offset += adjustment;
+}
+
+// static
+void OffsetAdjuster::MergeSequentialAdjustments(
+    const Adjustments& first_adjustments,
+    Adjustments* adjustments_on_adjusted_string) {
+  Adjustments::iterator adjusted_iter = adjustments_on_adjusted_string->begin();
+  Adjustments::const_iterator first_iter = first_adjustments.begin();
+  // Simultaneously iterate over all |adjustments_on_adjusted_string| and
+  // |first_adjustments|, adding adjustments to or correcting the adjustments
+  // in |adjustments_on_adjusted_string| as we go.  |shift| keeps track of the
+  // current number of characters collapsed by |first_adjustments| up to this
+  // point.  |currently_collapsing| keeps track of the number of characters
+  // collapsed by |first_adjustments| into the current |adjusted_iter|'s
+  // length.  These are characters that will change |shift| as soon as we're
+  // done processing the current |adjusted_iter|; they are not yet reflected in
+  // |shift|.
+  size_t shift = 0;
+  size_t currently_collapsing = 0;
+  while (adjusted_iter != adjustments_on_adjusted_string->end()) {
+    if ((first_iter == first_adjustments.end()) ||
+        ((adjusted_iter->original_offset + shift +
+          adjusted_iter->original_length) <= first_iter->original_offset)) {
+      // Entire |adjusted_iter| (accounting for its shift and including its
+      // whole original length) comes before |first_iter|.
+      //
+      // Correct the offset at |adjusted_iter| and move onto the next
+      // adjustment that needs revising.
+      adjusted_iter->original_offset += shift;
+      shift += currently_collapsing;
+      currently_collapsing = 0;
+      ++adjusted_iter;
+    } else if ((adjusted_iter->original_offset + shift) >
+               first_iter->original_offset) {
+      // |first_iter| comes before the |adjusted_iter| (as adjusted by |shift|).
+
+      // It's not possible for the adjustments to overlap.  (It shouldn't
+      // be possible that we have an |adjusted_iter->original_offset| that,
+      // when adjusted by the computed |shift|, is in the middle of
+      // |first_iter|'s output's length.  After all, that would mean the
+      // current adjustment_on_adjusted_string somehow points to an offset
+      // that was supposed to have been eliminated by the first set of
+      // adjustments.)
+      DCHECK_LE(first_iter->original_offset + first_iter->output_length,
+                adjusted_iter->original_offset + shift);
+
+      // Add the |first_adjustment_iter| to the full set of adjustments while
+      // making sure |adjusted_iter| continues pointing to the same element.
+      // We do this by inserting the |first_adjustment_iter| right before
+      // |adjusted_iter|, then incrementing |adjusted_iter| so it points to
+      // the following element.
+      shift += first_iter->original_length - first_iter->output_length;
+      adjusted_iter = adjustments_on_adjusted_string->insert(
+          adjusted_iter, *first_iter);
+      ++adjusted_iter;
+      ++first_iter;
+    } else {
+      // The first adjustment adjusted something that then got further adjusted
+      // by the second set of adjustments.  In other words, |first_iter| points
+      // to something in the range covered by |adjusted_iter|'s length (after
+      // accounting for |shift|).  Precisely,
+      //   adjusted_iter->original_offset + shift
+      //   <=
+      //   first_iter->original_offset
+      //   <=
+      //   adjusted_iter->original_offset + shift +
+      //       adjusted_iter->original_length
+
+      // Modify the current |adjusted_iter| to include whatever collapsing
+      // happened in |first_iter|, then advance to the next |first_adjustments|
+      // because we dealt with the current one.
+      const int collapse = static_cast<int>(first_iter->original_length) -
+          static_cast<int>(first_iter->output_length);
+      // This function does not know how to deal with a string that expands and
+      // then gets modified, only strings that collapse and then get modified.
+      DCHECK_GT(collapse, 0);
+      adjusted_iter->original_length += collapse;
+      currently_collapsing += collapse;
+      ++first_iter;
+    }
+  }
+  DCHECK_EQ(0u, currently_collapsing);
+  if (first_iter != first_adjustments.end()) {
+    // Only first adjustments are left.  These do not need to be modified.
+    // (Their offsets are already correct with respect to the original string.)
+    // Append them all.
+    DCHECK(adjusted_iter == adjustments_on_adjusted_string->end());
+    adjustments_on_adjusted_string->insert(
+        adjustments_on_adjusted_string->end(), first_iter,
+        first_adjustments.end());
+  }
+}
+
+// Converts the given source Unicode character type to the given destination
+// Unicode character type as a STL string. The given input buffer and size
+// determine the source, and the given output STL string will be replaced by
+// the result.  If non-NULL, |adjustments| is set to reflect the all the
+// alterations to the string that are not one-character-to-one-character.
+// It will always be sorted by increasing offset.
+template<typename SrcChar, typename DestStdString>
+bool ConvertUnicode(const SrcChar* src,
+                    size_t src_len,
+                    DestStdString* output,
+                    OffsetAdjuster::Adjustments* adjustments) {
+  if (adjustments)
+    adjustments->clear();
+  // ICU requires 32-bit numbers.
+  bool success = true;
+  int32_t src_len32 = static_cast<int32_t>(src_len);
+  for (int32_t i = 0; i < src_len32; i++) {
+    uint32_t code_point;
+    size_t original_i = i;
+    size_t chars_written = 0;
+    if (ReadUnicodeCharacter(src, src_len32, &i, &code_point)) {
+      chars_written = WriteUnicodeCharacter(code_point, output);
+    } else {
+      chars_written = WriteUnicodeCharacter(0xFFFD, output);
+      success = false;
+    }
+
+    // Only bother writing an adjustment if this modification changed the
+    // length of this character.
+    // NOTE: ReadUnicodeCharacter() adjusts |i| to point _at_ the last
+    // character read, not after it (so that incrementing it in the loop
+    // increment will place it at the right location), so we need to account
+    // for that in determining the amount that was read.
+    if (adjustments && ((i - original_i + 1) != chars_written)) {
+      adjustments->push_back(OffsetAdjuster::Adjustment(
+          original_i, i - original_i + 1, chars_written));
+    }
+  }
+  return success;
+}
+
+bool UTF8ToUTF16WithAdjustments(
+    const char* src,
+    size_t src_len,
+    string16* output,
+    base::OffsetAdjuster::Adjustments* adjustments) {
+  PrepareForUTF16Or32Output(src, src_len, output);
+  return ConvertUnicode(src, src_len, output, adjustments);
+}
+
+string16 UTF8ToUTF16WithAdjustments(
+    const base::StringPiece& utf8,
+    base::OffsetAdjuster::Adjustments* adjustments) {
+  string16 result;
+  UTF8ToUTF16WithAdjustments(utf8.data(), utf8.length(), &result, adjustments);
+  return result;
+}
+
+string16 UTF8ToUTF16AndAdjustOffsets(
+    const base::StringPiece& utf8,
+    std::vector<size_t>* offsets_for_adjustment) {
+  for (size_t& offset : *offsets_for_adjustment) {
+    if (offset > utf8.length())
+      offset = string16::npos;
+  }
+  OffsetAdjuster::Adjustments adjustments;
+  string16 result = UTF8ToUTF16WithAdjustments(utf8, &adjustments);
+  OffsetAdjuster::AdjustOffsets(adjustments, offsets_for_adjustment);
+  return result;
+}
+
+std::string UTF16ToUTF8AndAdjustOffsets(
+    const base::StringPiece16& utf16,
+    std::vector<size_t>* offsets_for_adjustment) {
+  for (size_t& offset : *offsets_for_adjustment) {
+    if (offset > utf16.length())
+      offset = string16::npos;
+  }
+  std::string result;
+  PrepareForUTF8Output(utf16.data(), utf16.length(), &result);
+  OffsetAdjuster::Adjustments adjustments;
+  ConvertUnicode(utf16.data(), utf16.length(), &result, &adjustments);
+  OffsetAdjuster::AdjustOffsets(adjustments, offsets_for_adjustment);
+  return result;
+}
+
+}  // namespace base
diff --git a/base/strings/utf_offset_string_conversions.h b/base/strings/utf_offset_string_conversions.h
new file mode 100644
index 0000000..f741955
--- /dev/null
+++ b/base/strings/utf_offset_string_conversions.h
@@ -0,0 +1,114 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_STRINGS_UTF_OFFSET_STRING_CONVERSIONS_H_
+#define BASE_STRINGS_UTF_OFFSET_STRING_CONVERSIONS_H_
+
+#include <stddef.h>
+
+#include <string>
+#include <vector>
+
+#include "base/base_export.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_piece.h"
+
+namespace base {
+
+// A helper class and associated data structures to adjust offsets into a
+// string in response to various adjustments one might do to that string
+// (e.g., eliminating a range).  For details on offsets, see the comments by
+// the AdjustOffsets() function below.
+class BASE_EXPORT OffsetAdjuster {
+ public:
+  struct BASE_EXPORT Adjustment {
+    Adjustment(size_t original_offset,
+               size_t original_length,
+               size_t output_length);
+
+    size_t original_offset;
+    size_t original_length;
+    size_t output_length;
+  };
+  typedef std::vector<Adjustment> Adjustments;
+
+  // Adjusts all offsets in |offsets_for_adjustment| to reflect the adjustments
+  // recorded in |adjustments|.  Adjusted offsets greater than |limit| will be
+  // set to string16::npos.
+  //
+  // Offsets represents insertion/selection points between characters: if |src|
+  // is "abcd", then 0 is before 'a', 2 is between 'b' and 'c', and 4 is at the
+  // end of the string.  Valid input offsets range from 0 to |src_len|.  On
+  // exit, each offset will have been modified to point at the same logical
+  // position in the output string.  If an offset cannot be successfully
+  // adjusted (e.g., because it points into the middle of a multibyte sequence),
+  // it will be set to string16::npos.
+  static void AdjustOffsets(const Adjustments& adjustments,
+                            std::vector<size_t>* offsets_for_adjustment,
+                            size_t limit = string16::npos);
+
+  // Adjusts the single |offset| to reflect the adjustments recorded in
+  // |adjustments|.
+  static void AdjustOffset(const Adjustments& adjustments,
+                           size_t* offset,
+                           size_t limit = string16::npos);
+
+  // Adjusts all offsets in |offsets_for_unadjustment| to reflect the reverse
+  // of the adjustments recorded in |adjustments|.  In other words, the offsets
+  // provided represent offsets into an adjusted string and the caller wants
+  // to know the offsets they correspond to in the original string.  If an
+  // offset cannot be successfully unadjusted (e.g., because it points into
+  // the middle of a multibyte sequence), it will be set to string16::npos.
+  static void UnadjustOffsets(const Adjustments& adjustments,
+                              std::vector<size_t>* offsets_for_unadjustment);
+
+  // Adjusts the single |offset| to reflect the reverse of the adjustments
+  // recorded in |adjustments|.
+  static void UnadjustOffset(const Adjustments& adjustments,
+                             size_t* offset);
+
+  // Combines two sequential sets of adjustments, storing the combined revised
+  // adjustments in |adjustments_on_adjusted_string|.  That is, suppose a
+  // string was altered in some way, with the alterations recorded as
+  // adjustments in |first_adjustments|.  Then suppose the resulting string is
+  // further altered, with the alterations recorded as adjustments scored in
+  // |adjustments_on_adjusted_string|, with the offsets recorded in these
+  // adjustments being with respect to the intermediate string.  This function
+  // combines the two sets of adjustments into one, storing the result in
+  // |adjustments_on_adjusted_string|, whose offsets are correct with respect
+  // to the original string.
+  //
+  // Assumes both parameters are sorted by increasing offset.
+  //
+  // WARNING: Only supports |first_adjustments| that involve collapsing ranges
+  // of text, not expanding ranges.
+  static void MergeSequentialAdjustments(
+      const Adjustments& first_adjustments,
+      Adjustments* adjustments_on_adjusted_string);
+};
+
+// Like the conversions in utf_string_conversions.h, but also fills in an
+// |adjustments| parameter that reflects the alterations done to the string.
+// It may be NULL.
+BASE_EXPORT bool UTF8ToUTF16WithAdjustments(
+    const char* src,
+    size_t src_len,
+    string16* output,
+    base::OffsetAdjuster::Adjustments* adjustments);
+BASE_EXPORT string16 UTF8ToUTF16WithAdjustments(
+    const base::StringPiece& utf8,
+    base::OffsetAdjuster::Adjustments* adjustments);
+// As above, but instead internally examines the adjustments and applies them
+// to |offsets_for_adjustment|.  Input offsets greater than the length of the
+// input string will be set to string16::npos.  See comments by AdjustOffsets().
+BASE_EXPORT string16 UTF8ToUTF16AndAdjustOffsets(
+    const base::StringPiece& utf8,
+    std::vector<size_t>* offsets_for_adjustment);
+BASE_EXPORT std::string UTF16ToUTF8AndAdjustOffsets(
+    const base::StringPiece16& utf16,
+    std::vector<size_t>* offsets_for_adjustment);
+
+}  // namespace base
+
+#endif  // BASE_STRINGS_UTF_OFFSET_STRING_CONVERSIONS_H_
diff --git a/base/strings/utf_offset_string_conversions_unittest.cc b/base/strings/utf_offset_string_conversions_unittest.cc
new file mode 100644
index 0000000..c5ce647
--- /dev/null
+++ b/base/strings/utf_offset_string_conversions_unittest.cc
@@ -0,0 +1,300 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+
+#include <algorithm>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/utf_offset_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+namespace {
+
+static const size_t kNpos = string16::npos;
+
+}  // namespace
+
+TEST(UTFOffsetStringConversionsTest, AdjustOffset) {
+  struct UTF8ToUTF16Case {
+    const char* utf8;
+    size_t input_offset;
+    size_t output_offset;
+  } utf8_to_utf16_cases[] = {
+    {"", 0, 0},
+    {"", kNpos, kNpos},
+    {"\xe4\xbd\xa0\xe5\xa5\xbd", 1, kNpos},
+    {"\xe4\xbd\xa0\xe5\xa5\xbd", 3, 1},
+    {"\xed\xb0\x80z", 3, 3},
+    {"A\xF0\x90\x8C\x80z", 1, 1},
+    {"A\xF0\x90\x8C\x80z", 2, kNpos},
+    {"A\xF0\x90\x8C\x80z", 5, 3},
+    {"A\xF0\x90\x8C\x80z", 6, 4},
+    {"A\xF0\x90\x8C\x80z", kNpos, kNpos},
+  };
+  for (size_t i = 0; i < arraysize(utf8_to_utf16_cases); ++i) {
+    const size_t offset = utf8_to_utf16_cases[i].input_offset;
+    std::vector<size_t> offsets;
+    offsets.push_back(offset);
+    UTF8ToUTF16AndAdjustOffsets(utf8_to_utf16_cases[i].utf8, &offsets);
+    EXPECT_EQ(utf8_to_utf16_cases[i].output_offset, offsets[0]);
+  }
+
+  struct UTF16ToUTF8Case {
+    char16 utf16[10];
+    size_t input_offset;
+    size_t output_offset;
+  } utf16_to_utf8_cases[] = {
+      {{}, 0, 0},
+      // Converted to 3-byte utf-8 sequences
+      {{0x5909, 0x63DB}, 3, kNpos},
+      {{0x5909, 0x63DB}, 2, 6},
+      {{0x5909, 0x63DB}, 1, 3},
+      {{0x5909, 0x63DB}, 0, 0},
+      // Converted to 2-byte utf-8 sequences
+      {{'A', 0x00bc, 0x00be, 'z'}, 1, 1},
+      {{'A', 0x00bc, 0x00be, 'z'}, 2, 3},
+      {{'A', 0x00bc, 0x00be, 'z'}, 3, 5},
+      {{'A', 0x00bc, 0x00be, 'z'}, 4, 6},
+      // Surrogate pair
+      {{'A', 0xd800, 0xdf00, 'z'}, 1, 1},
+      {{'A', 0xd800, 0xdf00, 'z'}, 2, kNpos},
+      {{'A', 0xd800, 0xdf00, 'z'}, 3, 5},
+      {{'A', 0xd800, 0xdf00, 'z'}, 4, 6},
+  };
+  for (size_t i = 0; i < arraysize(utf16_to_utf8_cases); ++i) {
+    size_t offset = utf16_to_utf8_cases[i].input_offset;
+    std::vector<size_t> offsets;
+    offsets.push_back(offset);
+    UTF16ToUTF8AndAdjustOffsets(utf16_to_utf8_cases[i].utf16, &offsets);
+    EXPECT_EQ(utf16_to_utf8_cases[i].output_offset, offsets[0]) << i;
+  }
+}
+
+TEST(UTFOffsetStringConversionsTest, LimitOffsets) {
+  const OffsetAdjuster::Adjustments kNoAdjustments;
+  const size_t kLimit = 10;
+  const size_t kItems = 20;
+  std::vector<size_t> size_ts;
+  for (size_t t = 0; t < kItems; ++t) {
+    size_ts.push_back(t);
+    OffsetAdjuster::AdjustOffset(kNoAdjustments, &size_ts.back(), kLimit);
+  }
+  size_t unlimited_count = 0;
+  for (std::vector<size_t>::iterator ti = size_ts.begin(); ti != size_ts.end();
+       ++ti) {
+    if (*ti != kNpos)
+      ++unlimited_count;
+  }
+  EXPECT_EQ(11U, unlimited_count);
+
+  // Reverse the values in the vector and try again.
+  size_ts.clear();
+  for (size_t t = kItems; t > 0; --t) {
+    size_ts.push_back(t - 1);
+    OffsetAdjuster::AdjustOffset(kNoAdjustments, &size_ts.back(), kLimit);
+  }
+  unlimited_count = 0;
+  for (std::vector<size_t>::iterator ti = size_ts.begin(); ti != size_ts.end();
+       ++ti) {
+    if (*ti != kNpos)
+      ++unlimited_count;
+  }
+  EXPECT_EQ(11U, unlimited_count);
+}
+
+TEST(UTFOffsetStringConversionsTest, AdjustOffsets) {
+  // Imagine we have strings as shown in the following cases where the
+  // X's represent encoded characters.
+  // 1: abcXXXdef ==> abcXdef
+  {
+    std::vector<size_t> offsets;
+    for (size_t t = 0; t <= 9; ++t)
+      offsets.push_back(t);
+    OffsetAdjuster::Adjustments adjustments;
+    adjustments.push_back(OffsetAdjuster::Adjustment(3, 3, 1));
+    OffsetAdjuster::AdjustOffsets(adjustments, &offsets);
+    size_t expected_1[] = {0, 1, 2, 3, kNpos, kNpos, 4, 5, 6, 7};
+    EXPECT_EQ(offsets.size(), arraysize(expected_1));
+    for (size_t i = 0; i < arraysize(expected_1); ++i)
+      EXPECT_EQ(expected_1[i], offsets[i]);
+  }
+
+  // 2: XXXaXXXXbcXXXXXXXdefXXX ==> XaXXbcXXXXdefX
+  {
+    std::vector<size_t> offsets;
+    for (size_t t = 0; t <= 23; ++t)
+      offsets.push_back(t);
+    OffsetAdjuster::Adjustments adjustments;
+    adjustments.push_back(OffsetAdjuster::Adjustment(0, 3, 1));
+    adjustments.push_back(OffsetAdjuster::Adjustment(4, 4, 2));
+    adjustments.push_back(OffsetAdjuster::Adjustment(10, 7, 4));
+    adjustments.push_back(OffsetAdjuster::Adjustment(20, 3, 1));
+    OffsetAdjuster::AdjustOffsets(adjustments, &offsets);
+    size_t expected_2[] = {
+      0, kNpos, kNpos, 1, 2, kNpos, kNpos, kNpos, 4, 5, 6, kNpos, kNpos, kNpos,
+      kNpos, kNpos, kNpos, 10, 11, 12, 13, kNpos, kNpos, 14
+    };
+    EXPECT_EQ(offsets.size(), arraysize(expected_2));
+    for (size_t i = 0; i < arraysize(expected_2); ++i)
+      EXPECT_EQ(expected_2[i], offsets[i]);
+  }
+
+  // 3: XXXaXXXXbcdXXXeXX ==> aXXXXbcdXXXe
+  {
+    std::vector<size_t> offsets;
+    for (size_t t = 0; t <= 17; ++t)
+      offsets.push_back(t);
+    OffsetAdjuster::Adjustments adjustments;
+    adjustments.push_back(OffsetAdjuster::Adjustment(0, 3, 0));
+    adjustments.push_back(OffsetAdjuster::Adjustment(4, 4, 4));
+    adjustments.push_back(OffsetAdjuster::Adjustment(11, 3, 3));
+    adjustments.push_back(OffsetAdjuster::Adjustment(15, 2, 0));
+    OffsetAdjuster::AdjustOffsets(adjustments, &offsets);
+    size_t expected_3[] = {
+      0, kNpos, kNpos, 0, 1, kNpos, kNpos, kNpos, 5, 6, 7, 8, kNpos, kNpos, 11,
+      12, kNpos, 12
+    };
+    EXPECT_EQ(offsets.size(), arraysize(expected_3));
+    for (size_t i = 0; i < arraysize(expected_3); ++i)
+      EXPECT_EQ(expected_3[i], offsets[i]);
+  }
+}
+
+TEST(UTFOffsetStringConversionsTest, UnadjustOffsets) {
+  // Imagine we have strings as shown in the following cases where the
+  // X's represent encoded characters.
+  // 1: abcXXXdef ==> abcXdef
+  {
+    std::vector<size_t> offsets;
+    for (size_t t = 0; t <= 7; ++t)
+      offsets.push_back(t);
+    OffsetAdjuster::Adjustments adjustments;
+    adjustments.push_back(OffsetAdjuster::Adjustment(3, 3, 1));
+    OffsetAdjuster::UnadjustOffsets(adjustments, &offsets);
+    size_t expected_1[] = {0, 1, 2, 3, 6, 7, 8, 9};
+    EXPECT_EQ(offsets.size(), arraysize(expected_1));
+    for (size_t i = 0; i < arraysize(expected_1); ++i)
+      EXPECT_EQ(expected_1[i], offsets[i]);
+  }
+
+  // 2: XXXaXXXXbcXXXXXXXdefXXX ==> XaXXbcXXXXdefX
+  {
+    std::vector<size_t> offsets;
+    for (size_t t = 0; t <= 14; ++t)
+      offsets.push_back(t);
+    OffsetAdjuster::Adjustments adjustments;
+    adjustments.push_back(OffsetAdjuster::Adjustment(0, 3, 1));
+    adjustments.push_back(OffsetAdjuster::Adjustment(4, 4, 2));
+    adjustments.push_back(OffsetAdjuster::Adjustment(10, 7, 4));
+    adjustments.push_back(OffsetAdjuster::Adjustment(20, 3, 1));
+    OffsetAdjuster::UnadjustOffsets(adjustments, &offsets);
+    size_t expected_2[] = {
+      0, 3, 4, kNpos, 8, 9, 10, kNpos, kNpos, kNpos, 17, 18, 19, 20, 23
+    };
+    EXPECT_EQ(offsets.size(), arraysize(expected_2));
+    for (size_t i = 0; i < arraysize(expected_2); ++i)
+      EXPECT_EQ(expected_2[i], offsets[i]);
+  }
+
+  // 3: XXXaXXXXbcdXXXeXX ==> aXXXXbcdXXXe
+  {
+    std::vector<size_t> offsets;
+    for (size_t t = 0; t <= 12; ++t)
+      offsets.push_back(t);
+    OffsetAdjuster::Adjustments adjustments;
+    adjustments.push_back(OffsetAdjuster::Adjustment(0, 3, 0));
+    adjustments.push_back(OffsetAdjuster::Adjustment(4, 4, 4));
+    adjustments.push_back(OffsetAdjuster::Adjustment(11, 3, 3));
+    adjustments.push_back(OffsetAdjuster::Adjustment(15, 2, 0));
+    OffsetAdjuster::UnadjustOffsets(adjustments, &offsets);
+    size_t expected_3[] = {
+      0,  // this could just as easily be 3
+      4, kNpos, kNpos, kNpos, 8, 9, 10, 11, kNpos, kNpos, 14,
+      15  // this could just as easily be 17
+    };
+    EXPECT_EQ(offsets.size(), arraysize(expected_3));
+    for (size_t i = 0; i < arraysize(expected_3); ++i)
+      EXPECT_EQ(expected_3[i], offsets[i]);
+  }
+}
+
+// MergeSequentialAdjustments is used by net/base/escape.{h,cc} and
+// net/base/net_util.{h,cc}.  The two tests EscapeTest.AdjustOffset and
+// NetUtilTest.FormatUrlWithOffsets test its behavior extensively.  This
+// is simply a short, additional test.
+TEST(UTFOffsetStringConversionsTest, MergeSequentialAdjustments) {
+  // Pretend the input string is "abcdefghijklmnopqrstuvwxyz".
+
+  // Set up |first_adjustments| to
+  // - remove the leading "a"
+  // - combine the "bc" into one character (call it ".")
+  // - remove the "f"
+  // - remove the "tuv"
+  // The resulting string should be ".deghijklmnopqrswxyz".
+  OffsetAdjuster::Adjustments first_adjustments;
+  first_adjustments.push_back(OffsetAdjuster::Adjustment(0, 1, 0));
+  first_adjustments.push_back(OffsetAdjuster::Adjustment(1, 2, 1));
+  first_adjustments.push_back(OffsetAdjuster::Adjustment(5, 1, 0));
+  first_adjustments.push_back(OffsetAdjuster::Adjustment(19, 3, 0));
+
+  // Set up |adjustments_on_adjusted_string| to
+  // - combine the "." character that replaced "bc" with "d" into one character
+  //   (call it "?")
+  // - remove the "egh"
+  // - expand the "i" into two characters (call them "12")
+  // - combine the "jkl" into one character (call it "@")
+  // - expand the "z" into two characters (call it "34")
+  // The resulting string should be "?12@mnopqrswxy34".
+  OffsetAdjuster::Adjustments adjustments_on_adjusted_string;
+  adjustments_on_adjusted_string.push_back(OffsetAdjuster::Adjustment(
+      0, 2, 1));
+  adjustments_on_adjusted_string.push_back(OffsetAdjuster::Adjustment(
+      2, 3, 0));
+  adjustments_on_adjusted_string.push_back(OffsetAdjuster::Adjustment(
+      5, 1, 2));
+  adjustments_on_adjusted_string.push_back(OffsetAdjuster::Adjustment(
+      6, 3, 1));
+  adjustments_on_adjusted_string.push_back(OffsetAdjuster::Adjustment(
+      19, 1, 2));
+
+  // Now merge the adjustments and check the results.
+  OffsetAdjuster::MergeSequentialAdjustments(first_adjustments,
+                                             &adjustments_on_adjusted_string);
+  // The merged adjustments should look like
+  // - combine abcd into "?"
+  //   - note: it's also reasonable for the Merge function to instead produce
+  //     two adjustments instead of this, one to remove a and another to
+  //     combine bcd into "?".  This test verifies the current behavior.
+  // - remove efgh
+  // - expand i into "12"
+  // - combine jkl into "@"
+  // - remove tuv
+  // - expand z into "34"
+  ASSERT_EQ(6u, adjustments_on_adjusted_string.size());
+  EXPECT_EQ(0u, adjustments_on_adjusted_string[0].original_offset);
+  EXPECT_EQ(4u, adjustments_on_adjusted_string[0].original_length);
+  EXPECT_EQ(1u, adjustments_on_adjusted_string[0].output_length);
+  EXPECT_EQ(4u, adjustments_on_adjusted_string[1].original_offset);
+  EXPECT_EQ(4u, adjustments_on_adjusted_string[1].original_length);
+  EXPECT_EQ(0u, adjustments_on_adjusted_string[1].output_length);
+  EXPECT_EQ(8u, adjustments_on_adjusted_string[2].original_offset);
+  EXPECT_EQ(1u, adjustments_on_adjusted_string[2].original_length);
+  EXPECT_EQ(2u, adjustments_on_adjusted_string[2].output_length);
+  EXPECT_EQ(9u, adjustments_on_adjusted_string[3].original_offset);
+  EXPECT_EQ(3u, adjustments_on_adjusted_string[3].original_length);
+  EXPECT_EQ(1u, adjustments_on_adjusted_string[3].output_length);
+  EXPECT_EQ(19u, adjustments_on_adjusted_string[4].original_offset);
+  EXPECT_EQ(3u, adjustments_on_adjusted_string[4].original_length);
+  EXPECT_EQ(0u, adjustments_on_adjusted_string[4].output_length);
+  EXPECT_EQ(25u, adjustments_on_adjusted_string[5].original_offset);
+  EXPECT_EQ(1u, adjustments_on_adjusted_string[5].original_length);
+  EXPECT_EQ(2u, adjustments_on_adjusted_string[5].output_length);
+}
+
+}  // namespace base
diff --git a/base/supports_user_data.cc b/base/supports_user_data.cc
new file mode 100644
index 0000000..43ab21a
--- /dev/null
+++ b/base/supports_user_data.cc
@@ -0,0 +1,51 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/supports_user_data.h"
+
+namespace base {
+
+SupportsUserData::SupportsUserData() {
+  // Harmless to construct on a different execution sequence to subsequent
+  // usage.
+  sequence_checker_.DetachFromSequence();
+}
+
+SupportsUserData::Data* SupportsUserData::GetUserData(const void* key) const {
+  DCHECK(sequence_checker_.CalledOnValidSequence());
+  // Avoid null keys; they are too vulnerable to collision.
+  DCHECK(key);
+  DataMap::const_iterator found = user_data_.find(key);
+  if (found != user_data_.end())
+    return found->second.get();
+  return nullptr;
+}
+
+void SupportsUserData::SetUserData(const void* key,
+                                   std::unique_ptr<Data> data) {
+  DCHECK(sequence_checker_.CalledOnValidSequence());
+  // Avoid null keys; they are too vulnerable to collision.
+  DCHECK(key);
+  user_data_[key] = std::move(data);
+}
+
+void SupportsUserData::RemoveUserData(const void* key) {
+  DCHECK(sequence_checker_.CalledOnValidSequence());
+  user_data_.erase(key);
+}
+
+void SupportsUserData::DetachFromSequence() {
+  sequence_checker_.DetachFromSequence();
+}
+
+SupportsUserData::~SupportsUserData() {
+  DCHECK(sequence_checker_.CalledOnValidSequence() || user_data_.empty());
+  DataMap local_user_data;
+  user_data_.swap(local_user_data);
+  // Now this->user_data_ is empty, and any destructors called transitively from
+  // the destruction of |local_user_data| will see it that way instead of
+  // examining a being-destroyed object.
+}
+
+}  // namespace base
diff --git a/base/supports_user_data.h b/base/supports_user_data.h
new file mode 100644
index 0000000..356c973
--- /dev/null
+++ b/base/supports_user_data.h
@@ -0,0 +1,87 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_SUPPORTS_USER_DATA_H_
+#define BASE_SUPPORTS_USER_DATA_H_
+
+#include <map>
+#include <memory>
+
+#include "base/base_export.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/sequence_checker.h"
+
+// TODO(gab): Removing this include causes IWYU failures in other headers,
+// remove it in a follow- up CL.
+#include "base/threading/thread_checker.h"
+
+namespace base {
+
+// This is a helper for classes that want to allow users to stash random data by
+// key. At destruction all the objects will be destructed.
+class BASE_EXPORT SupportsUserData {
+ public:
+  SupportsUserData();
+
+  // Derive from this class and add your own data members to associate extra
+  // information with this object. Alternatively, add this as a public base
+  // class to any class with a virtual destructor.
+  class BASE_EXPORT Data {
+   public:
+    virtual ~Data() = default;
+  };
+
+  // The user data allows the clients to associate data with this object.
+  // Multiple user data values can be stored under different keys.
+  // This object will TAKE OWNERSHIP of the given data pointer, and will
+  // delete the object if it is changed or the object is destroyed.
+  // |key| must not be null--that value is too vulnerable for collision.
+  Data* GetUserData(const void* key) const;
+  void SetUserData(const void* key, std::unique_ptr<Data> data);
+  void RemoveUserData(const void* key);
+
+  // SupportsUserData is not thread-safe, and on debug build will assert it is
+  // only used on one execution sequence. Calling this method allows the caller
+  // to hand the SupportsUserData instance across execution sequences. Use only
+  // if you are taking full control of the synchronization of that hand over.
+  void DetachFromSequence();
+
+ protected:
+  virtual ~SupportsUserData();
+
+ private:
+  using DataMap = std::map<const void*, std::unique_ptr<Data>>;
+
+  // Externally-defined data accessible by key.
+  DataMap user_data_;
+  // Guards usage of |user_data_|
+  SequenceChecker sequence_checker_;
+
+  DISALLOW_COPY_AND_ASSIGN(SupportsUserData);
+};
+
+// Adapter class that releases a refcounted object when the
+// SupportsUserData::Data object is deleted.
+template <typename T>
+class UserDataAdapter : public base::SupportsUserData::Data {
+ public:
+  static T* Get(const SupportsUserData* supports_user_data, const void* key) {
+    UserDataAdapter* data =
+      static_cast<UserDataAdapter*>(supports_user_data->GetUserData(key));
+    return data ? static_cast<T*>(data->object_.get()) : NULL;
+  }
+
+  UserDataAdapter(T* object) : object_(object) {}
+  T* release() { return object_.release(); }
+
+ private:
+  scoped_refptr<T> object_;
+
+  DISALLOW_COPY_AND_ASSIGN(UserDataAdapter);
+};
+
+}  // namespace base
+
+#endif  // BASE_SUPPORTS_USER_DATA_H_
diff --git a/base/supports_user_data_unittest.cc b/base/supports_user_data_unittest.cc
new file mode 100644
index 0000000..2e0a724
--- /dev/null
+++ b/base/supports_user_data_unittest.cc
@@ -0,0 +1,40 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/supports_user_data.h"
+
+#include <vector>
+
+#include "base/memory/ptr_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace {
+
+struct TestSupportsUserData : public SupportsUserData {};
+
+struct UsesItself : public SupportsUserData::Data {
+  UsesItself(SupportsUserData* supports_user_data, const void* key)
+      : supports_user_data_(supports_user_data),
+        key_(key) {
+  }
+
+  ~UsesItself() override {
+    EXPECT_EQ(nullptr, supports_user_data_->GetUserData(key_));
+  }
+
+  SupportsUserData* supports_user_data_;
+  const void* key_;
+};
+
+TEST(SupportsUserDataTest, ClearWorksRecursively) {
+  TestSupportsUserData supports_user_data;
+  char key = 0;
+  supports_user_data.SetUserData(
+      &key, std::make_unique<UsesItself>(&supports_user_data, &key));
+  // Destruction of supports_user_data runs the actual test.
+}
+
+}  // namespace
+}  // namespace base
diff --git a/base/synchronization/waitable_event_watcher_unittest.cc b/base/synchronization/waitable_event_watcher_unittest.cc
new file mode 100644
index 0000000..ec056ef
--- /dev/null
+++ b/base/synchronization/waitable_event_watcher_unittest.cc
@@ -0,0 +1,429 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/synchronization/waitable_event_watcher.h"
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/platform_thread.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+namespace {
+
+// The message loops on which each waitable event timer should be tested.
+const MessageLoop::Type testing_message_loops[] = {
+  MessageLoop::TYPE_DEFAULT,
+  MessageLoop::TYPE_IO,
+#if !defined(OS_IOS)  // iOS does not allow direct running of the UI loop.
+  MessageLoop::TYPE_UI,
+#endif
+};
+
+void QuitWhenSignaled(WaitableEvent* event) {
+  RunLoop::QuitCurrentWhenIdleDeprecated();
+}
+
+class DecrementCountContainer {
+ public:
+  explicit DecrementCountContainer(int* counter) : counter_(counter) {}
+  void OnWaitableEventSignaled(WaitableEvent* object) {
+    // NOTE: |object| may be already deleted.
+    --(*counter_);
+  }
+
+ private:
+  int* counter_;
+};
+
+}  // namespace
+
+class WaitableEventWatcherTest
+    : public testing::TestWithParam<MessageLoop::Type> {};
+
+TEST_P(WaitableEventWatcherTest, BasicSignalManual) {
+  MessageLoop message_loop(GetParam());
+
+  // A manual-reset event that is not yet signaled.
+  WaitableEvent event(WaitableEvent::ResetPolicy::MANUAL,
+                      WaitableEvent::InitialState::NOT_SIGNALED);
+
+  WaitableEventWatcher watcher;
+  watcher.StartWatching(&event, BindOnce(&QuitWhenSignaled),
+                        SequencedTaskRunnerHandle::Get());
+
+  event.Signal();
+
+  RunLoop().Run();
+
+  EXPECT_TRUE(event.IsSignaled());
+}
+
+TEST_P(WaitableEventWatcherTest, BasicSignalAutomatic) {
+  MessageLoop message_loop(GetParam());
+
+  WaitableEvent event(WaitableEvent::ResetPolicy::AUTOMATIC,
+                      WaitableEvent::InitialState::NOT_SIGNALED);
+
+  WaitableEventWatcher watcher;
+  watcher.StartWatching(&event, BindOnce(&QuitWhenSignaled),
+                        SequencedTaskRunnerHandle::Get());
+
+  event.Signal();
+
+  RunLoop().Run();
+
+  // The WaitableEventWatcher consumes the event signal.
+  EXPECT_FALSE(event.IsSignaled());
+}
+
+TEST_P(WaitableEventWatcherTest, BasicCancel) {
+  MessageLoop message_loop(GetParam());
+
+  // A manual-reset event that is not yet signaled.
+  WaitableEvent event(WaitableEvent::ResetPolicy::MANUAL,
+                      WaitableEvent::InitialState::NOT_SIGNALED);
+
+  WaitableEventWatcher watcher;
+
+  watcher.StartWatching(&event, BindOnce(&QuitWhenSignaled),
+                        SequencedTaskRunnerHandle::Get());
+
+  watcher.StopWatching();
+}
+
+TEST_P(WaitableEventWatcherTest, CancelAfterSet) {
+  MessageLoop message_loop(GetParam());
+
+  // A manual-reset event that is not yet signaled.
+  WaitableEvent event(WaitableEvent::ResetPolicy::MANUAL,
+                      WaitableEvent::InitialState::NOT_SIGNALED);
+
+  WaitableEventWatcher watcher;
+
+  int counter = 1;
+  DecrementCountContainer delegate(&counter);
+  WaitableEventWatcher::EventCallback callback = BindOnce(
+      &DecrementCountContainer::OnWaitableEventSignaled, Unretained(&delegate));
+  watcher.StartWatching(&event, std::move(callback),
+                        SequencedTaskRunnerHandle::Get());
+
+  event.Signal();
+
+  // Let the background thread do its business
+  PlatformThread::Sleep(TimeDelta::FromMilliseconds(30));
+
+  watcher.StopWatching();
+
+  RunLoop().RunUntilIdle();
+
+  // Our delegate should not have fired.
+  EXPECT_EQ(1, counter);
+}
+
+TEST_P(WaitableEventWatcherTest, OutlivesMessageLoop) {
+  // Simulate a MessageLoop that dies before an WaitableEventWatcher.  This
+  // ordinarily doesn't happen when people use the Thread class, but it can
+  // happen when people use the Singleton pattern or atexit.
+  WaitableEvent event(WaitableEvent::ResetPolicy::MANUAL,
+                      WaitableEvent::InitialState::NOT_SIGNALED);
+  {
+    std::unique_ptr<WaitableEventWatcher> watcher;
+    {
+      MessageLoop message_loop(GetParam());
+      watcher = std::make_unique<WaitableEventWatcher>();
+
+      watcher->StartWatching(&event, BindOnce(&QuitWhenSignaled),
+                             SequencedTaskRunnerHandle::Get());
+    }
+  }
+}
+
+TEST_P(WaitableEventWatcherTest, SignaledAtStartManual) {
+  MessageLoop message_loop(GetParam());
+
+  WaitableEvent event(WaitableEvent::ResetPolicy::MANUAL,
+                      WaitableEvent::InitialState::SIGNALED);
+
+  WaitableEventWatcher watcher;
+  watcher.StartWatching(&event, BindOnce(&QuitWhenSignaled),
+                        SequencedTaskRunnerHandle::Get());
+
+  RunLoop().Run();
+
+  EXPECT_TRUE(event.IsSignaled());
+}
+
+TEST_P(WaitableEventWatcherTest, SignaledAtStartAutomatic) {
+  MessageLoop message_loop(GetParam());
+
+  WaitableEvent event(WaitableEvent::ResetPolicy::AUTOMATIC,
+                      WaitableEvent::InitialState::SIGNALED);
+
+  WaitableEventWatcher watcher;
+  watcher.StartWatching(&event, BindOnce(&QuitWhenSignaled),
+                        SequencedTaskRunnerHandle::Get());
+
+  RunLoop().Run();
+
+  // The watcher consumes the event signal.
+  EXPECT_FALSE(event.IsSignaled());
+}
+
+TEST_P(WaitableEventWatcherTest, StartWatchingInCallback) {
+  MessageLoop message_loop(GetParam());
+
+  WaitableEvent event(WaitableEvent::ResetPolicy::MANUAL,
+                      WaitableEvent::InitialState::NOT_SIGNALED);
+
+  WaitableEventWatcher watcher;
+  watcher.StartWatching(
+      &event,
+      BindOnce(
+          [](WaitableEventWatcher* watcher, WaitableEvent* event) {
+            // |event| is manual, so the second watcher will run
+            // immediately.
+            watcher->StartWatching(event, BindOnce(&QuitWhenSignaled),
+                                   SequencedTaskRunnerHandle::Get());
+          },
+          &watcher),
+      SequencedTaskRunnerHandle::Get());
+
+  event.Signal();
+
+  RunLoop().Run();
+}
+
+TEST_P(WaitableEventWatcherTest, MultipleWatchersManual) {
+  MessageLoop message_loop(GetParam());
+
+  WaitableEvent event(WaitableEvent::ResetPolicy::MANUAL,
+                      WaitableEvent::InitialState::NOT_SIGNALED);
+
+  int counter1 = 0;
+  int counter2 = 0;
+
+  auto callback = [](RunLoop* run_loop, int* counter, WaitableEvent* event) {
+    ++(*counter);
+    run_loop->QuitWhenIdle();
+  };
+
+  RunLoop run_loop;
+
+  WaitableEventWatcher watcher1;
+  watcher1.StartWatching(
+      &event, BindOnce(callback, Unretained(&run_loop), Unretained(&counter1)),
+      SequencedTaskRunnerHandle::Get());
+
+  WaitableEventWatcher watcher2;
+  watcher2.StartWatching(
+      &event, BindOnce(callback, Unretained(&run_loop), Unretained(&counter2)),
+      SequencedTaskRunnerHandle::Get());
+
+  event.Signal();
+  run_loop.Run();
+
+  EXPECT_EQ(1, counter1);
+  EXPECT_EQ(1, counter2);
+  EXPECT_TRUE(event.IsSignaled());
+}
+
+// Tests that only one async waiter gets called back for an auto-reset event.
+TEST_P(WaitableEventWatcherTest, MultipleWatchersAutomatic) {
+  MessageLoop message_loop(GetParam());
+
+  WaitableEvent event(WaitableEvent::ResetPolicy::AUTOMATIC,
+                      WaitableEvent::InitialState::NOT_SIGNALED);
+
+  int counter1 = 0;
+  int counter2 = 0;
+
+  auto callback = [](RunLoop** run_loop, int* counter, WaitableEvent* event) {
+    ++(*counter);
+    (*run_loop)->QuitWhenIdle();
+  };
+
+  // The same RunLoop instance cannot be Run more than once, and it is
+  // undefined which watcher will get called back first. Have the callback
+  // dereference this pointer to quit the loop, which will be updated on each
+  // Run.
+  RunLoop* current_run_loop;
+
+  WaitableEventWatcher watcher1;
+  watcher1.StartWatching(
+      &event,
+      BindOnce(callback, Unretained(&current_run_loop), Unretained(&counter1)),
+      SequencedTaskRunnerHandle::Get());
+
+  WaitableEventWatcher watcher2;
+  watcher2.StartWatching(
+      &event,
+      BindOnce(callback, Unretained(&current_run_loop), Unretained(&counter2)),
+      SequencedTaskRunnerHandle::Get());
+
+  event.Signal();
+  {
+    RunLoop run_loop;
+    current_run_loop = &run_loop;
+    run_loop.Run();
+  }
+
+  // Only one of the waiters should have been signaled.
+  EXPECT_TRUE((counter1 == 1) ^ (counter2 == 1));
+
+  EXPECT_FALSE(event.IsSignaled());
+
+  event.Signal();
+  {
+    RunLoop run_loop;
+    current_run_loop = &run_loop;
+    run_loop.Run();
+  }
+
+  EXPECT_FALSE(event.IsSignaled());
+
+  // The other watcher should have been signaled.
+  EXPECT_EQ(1, counter1);
+  EXPECT_EQ(1, counter2);
+}
+
+// To help detect errors around deleting WaitableEventWatcher, an additional
+// bool parameter is used to test sleeping between watching and deletion.
+class WaitableEventWatcherDeletionTest
+    : public testing::TestWithParam<std::tuple<MessageLoop::Type, bool>> {};
+
+TEST_P(WaitableEventWatcherDeletionTest, DeleteUnder) {
+  MessageLoop::Type message_loop_type;
+  bool delay_after_delete;
+  std::tie(message_loop_type, delay_after_delete) = GetParam();
+
+  // Delete the WaitableEvent out from under the Watcher. This is explictly
+  // allowed by the interface.
+
+  MessageLoop message_loop(message_loop_type);
+
+  {
+    WaitableEventWatcher watcher;
+
+    auto* event = new WaitableEvent(WaitableEvent::ResetPolicy::AUTOMATIC,
+                                    WaitableEvent::InitialState::NOT_SIGNALED);
+
+    watcher.StartWatching(event, BindOnce(&QuitWhenSignaled),
+                          SequencedTaskRunnerHandle::Get());
+
+    if (delay_after_delete) {
+      // On Windows that sleep() improves the chance to catch some problems.
+      // It postpones the dtor |watcher| (which immediately cancel the waiting)
+      // and gives some time to run to a created background thread.
+      // Unfortunately, that thread is under OS control and we can't
+      // manipulate it directly.
+      PlatformThread::Sleep(TimeDelta::FromMilliseconds(30));
+    }
+
+    delete event;
+  }
+}
+
+TEST_P(WaitableEventWatcherDeletionTest, SignalAndDelete) {
+  MessageLoop::Type message_loop_type;
+  bool delay_after_delete;
+  std::tie(message_loop_type, delay_after_delete) = GetParam();
+
+  // Signal and immediately delete the WaitableEvent out from under the Watcher.
+
+  MessageLoop message_loop(message_loop_type);
+
+  {
+    WaitableEventWatcher watcher;
+
+    auto event = std::make_unique<WaitableEvent>(
+        WaitableEvent::ResetPolicy::AUTOMATIC,
+        WaitableEvent::InitialState::NOT_SIGNALED);
+
+    watcher.StartWatching(event.get(), BindOnce(&QuitWhenSignaled),
+                          SequencedTaskRunnerHandle::Get());
+    event->Signal();
+    event.reset();
+
+    if (delay_after_delete) {
+      // On Windows that sleep() improves the chance to catch some problems.
+      // It postpones the dtor |watcher| (which immediately cancel the waiting)
+      // and gives some time to run to a created background thread.
+      // Unfortunately, that thread is under OS control and we can't
+      // manipulate it directly.
+      PlatformThread::Sleep(TimeDelta::FromMilliseconds(30));
+    }
+
+    // Wait for the watcher callback.
+    RunLoop().Run();
+  }
+}
+
+// Tests deleting the WaitableEventWatcher between signaling the event and
+// when the callback should be run.
+TEST_P(WaitableEventWatcherDeletionTest, DeleteWatcherBeforeCallback) {
+  MessageLoop::Type message_loop_type;
+  bool delay_after_delete;
+  std::tie(message_loop_type, delay_after_delete) = GetParam();
+
+  MessageLoop message_loop(message_loop_type);
+  scoped_refptr<SingleThreadTaskRunner> task_runner =
+      message_loop.task_runner();
+
+  // Flag used to esnure that the |watcher_callback| never runs.
+  bool did_callback = false;
+
+  WaitableEvent event(WaitableEvent::ResetPolicy::AUTOMATIC,
+                      WaitableEvent::InitialState::NOT_SIGNALED);
+  auto watcher = std::make_unique<WaitableEventWatcher>();
+
+  // Queue up a series of tasks:
+  // 1. StartWatching the WaitableEvent
+  // 2. Signal the event (which will result in another task getting posted to
+  //    the |task_runner|)
+  // 3. Delete the WaitableEventWatcher
+  // 4. WaitableEventWatcher callback should run (from #2)
+
+  WaitableEventWatcher::EventCallback watcher_callback = BindOnce(
+      [](bool* did_callback, WaitableEvent*) {
+        *did_callback = true;
+      },
+      Unretained(&did_callback));
+
+  task_runner->PostTask(
+      FROM_HERE, BindOnce(IgnoreResult(&WaitableEventWatcher::StartWatching),
+                          Unretained(watcher.get()), Unretained(&event),
+                          std::move(watcher_callback), task_runner));
+  task_runner->PostTask(FROM_HERE,
+                        BindOnce(&WaitableEvent::Signal, Unretained(&event)));
+  task_runner->DeleteSoon(FROM_HERE, std::move(watcher));
+  if (delay_after_delete) {
+    task_runner->PostTask(FROM_HERE, BindOnce(&PlatformThread::Sleep,
+                                              TimeDelta::FromMilliseconds(30)));
+  }
+
+  RunLoop().RunUntilIdle();
+
+  EXPECT_FALSE(did_callback);
+}
+
+INSTANTIATE_TEST_CASE_P(,
+                        WaitableEventWatcherTest,
+                        testing::ValuesIn(testing_message_loops));
+
+INSTANTIATE_TEST_CASE_P(
+    ,
+    WaitableEventWatcherDeletionTest,
+    testing::Combine(testing::ValuesIn(testing_message_loops),
+                     testing::Bool()));
+
+}  // namespace base
diff --git a/base/sys_byteorder_unittest.cc b/base/sys_byteorder_unittest.cc
new file mode 100644
index 0000000..8167be3
--- /dev/null
+++ b/base/sys_byteorder_unittest.cc
@@ -0,0 +1,142 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/sys_byteorder.h"
+
+#include <stdint.h>
+
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+const uint16_t k16BitTestData = 0xaabb;
+const uint16_t k16BitSwappedTestData = 0xbbaa;
+const uint32_t k32BitTestData = 0xaabbccdd;
+const uint32_t k32BitSwappedTestData = 0xddccbbaa;
+const uint64_t k64BitTestData = 0xaabbccdd44332211;
+const uint64_t k64BitSwappedTestData = 0x11223344ddccbbaa;
+
+}  // namespace
+
+TEST(ByteOrderTest, ByteSwap16) {
+  uint16_t swapped = base::ByteSwap(k16BitTestData);
+  EXPECT_EQ(k16BitSwappedTestData, swapped);
+  uint16_t reswapped = base::ByteSwap(swapped);
+  EXPECT_EQ(k16BitTestData, reswapped);
+}
+
+TEST(ByteOrderTest, ByteSwap32) {
+  uint32_t swapped = base::ByteSwap(k32BitTestData);
+  EXPECT_EQ(k32BitSwappedTestData, swapped);
+  uint32_t reswapped = base::ByteSwap(swapped);
+  EXPECT_EQ(k32BitTestData, reswapped);
+}
+
+TEST(ByteOrderTest, ByteSwap64) {
+  uint64_t swapped = base::ByteSwap(k64BitTestData);
+  EXPECT_EQ(k64BitSwappedTestData, swapped);
+  uint64_t reswapped = base::ByteSwap(swapped);
+  EXPECT_EQ(k64BitTestData, reswapped);
+}
+
+TEST(ByteOrderTest, ByteSwapUintPtrT) {
+#if defined(ARCH_CPU_64_BITS)
+  const uintptr_t test_data = static_cast<uintptr_t>(k64BitTestData);
+  const uintptr_t swapped_test_data =
+      static_cast<uintptr_t>(k64BitSwappedTestData);
+#elif defined(ARCH_CPU_32_BITS)
+  const uintptr_t test_data = static_cast<uintptr_t>(k32BitTestData);
+  const uintptr_t swapped_test_data =
+      static_cast<uintptr_t>(k32BitSwappedTestData);
+#else
+#error architecture not supported
+#endif
+
+  uintptr_t swapped = base::ByteSwapUintPtrT(test_data);
+  EXPECT_EQ(swapped_test_data, swapped);
+  uintptr_t reswapped = base::ByteSwapUintPtrT(swapped);
+  EXPECT_EQ(test_data, reswapped);
+}
+
+TEST(ByteOrderTest, ByteSwapToLE16) {
+  uint16_t le = base::ByteSwapToLE16(k16BitTestData);
+#if defined(ARCH_CPU_LITTLE_ENDIAN)
+  EXPECT_EQ(k16BitTestData, le);
+#else
+  EXPECT_EQ(k16BitSwappedTestData, le);
+#endif
+}
+
+TEST(ByteOrderTest, ByteSwapToLE32) {
+  uint32_t le = base::ByteSwapToLE32(k32BitTestData);
+#if defined(ARCH_CPU_LITTLE_ENDIAN)
+  EXPECT_EQ(k32BitTestData, le);
+#else
+  EXPECT_EQ(k32BitSwappedTestData, le);
+#endif
+}
+
+TEST(ByteOrderTest, ByteSwapToLE64) {
+  uint64_t le = base::ByteSwapToLE64(k64BitTestData);
+#if defined(ARCH_CPU_LITTLE_ENDIAN)
+  EXPECT_EQ(k64BitTestData, le);
+#else
+  EXPECT_EQ(k64BitSwappedTestData, le);
+#endif
+}
+
+TEST(ByteOrderTest, NetToHost16) {
+  uint16_t h = base::NetToHost16(k16BitTestData);
+#if defined(ARCH_CPU_LITTLE_ENDIAN)
+  EXPECT_EQ(k16BitSwappedTestData, h);
+#else
+  EXPECT_EQ(k16BitTestData, h);
+#endif
+}
+
+TEST(ByteOrderTest, NetToHost32) {
+  uint32_t h = base::NetToHost32(k32BitTestData);
+#if defined(ARCH_CPU_LITTLE_ENDIAN)
+  EXPECT_EQ(k32BitSwappedTestData, h);
+#else
+  EXPECT_EQ(k32BitTestData, h);
+#endif
+}
+
+TEST(ByteOrderTest, NetToHost64) {
+  uint64_t h = base::NetToHost64(k64BitTestData);
+#if defined(ARCH_CPU_LITTLE_ENDIAN)
+  EXPECT_EQ(k64BitSwappedTestData, h);
+#else
+  EXPECT_EQ(k64BitTestData, h);
+#endif
+}
+
+TEST(ByteOrderTest, HostToNet16) {
+  uint16_t n = base::HostToNet16(k16BitTestData);
+#if defined(ARCH_CPU_LITTLE_ENDIAN)
+  EXPECT_EQ(k16BitSwappedTestData, n);
+#else
+  EXPECT_EQ(k16BitTestData, n);
+#endif
+}
+
+TEST(ByteOrderTest, HostToNet32) {
+  uint32_t n = base::HostToNet32(k32BitTestData);
+#if defined(ARCH_CPU_LITTLE_ENDIAN)
+  EXPECT_EQ(k32BitSwappedTestData, n);
+#else
+  EXPECT_EQ(k32BitTestData, n);
+#endif
+}
+
+TEST(ByteOrderTest, HostToNet64) {
+  uint64_t n = base::HostToNet64(k64BitTestData);
+#if defined(ARCH_CPU_LITTLE_ENDIAN)
+  EXPECT_EQ(k64BitSwappedTestData, n);
+#else
+  EXPECT_EQ(k64BitTestData, n);
+#endif
+}
diff --git a/base/sys_info_android.cc b/base/sys_info_android.cc
new file mode 100644
index 0000000..7704796
--- /dev/null
+++ b/base/sys_info_android.cc
@@ -0,0 +1,241 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/sys_info.h"
+
+#include <dlfcn.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/system_properties.h>
+
+#include "base/android/jni_android.h"
+#include "base/android/sys_utils.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/stringprintf.h"
+#include "base/sys_info_internal.h"
+
+#if (__ANDROID_API__ >= 21 /* 5.0 - Lollipop */)
+
+namespace {
+
+typedef int (SystemPropertyGetFunction)(const char*, char*);
+
+SystemPropertyGetFunction* DynamicallyLoadRealSystemPropertyGet() {
+  // libc.so should already be open, get a handle to it.
+  void* handle = dlopen("libc.so", RTLD_NOLOAD);
+  if (!handle) {
+    LOG(FATAL) << "Cannot dlopen libc.so: " << dlerror();
+  }
+  SystemPropertyGetFunction* real_system_property_get =
+      reinterpret_cast<SystemPropertyGetFunction*>(
+          dlsym(handle, "__system_property_get"));
+  if (!real_system_property_get) {
+    LOG(FATAL) << "Cannot resolve __system_property_get(): " << dlerror();
+  }
+  return real_system_property_get;
+}
+
+static base::LazyInstance<base::internal::LazySysInfoValue<
+    SystemPropertyGetFunction*, DynamicallyLoadRealSystemPropertyGet> >::Leaky
+    g_lazy_real_system_property_get = LAZY_INSTANCE_INITIALIZER;
+
+}  // namespace
+
+// Android 'L' removes __system_property_get from the NDK, however it is still
+// a hidden symbol in libc. Until we remove all calls of __system_property_get
+// from Chrome we work around this by defining a weak stub here, which uses
+// dlsym to but ensures that Chrome uses the real system
+// implementatation when loaded.  http://crbug.com/392191.
+BASE_EXPORT int __system_property_get(const char* name, char* value) {
+  return g_lazy_real_system_property_get.Get().value()(name, value);
+}
+
+#endif
+
+namespace {
+
+// Default version of Android to fall back to when actual version numbers
+// cannot be acquired. Use the latest Android release with a higher bug fix
+// version to avoid unnecessarily comparison errors with the latest release.
+// This should be manually kept up to date on each Android release.
+const int kDefaultAndroidMajorVersion = 8;
+const int kDefaultAndroidMinorVersion = 1;
+const int kDefaultAndroidBugfixVersion = 99;
+
+// Get and parse out the OS version numbers from the system properties.
+// Note if parse fails, the "default" version is returned as fallback.
+void GetOsVersionStringAndNumbers(std::string* version_string,
+                                  int32_t* major_version,
+                                  int32_t* minor_version,
+                                  int32_t* bugfix_version) {
+  // Read the version number string out from the properties.
+  char os_version_str[PROP_VALUE_MAX];
+  __system_property_get("ro.build.version.release", os_version_str);
+
+  if (os_version_str[0]) {
+    // Try to parse out the version numbers from the string.
+    int num_read = sscanf(os_version_str, "%d.%d.%d", major_version,
+                          minor_version, bugfix_version);
+
+    if (num_read > 0) {
+      // If we don't have a full set of version numbers, make the extras 0.
+      if (num_read < 2)
+        *minor_version = 0;
+      if (num_read < 3)
+        *bugfix_version = 0;
+      *version_string = std::string(os_version_str);
+      return;
+    }
+  }
+
+  // For some reason, we couldn't parse the version number string.
+  *major_version = kDefaultAndroidMajorVersion;
+  *minor_version = kDefaultAndroidMinorVersion;
+  *bugfix_version = kDefaultAndroidBugfixVersion;
+  *version_string = ::base::StringPrintf("%d.%d.%d", *major_version,
+                                         *minor_version, *bugfix_version);
+}
+
+// Parses a system property (specified with unit 'k','m' or 'g').
+// Returns a value in bytes.
+// Returns -1 if the string could not be parsed.
+int64_t ParseSystemPropertyBytes(const base::StringPiece& str) {
+  const int64_t KB = 1024;
+  const int64_t MB = 1024 * KB;
+  const int64_t GB = 1024 * MB;
+  if (str.size() == 0u)
+    return -1;
+  int64_t unit_multiplier = 1;
+  size_t length = str.size();
+  if (str[length - 1] == 'k') {
+    unit_multiplier = KB;
+    length--;
+  } else if (str[length - 1] == 'm') {
+    unit_multiplier = MB;
+    length--;
+  } else if (str[length - 1] == 'g') {
+    unit_multiplier = GB;
+    length--;
+  }
+  int64_t result = 0;
+  bool parsed = base::StringToInt64(str.substr(0, length), &result);
+  bool negative = result <= 0;
+  bool overflow =
+      result >= std::numeric_limits<int64_t>::max() / unit_multiplier;
+  if (!parsed || negative || overflow)
+    return -1;
+  return result * unit_multiplier;
+}
+
+int GetDalvikHeapSizeMB() {
+  char heap_size_str[PROP_VALUE_MAX];
+  __system_property_get("dalvik.vm.heapsize", heap_size_str);
+  // dalvik.vm.heapsize property is writable by a root user.
+  // Clamp it to reasonable range as a sanity check,
+  // a typical android device will never have less than 48MB.
+  const int64_t MB = 1024 * 1024;
+  int64_t result = ParseSystemPropertyBytes(heap_size_str);
+  if (result == -1) {
+     // We should consider not exposing these values if they are not reliable.
+     LOG(ERROR) << "Can't parse dalvik.vm.heapsize: " << heap_size_str;
+     result = base::SysInfo::AmountOfPhysicalMemoryMB() / 3;
+  }
+  result =
+      std::min<int64_t>(std::max<int64_t>(32 * MB, result), 1024 * MB) / MB;
+  return static_cast<int>(result);
+}
+
+int GetDalvikHeapGrowthLimitMB() {
+  char heap_size_str[PROP_VALUE_MAX];
+  __system_property_get("dalvik.vm.heapgrowthlimit", heap_size_str);
+  // dalvik.vm.heapgrowthlimit property is writable by a root user.
+  // Clamp it to reasonable range as a sanity check,
+  // a typical android device will never have less than 24MB.
+  const int64_t MB = 1024 * 1024;
+  int64_t result = ParseSystemPropertyBytes(heap_size_str);
+  if (result == -1) {
+     // We should consider not exposing these values if they are not reliable.
+     LOG(ERROR) << "Can't parse dalvik.vm.heapgrowthlimit: " << heap_size_str;
+     result = base::SysInfo::AmountOfPhysicalMemoryMB() / 6;
+  }
+  result = std::min<int64_t>(std::max<int64_t>(16 * MB, result), 512 * MB) / MB;
+  return static_cast<int>(result);
+}
+
+}  // anonymous namespace
+
+namespace base {
+
+std::string SysInfo::HardwareModelName() {
+  char device_model_str[PROP_VALUE_MAX];
+  __system_property_get("ro.product.model", device_model_str);
+  return std::string(device_model_str);
+}
+
+std::string SysInfo::OperatingSystemName() {
+  return "Android";
+}
+
+std::string SysInfo::OperatingSystemVersion() {
+  std::string version_string;
+  int32_t major, minor, bugfix;
+  GetOsVersionStringAndNumbers(&version_string, &major, &minor, &bugfix);
+  return version_string;
+}
+
+void SysInfo::OperatingSystemVersionNumbers(int32_t* major_version,
+                                            int32_t* minor_version,
+                                            int32_t* bugfix_version) {
+  std::string version_string;
+  GetOsVersionStringAndNumbers(&version_string, major_version, minor_version,
+                               bugfix_version);
+}
+
+std::string SysInfo::GetAndroidBuildCodename() {
+  char os_version_codename_str[PROP_VALUE_MAX];
+  __system_property_get("ro.build.version.codename", os_version_codename_str);
+  return std::string(os_version_codename_str);
+}
+
+std::string SysInfo::GetAndroidBuildID() {
+  char os_build_id_str[PROP_VALUE_MAX];
+  __system_property_get("ro.build.id", os_build_id_str);
+  return std::string(os_build_id_str);
+}
+
+int SysInfo::DalvikHeapSizeMB() {
+  static int heap_size = GetDalvikHeapSizeMB();
+  return heap_size;
+}
+
+int SysInfo::DalvikHeapGrowthLimitMB() {
+  static int heap_growth_limit = GetDalvikHeapGrowthLimitMB();
+  return heap_growth_limit;
+}
+
+static base::LazyInstance<
+    base::internal::LazySysInfoValue<bool,
+        android::SysUtils::IsLowEndDeviceFromJni> >::Leaky
+    g_lazy_low_end_device = LAZY_INSTANCE_INITIALIZER;
+
+bool SysInfo::IsLowEndDeviceImpl() {
+  // This code might be used in some environments
+  // which might not have a Java environment.
+  // Note that we need to call the Java version here.
+  // There exists a complete native implementation in
+  // sys_info.cc but calling that here would mean that
+  // the Java code and the native code would call different
+  // implementations which could give different results.
+  // Also the Java code cannot depend on the native code
+  // since it might not be loaded yet.
+  if (!base::android::IsVMInitialized())
+    return false;
+  return g_lazy_low_end_device.Get().value();
+}
+
+
+}  // namespace base
diff --git a/base/syslog_logging.cc b/base/syslog_logging.cc
new file mode 100644
index 0000000..53bc1aa
--- /dev/null
+++ b/base/syslog_logging.cc
@@ -0,0 +1,119 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/syslog_logging.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/debug/stack_trace.h"
+#elif defined(OS_LINUX)
+// <syslog.h> defines LOG_INFO, LOG_WARNING macros that could conflict with
+// base::LOG_INFO, base::LOG_WARNING.
+#include <syslog.h>
+#undef LOG_INFO
+#undef LOG_WARNING
+#endif
+
+#include <ostream>
+#include <string>
+
+namespace logging {
+
+#if defined(OS_WIN)
+
+namespace {
+
+std::string* g_event_source_name = nullptr;
+uint16_t g_category = 0;
+uint32_t g_event_id = 0;
+
+}  // namespace
+
+void SetEventSource(const std::string& name,
+                    uint16_t category,
+                    uint32_t event_id) {
+  DCHECK_EQ(nullptr, g_event_source_name);
+  g_event_source_name = new std::string(name);
+  g_category = category;
+  g_event_id = event_id;
+}
+
+#endif  // defined(OS_WIN)
+
+EventLogMessage::EventLogMessage(const char* file,
+                                 int line,
+                                 LogSeverity severity)
+    : log_message_(file, line, severity) {
+}
+
+EventLogMessage::~EventLogMessage() {
+#if defined(OS_WIN)
+  // If g_event_source_name is nullptr (which it is per default) SYSLOG will
+  // degrade gracefully to regular LOG. If you see this happening most probably
+  // you are using SYSLOG before you called SetEventSourceName.
+  if (g_event_source_name == nullptr)
+    return;
+
+  HANDLE event_log_handle =
+      RegisterEventSourceA(nullptr, g_event_source_name->c_str());
+  if (event_log_handle == nullptr) {
+    stream() << " !!NOT ADDED TO EVENTLOG!!";
+    return;
+  }
+
+  base::ScopedClosureRunner auto_deregister(
+      base::Bind(base::IgnoreResult(&DeregisterEventSource), event_log_handle));
+  std::string message(log_message_.str());
+  WORD log_type = EVENTLOG_ERROR_TYPE;
+  switch (log_message_.severity()) {
+    case LOG_INFO:
+      log_type = EVENTLOG_INFORMATION_TYPE;
+      break;
+    case LOG_WARNING:
+      log_type = EVENTLOG_WARNING_TYPE;
+      break;
+    case LOG_ERROR:
+    case LOG_FATAL:
+      // The price of getting the stack trace is not worth the hassle for
+      // non-error conditions.
+      base::debug::StackTrace trace;
+      message.append(trace.ToString());
+      log_type = EVENTLOG_ERROR_TYPE;
+      break;
+  }
+  LPCSTR strings[1] = {message.data()};
+  if (!ReportEventA(event_log_handle, log_type, g_category, g_event_id, nullptr,
+                    1, 0, strings, nullptr)) {
+    stream() << " !!NOT ADDED TO EVENTLOG!!";
+  }
+#elif defined(OS_LINUX)
+  const char kEventSource[] = "chrome";
+  openlog(kEventSource, LOG_NOWAIT | LOG_PID, LOG_USER);
+  // We can't use the defined names for the logging severity from syslog.h
+  // because they collide with the names of our own severity levels. Therefore
+  // we use the actual values which of course do not match ours.
+  // See sys/syslog.h for reference.
+  int priority = 3;
+  switch (log_message_.severity()) {
+    case LOG_INFO:
+      priority = 6;
+      break;
+    case LOG_WARNING:
+      priority = 4;
+      break;
+    case LOG_ERROR:
+      priority = 3;
+      break;
+    case LOG_FATAL:
+      priority = 2;
+      break;
+  }
+  syslog(priority, "%s", log_message_.str().c_str());
+  closelog();
+#endif  // defined(OS_WIN)
+}
+
+}  // namespace logging
diff --git a/base/syslog_logging.h b/base/syslog_logging.h
new file mode 100644
index 0000000..736a5b2
--- /dev/null
+++ b/base/syslog_logging.h
@@ -0,0 +1,50 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_SYSLOG_LOGGING_H_
+#define BASE_SYSLOG_LOGGING_H_
+
+#include <iosfwd>
+
+#include "base/logging.h"
+#include "build/build_config.h"
+
+namespace logging {
+
+// Keep in mind that the syslog is always active regardless of the logging level
+// and applied flags. Use only for important information that a system
+// administrator might need to maintain the browser installation.
+#define SYSLOG_STREAM(severity) \
+  COMPACT_GOOGLE_LOG_EX_ ## severity(EventLogMessage).stream()
+#define SYSLOG(severity) \
+  SYSLOG_STREAM(severity)
+
+#if defined(OS_WIN)
+// Sets the name, category and event id of the event source for logging to the
+// Windows Event Log. Call this function once before using the SYSLOG macro or
+// otherwise it will behave as a regular LOG macro.
+void BASE_EXPORT SetEventSource(const std::string& name,
+                                uint16_t category,
+                                uint32_t event_id);
+#endif  // defined(OS_WIN)
+
+// Creates a formatted message on the system event log. That would be the
+// Application Event log on Windows and the messages log file on POSIX systems.
+class BASE_EXPORT EventLogMessage {
+ public:
+  EventLogMessage(const char* file, int line, LogSeverity severity);
+
+  ~EventLogMessage();
+
+  std::ostream& stream() { return log_message_.stream(); }
+
+ private:
+  LogMessage log_message_;
+
+  DISALLOW_COPY_AND_ASSIGN(EventLogMessage);
+};
+
+}  // namespace logging
+
+#endif  // BASE_SYSLOG_LOGGING_H_
diff --git a/base/system_monitor/system_monitor.cc b/base/system_monitor/system_monitor.cc
new file mode 100644
index 0000000..71e4f07
--- /dev/null
+++ b/base/system_monitor/system_monitor.cc
@@ -0,0 +1,51 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/system_monitor/system_monitor.h"
+
+#include <utility>
+
+#include "base/logging.h"
+#include "base/time/time.h"
+
+namespace base {
+
+static SystemMonitor* g_system_monitor = nullptr;
+
+SystemMonitor::SystemMonitor()
+    :  devices_changed_observer_list_(
+          new ObserverListThreadSafe<DevicesChangedObserver>()) {
+  DCHECK(!g_system_monitor);
+  g_system_monitor = this;
+}
+
+SystemMonitor::~SystemMonitor() {
+  DCHECK_EQ(this, g_system_monitor);
+  g_system_monitor = nullptr;
+}
+
+// static
+SystemMonitor* SystemMonitor::Get() {
+  return g_system_monitor;
+}
+
+void SystemMonitor::ProcessDevicesChanged(DeviceType device_type) {
+  NotifyDevicesChanged(device_type);
+}
+
+void SystemMonitor::AddDevicesChangedObserver(DevicesChangedObserver* obs) {
+  devices_changed_observer_list_->AddObserver(obs);
+}
+
+void SystemMonitor::RemoveDevicesChangedObserver(DevicesChangedObserver* obs) {
+  devices_changed_observer_list_->RemoveObserver(obs);
+}
+
+void SystemMonitor::NotifyDevicesChanged(DeviceType device_type) {
+  DVLOG(1) << "DevicesChanged with device type " << device_type;
+  devices_changed_observer_list_->Notify(
+      FROM_HERE, &DevicesChangedObserver::OnDevicesChanged, device_type);
+}
+
+}  // namespace base
diff --git a/base/system_monitor/system_monitor.h b/base/system_monitor/system_monitor.h
new file mode 100644
index 0000000..7f21e47
--- /dev/null
+++ b/base/system_monitor/system_monitor.h
@@ -0,0 +1,75 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_SYSTEM_MONITOR_SYSTEM_MONITOR_H_
+#define BASE_SYSTEM_MONITOR_SYSTEM_MONITOR_H_
+
+#include "base/base_export.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/observer_list_threadsafe.h"
+#include "build/build_config.h"
+
+namespace base {
+
+// Class for monitoring various system-related subsystems
+// such as power management, network status, etc.
+// TODO(mbelshe):  Add support beyond just power management.
+class BASE_EXPORT SystemMonitor {
+ public:
+  // Type of devices whose change need to be monitored, such as add/remove.
+  enum DeviceType {
+    DEVTYPE_AUDIO,          // Audio device, e.g., microphone.
+    DEVTYPE_VIDEO_CAPTURE,  // Video capture device, e.g., webcam.
+    DEVTYPE_UNKNOWN,        // Other devices.
+  };
+
+  // Create SystemMonitor. Only one SystemMonitor instance per application
+  // is allowed.
+  SystemMonitor();
+  ~SystemMonitor();
+
+  // Get the application-wide SystemMonitor (if not present, returns NULL).
+  static SystemMonitor* Get();
+
+  class BASE_EXPORT DevicesChangedObserver {
+   public:
+    // Notification that the devices connected to the system have changed.
+    // This is only implemented on Windows currently.
+    virtual void OnDevicesChanged(DeviceType device_type) {}
+
+   protected:
+    virtual ~DevicesChangedObserver() = default;
+  };
+
+  // Add a new observer.
+  // Can be called from any thread.
+  // Must not be called from within a notification callback.
+  void AddDevicesChangedObserver(DevicesChangedObserver* obs);
+
+  // Remove an existing observer.
+  // Can be called from any thread.
+  // Must not be called from within a notification callback.
+  void RemoveDevicesChangedObserver(DevicesChangedObserver* obs);
+
+  // The ProcessFoo() style methods are a broken pattern and should not
+  // be copied. Any significant addition to this class is blocked on
+  // refactoring to improve the state of affairs. See http://crbug.com/149059
+
+  // Cross-platform handling of a device change event.
+  void ProcessDevicesChanged(DeviceType device_type);
+
+ private:
+  // Functions to trigger notifications.
+  void NotifyDevicesChanged(DeviceType device_type);
+
+  scoped_refptr<ObserverListThreadSafe<DevicesChangedObserver> >
+      devices_changed_observer_list_;
+
+  DISALLOW_COPY_AND_ASSIGN(SystemMonitor);
+};
+
+}  // namespace base
+
+#endif  // BASE_SYSTEM_MONITOR_SYSTEM_MONITOR_H_
diff --git a/base/system_monitor/system_monitor_unittest.cc b/base/system_monitor/system_monitor_unittest.cc
new file mode 100644
index 0000000..8963f7b
--- /dev/null
+++ b/base/system_monitor/system_monitor_unittest.cc
@@ -0,0 +1,55 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/system_monitor/system_monitor.h"
+
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/test/mock_devices_changed_observer.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+namespace {
+
+class SystemMonitorTest : public testing::Test {
+ protected:
+  SystemMonitorTest() {
+    system_monitor_.reset(new SystemMonitor);
+  }
+
+  MessageLoop message_loop_;
+  std::unique_ptr<SystemMonitor> system_monitor_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SystemMonitorTest);
+};
+
+TEST_F(SystemMonitorTest, DeviceChangeNotifications) {
+  const int kObservers = 5;
+
+  testing::Sequence mock_sequencer[kObservers];
+  MockDevicesChangedObserver observers[kObservers];
+  for (int index = 0; index < kObservers; ++index) {
+    system_monitor_->AddDevicesChangedObserver(&observers[index]);
+
+    EXPECT_CALL(observers[index],
+                OnDevicesChanged(SystemMonitor::DEVTYPE_UNKNOWN))
+        .Times(3)
+        .InSequence(mock_sequencer[index]);
+  }
+
+  system_monitor_->ProcessDevicesChanged(SystemMonitor::DEVTYPE_UNKNOWN);
+  RunLoop().RunUntilIdle();
+
+  system_monitor_->ProcessDevicesChanged(SystemMonitor::DEVTYPE_UNKNOWN);
+  system_monitor_->ProcessDevicesChanged(SystemMonitor::DEVTYPE_UNKNOWN);
+  RunLoop().RunUntilIdle();
+}
+
+}  // namespace
+
+}  // namespace base
diff --git a/base/task_scheduler/delayed_task_manager_unittest.cc b/base/task_scheduler/delayed_task_manager_unittest.cc
new file mode 100644
index 0000000..67c797a
--- /dev/null
+++ b/base/task_scheduler/delayed_task_manager_unittest.cc
@@ -0,0 +1,209 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/task_scheduler/delayed_task_manager.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/task_scheduler/task.h"
+#include "base/test/bind_test_util.h"
+#include "base/test/test_mock_time_task_runner.h"
+#include "base/threading/thread.h"
+#include "base/time/time.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace internal {
+namespace {
+
+constexpr TimeDelta kLongDelay = TimeDelta::FromHours(1);
+
+class MockTask {
+ public:
+  MOCK_METHOD0(Run, void());
+};
+
+void RunTask(Task task) {
+  std::move(task.task).Run();
+}
+
+class TaskSchedulerDelayedTaskManagerTest : public testing::Test {
+ protected:
+  TaskSchedulerDelayedTaskManagerTest()
+      : delayed_task_manager_(
+            service_thread_task_runner_->DeprecatedGetMockTickClock()),
+        task_(FROM_HERE,
+              BindOnce(&MockTask::Run, Unretained(&mock_task_)),
+              TaskTraits(),
+              kLongDelay) {
+    // The constructor of Task computes |delayed_run_time| by adding |delay| to
+    // the real time. Recompute it by adding |delay| to the mock time.
+    task_.delayed_run_time =
+        service_thread_task_runner_->GetMockTickClock()->NowTicks() +
+        kLongDelay;
+  }
+  ~TaskSchedulerDelayedTaskManagerTest() override = default;
+
+  const scoped_refptr<TestMockTimeTaskRunner> service_thread_task_runner_ =
+      MakeRefCounted<TestMockTimeTaskRunner>();
+  DelayedTaskManager delayed_task_manager_;
+  testing::StrictMock<MockTask> mock_task_;
+  Task task_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TaskSchedulerDelayedTaskManagerTest);
+};
+
+}  // namespace
+
+// Verify that a delayed task isn't forwarded before Start().
+TEST_F(TaskSchedulerDelayedTaskManagerTest, DelayedTaskDoesNotRunBeforeStart) {
+  // Send |task| to the DelayedTaskManager.
+  delayed_task_manager_.AddDelayedTask(std::move(task_), BindOnce(&RunTask));
+
+  // Fast-forward time until the task is ripe for execution. Since Start() has
+  // not been called, the task should not be forwarded to RunTask() (MockTask is
+  // a StrictMock without expectations so test will fail if RunTask() runs it).
+  service_thread_task_runner_->FastForwardBy(kLongDelay);
+}
+
+// Verify that a delayed task added before Start() and whose delay expires after
+// Start() is forwarded when its delay expires.
+TEST_F(TaskSchedulerDelayedTaskManagerTest,
+       DelayedTaskPostedBeforeStartExpiresAfterStartRunsOnExpire) {
+  // Send |task| to the DelayedTaskManager.
+  delayed_task_manager_.AddDelayedTask(std::move(task_), BindOnce(&RunTask));
+
+  delayed_task_manager_.Start(service_thread_task_runner_);
+
+  // Run tasks on the service thread. Don't expect any forwarding to
+  // |task_target_| since the task isn't ripe for execution.
+  service_thread_task_runner_->RunUntilIdle();
+
+  // Fast-forward time until the task is ripe for execution. Expect the task to
+  // be forwarded to RunTask().
+  EXPECT_CALL(mock_task_, Run());
+  service_thread_task_runner_->FastForwardBy(kLongDelay);
+}
+
+// Verify that a delayed task added before Start() and whose delay expires
+// before Start() is forwarded when Start() is called.
+TEST_F(TaskSchedulerDelayedTaskManagerTest,
+       DelayedTaskPostedBeforeStartExpiresBeforeStartRunsOnStart) {
+  // Send |task| to the DelayedTaskManager.
+  delayed_task_manager_.AddDelayedTask(std::move(task_), BindOnce(&RunTask));
+
+  // Run tasks on the service thread. Don't expect any forwarding to
+  // |task_target_| since the task isn't ripe for execution.
+  service_thread_task_runner_->RunUntilIdle();
+
+  // Fast-forward time until the task is ripe for execution. Don't expect the
+  // task to be forwarded since Start() hasn't been called yet.
+  service_thread_task_runner_->FastForwardBy(kLongDelay);
+
+  // Start the DelayedTaskManager. Expect the task to be forwarded to RunTask().
+  EXPECT_CALL(mock_task_, Run());
+  delayed_task_manager_.Start(service_thread_task_runner_);
+  service_thread_task_runner_->RunUntilIdle();
+}
+
+// Verify that a delayed task added after Start() isn't forwarded before it is
+// ripe for execution.
+TEST_F(TaskSchedulerDelayedTaskManagerTest, DelayedTaskDoesNotRunTooEarly) {
+  delayed_task_manager_.Start(service_thread_task_runner_);
+
+  // Send |task| to the DelayedTaskManager.
+  delayed_task_manager_.AddDelayedTask(std::move(task_), BindOnce(&RunTask));
+
+  // Run tasks that are ripe for execution. Don't expect any forwarding to
+  // RunTask().
+  service_thread_task_runner_->RunUntilIdle();
+}
+
+// Verify that a delayed task added after Start() is forwarded when it is ripe
+// for execution.
+TEST_F(TaskSchedulerDelayedTaskManagerTest, DelayedTaskRunsAfterDelay) {
+  delayed_task_manager_.Start(service_thread_task_runner_);
+
+  // Send |task| to the DelayedTaskManager.
+  delayed_task_manager_.AddDelayedTask(std::move(task_), BindOnce(&RunTask));
+
+  // Fast-forward time. Expect the task to be forwarded to RunTask().
+  EXPECT_CALL(mock_task_, Run());
+  service_thread_task_runner_->FastForwardBy(kLongDelay);
+}
+
+// Verify that multiple delayed tasks added after Start() are forwarded when
+// they are ripe for execution.
+TEST_F(TaskSchedulerDelayedTaskManagerTest, DelayedTasksRunAfterDelay) {
+  delayed_task_manager_.Start(service_thread_task_runner_);
+
+  testing::StrictMock<MockTask> mock_task_a;
+  Task task_a(FROM_HERE, BindOnce(&MockTask::Run, Unretained(&mock_task_a)),
+              TaskTraits(), TimeDelta::FromHours(1));
+
+  testing::StrictMock<MockTask> mock_task_b;
+  Task task_b(FROM_HERE, BindOnce(&MockTask::Run, Unretained(&mock_task_b)),
+              TaskTraits(), TimeDelta::FromHours(2));
+
+  testing::StrictMock<MockTask> mock_task_c;
+  Task task_c(FROM_HERE, BindOnce(&MockTask::Run, Unretained(&mock_task_c)),
+              TaskTraits(), TimeDelta::FromHours(1));
+
+  // Send tasks to the DelayedTaskManager.
+  delayed_task_manager_.AddDelayedTask(std::move(task_a), BindOnce(&RunTask));
+  delayed_task_manager_.AddDelayedTask(std::move(task_b), BindOnce(&RunTask));
+  delayed_task_manager_.AddDelayedTask(std::move(task_c), BindOnce(&RunTask));
+
+  // Run tasks that are ripe for execution on the service thread. Don't expect
+  // any call to RunTask().
+  service_thread_task_runner_->RunUntilIdle();
+
+  // Fast-forward time. Expect |task_a| and |task_c| to be forwarded to
+  // |task_target_|.
+  EXPECT_CALL(mock_task_a, Run());
+  EXPECT_CALL(mock_task_c, Run());
+  service_thread_task_runner_->FastForwardBy(TimeDelta::FromHours(1));
+  testing::Mock::VerifyAndClear(&mock_task_a);
+  testing::Mock::VerifyAndClear(&mock_task_c);
+
+  // Fast-forward time. Expect |task_b| to be forwarded to RunTask().
+  EXPECT_CALL(mock_task_b, Run());
+  service_thread_task_runner_->FastForwardBy(TimeDelta::FromHours(1));
+  testing::Mock::VerifyAndClear(&mock_task_b);
+}
+
+TEST_F(TaskSchedulerDelayedTaskManagerTest, PostTaskDuringStart) {
+  Thread other_thread("Test");
+  other_thread.StartAndWaitForTesting();
+
+  WaitableEvent task_posted;
+
+  other_thread.task_runner()->PostTask(FROM_HERE, BindLambdaForTesting([&]() {
+                                         delayed_task_manager_.AddDelayedTask(
+                                             std::move(task_),
+                                             BindOnce(&RunTask));
+                                         task_posted.Signal();
+                                       }));
+
+  delayed_task_manager_.Start(service_thread_task_runner_);
+
+  // The test is testing a race between AddDelayedTask/Start but it still needs
+  // synchronization to ensure we don't do the final verification before the
+  // task itself is posted.
+  task_posted.Wait();
+
+  // Fast-forward time. Expect the task to be forwarded to RunTask().
+  EXPECT_CALL(mock_task_, Run());
+  service_thread_task_runner_->FastForwardBy(kLongDelay);
+}
+
+}  // namespace internal
+}  // namespace base
diff --git a/base/task_scheduler/initialization_util.cc b/base/task_scheduler/initialization_util.cc
new file mode 100644
index 0000000..7accd19
--- /dev/null
+++ b/base/task_scheduler/initialization_util.cc
@@ -0,0 +1,22 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/task_scheduler/initialization_util.h"
+
+#include <algorithm>
+
+#include "base/sys_info.h"
+
+namespace base {
+
+int RecommendedMaxNumberOfThreadsInPool(int min,
+                                        int max,
+                                        double cores_multiplier,
+                                        int offset) {
+  const int num_of_cores = SysInfo::NumberOfProcessors();
+  const int threads = std::ceil<int>(num_of_cores * cores_multiplier) + offset;
+  return std::min(max, std::max(min, threads));
+}
+
+}  // namespace base
diff --git a/base/task_scheduler/initialization_util.h b/base/task_scheduler/initialization_util.h
new file mode 100644
index 0000000..c3bd9e7
--- /dev/null
+++ b/base/task_scheduler/initialization_util.h
@@ -0,0 +1,21 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TASK_SCHEDULER_INITIALIZATION_UTIL_H_
+#define BASE_TASK_SCHEDULER_INITIALIZATION_UTIL_H_
+
+#include "base/base_export.h"
+
+namespace base {
+
+// Computes a value that may be used as the maximum number of threads in a
+// TaskScheduler pool. Developers may use other methods to choose this maximum.
+BASE_EXPORT int RecommendedMaxNumberOfThreadsInPool(int min,
+                                                    int max,
+                                                    double cores_multiplier,
+                                                    int offset);
+
+}  // namespace base
+
+#endif  // BASE_TASK_SCHEDULER_INITIALIZATION_UTIL_H_
diff --git a/base/task_scheduler/priority_queue_unittest.cc b/base/task_scheduler/priority_queue_unittest.cc
new file mode 100644
index 0000000..f131c55
--- /dev/null
+++ b/base/task_scheduler/priority_queue_unittest.cc
@@ -0,0 +1,170 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/task_scheduler/priority_queue.h"
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/task_scheduler/sequence.h"
+#include "base/task_scheduler/task.h"
+#include "base/task_scheduler/task_traits.h"
+#include "base/test/gtest_util.h"
+#include "base/threading/platform_thread.h"
+#include "base/threading/simple_thread.h"
+#include "base/time/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace internal {
+
+namespace {
+
+class ThreadBeginningTransaction : public SimpleThread {
+ public:
+  explicit ThreadBeginningTransaction(PriorityQueue* priority_queue)
+      : SimpleThread("ThreadBeginningTransaction"),
+        priority_queue_(priority_queue) {}
+
+  // SimpleThread:
+  void Run() override {
+    std::unique_ptr<PriorityQueue::Transaction> transaction =
+        priority_queue_->BeginTransaction();
+    transaction_began_.Signal();
+  }
+
+  void ExpectTransactionDoesNotBegin() {
+    // After a few milliseconds, the call to BeginTransaction() should not have
+    // returned.
+    EXPECT_FALSE(
+        transaction_began_.TimedWait(TimeDelta::FromMilliseconds(250)));
+  }
+
+ private:
+  PriorityQueue* const priority_queue_;
+  WaitableEvent transaction_began_;
+
+  DISALLOW_COPY_AND_ASSIGN(ThreadBeginningTransaction);
+};
+
+}  // namespace
+
+TEST(TaskSchedulerPriorityQueueTest, PushPopPeek) {
+  // Create test sequences.
+  scoped_refptr<Sequence> sequence_a(new Sequence);
+  sequence_a->PushTask(Task(FROM_HERE, DoNothing(),
+                            TaskTraits(TaskPriority::USER_VISIBLE),
+                            TimeDelta()));
+  SequenceSortKey sort_key_a = sequence_a->GetSortKey();
+
+  scoped_refptr<Sequence> sequence_b(new Sequence);
+  sequence_b->PushTask(Task(FROM_HERE, DoNothing(),
+                            TaskTraits(TaskPriority::USER_BLOCKING),
+                            TimeDelta()));
+  SequenceSortKey sort_key_b = sequence_b->GetSortKey();
+
+  scoped_refptr<Sequence> sequence_c(new Sequence);
+  sequence_c->PushTask(Task(FROM_HERE, DoNothing(),
+                            TaskTraits(TaskPriority::USER_BLOCKING),
+                            TimeDelta()));
+  SequenceSortKey sort_key_c = sequence_c->GetSortKey();
+
+  scoped_refptr<Sequence> sequence_d(new Sequence);
+  sequence_d->PushTask(Task(FROM_HERE, DoNothing(),
+                            TaskTraits(TaskPriority::BACKGROUND), TimeDelta()));
+  SequenceSortKey sort_key_d = sequence_d->GetSortKey();
+
+  // Create a PriorityQueue and a Transaction.
+  PriorityQueue pq;
+  auto transaction(pq.BeginTransaction());
+  EXPECT_TRUE(transaction->IsEmpty());
+
+  // Push |sequence_a| in the PriorityQueue. It becomes the sequence with the
+  // highest priority.
+  transaction->Push(sequence_a, sort_key_a);
+  EXPECT_EQ(sort_key_a, transaction->PeekSortKey());
+
+  // Push |sequence_b| in the PriorityQueue. It becomes the sequence with the
+  // highest priority.
+  transaction->Push(sequence_b, sort_key_b);
+  EXPECT_EQ(sort_key_b, transaction->PeekSortKey());
+
+  // Push |sequence_c| in the PriorityQueue. |sequence_b| is still the sequence
+  // with the highest priority.
+  transaction->Push(sequence_c, sort_key_c);
+  EXPECT_EQ(sort_key_b, transaction->PeekSortKey());
+
+  // Push |sequence_d| in the PriorityQueue. |sequence_b| is still the sequence
+  // with the highest priority.
+  transaction->Push(sequence_d, sort_key_d);
+  EXPECT_EQ(sort_key_b, transaction->PeekSortKey());
+
+  // Pop |sequence_b| from the PriorityQueue. |sequence_c| becomes the sequence
+  // with the highest priority.
+  EXPECT_EQ(sequence_b, transaction->PopSequence());
+  EXPECT_EQ(sort_key_c, transaction->PeekSortKey());
+
+  // Pop |sequence_c| from the PriorityQueue. |sequence_a| becomes the sequence
+  // with the highest priority.
+  EXPECT_EQ(sequence_c, transaction->PopSequence());
+  EXPECT_EQ(sort_key_a, transaction->PeekSortKey());
+
+  // Pop |sequence_a| from the PriorityQueue. |sequence_d| becomes the sequence
+  // with the highest priority.
+  EXPECT_EQ(sequence_a, transaction->PopSequence());
+  EXPECT_EQ(sort_key_d, transaction->PeekSortKey());
+
+  // Pop |sequence_d| from the PriorityQueue. It is now empty.
+  EXPECT_EQ(sequence_d, transaction->PopSequence());
+  EXPECT_TRUE(transaction->IsEmpty());
+}
+
+// Check that creating Transactions on the same thread for 2 unrelated
+// PriorityQueues causes a crash.
+TEST(TaskSchedulerPriorityQueueTest, IllegalTwoTransactionsSameThread) {
+  PriorityQueue pq_a;
+  PriorityQueue pq_b;
+
+  EXPECT_DCHECK_DEATH(
+      {
+        std::unique_ptr<PriorityQueue::Transaction> transaction_a =
+            pq_a.BeginTransaction();
+        std::unique_ptr<PriorityQueue::Transaction> transaction_b =
+            pq_b.BeginTransaction();
+      });
+}
+
+// Check that it is possible to begin multiple Transactions for the same
+// PriorityQueue on different threads. The call to BeginTransaction() on the
+// second thread should block until the Transaction has ended on the first
+// thread.
+TEST(TaskSchedulerPriorityQueueTest, TwoTransactionsTwoThreads) {
+  PriorityQueue pq;
+
+  // Call BeginTransaction() on this thread and keep the Transaction alive.
+  std::unique_ptr<PriorityQueue::Transaction> transaction =
+      pq.BeginTransaction();
+
+  // Call BeginTransaction() on another thread.
+  ThreadBeginningTransaction thread_beginning_transaction(&pq);
+  thread_beginning_transaction.Start();
+
+  // After a few milliseconds, the call to BeginTransaction() on the other
+  // thread should not have returned.
+  thread_beginning_transaction.ExpectTransactionDoesNotBegin();
+
+  // End the Transaction on the current thread.
+  transaction.reset();
+
+  // The other thread should exit after its call to BeginTransaction() returns.
+  thread_beginning_transaction.Join();
+}
+
+}  // namespace internal
+}  // namespace base
diff --git a/base/task_scheduler/scheduler_single_thread_task_runner_manager_unittest.cc b/base/task_scheduler/scheduler_single_thread_task_runner_manager_unittest.cc
new file mode 100644
index 0000000..8eb02f3
--- /dev/null
+++ b/base/task_scheduler/scheduler_single_thread_task_runner_manager_unittest.cc
@@ -0,0 +1,662 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/task_scheduler/scheduler_single_thread_task_runner_manager.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/memory/ptr_util.h"
+#include "base/synchronization/atomic_flag.h"
+#include "base/synchronization/lock.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/task_scheduler/delayed_task_manager.h"
+#include "base/task_scheduler/environment_config.h"
+#include "base/task_scheduler/post_task.h"
+#include "base/task_scheduler/scheduler_worker_pool_params.h"
+#include "base/task_scheduler/task_tracker.h"
+#include "base/task_scheduler/task_traits.h"
+#include "base/test/gtest_util.h"
+#include "base/test/test_timeouts.h"
+#include "base/threading/platform_thread.h"
+#include "base/threading/simple_thread.h"
+#include "base/threading/thread.h"
+#include "base/threading/thread_restrictions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+
+#include "base/win/com_init_util.h"
+#include "base/win/current_module.h"
+#endif  // defined(OS_WIN)
+
+namespace base {
+namespace internal {
+
+namespace {
+
+class TaskSchedulerSingleThreadTaskRunnerManagerTest : public testing::Test {
+ public:
+  TaskSchedulerSingleThreadTaskRunnerManagerTest()
+      : service_thread_("TaskSchedulerServiceThread") {}
+
+  void SetUp() override {
+    service_thread_.Start();
+    delayed_task_manager_.Start(service_thread_.task_runner());
+    single_thread_task_runner_manager_ =
+        std::make_unique<SchedulerSingleThreadTaskRunnerManager>(
+            task_tracker_.GetTrackedRef(), &delayed_task_manager_);
+    StartSingleThreadTaskRunnerManagerFromSetUp();
+  }
+
+  void TearDown() override {
+    if (single_thread_task_runner_manager_)
+      TearDownSingleThreadTaskRunnerManager();
+    service_thread_.Stop();
+  }
+
+ protected:
+  virtual void StartSingleThreadTaskRunnerManagerFromSetUp() {
+    single_thread_task_runner_manager_->Start();
+  }
+
+  virtual void TearDownSingleThreadTaskRunnerManager() {
+    single_thread_task_runner_manager_->JoinForTesting();
+    single_thread_task_runner_manager_.reset();
+  }
+
+  Thread service_thread_;
+  TaskTracker task_tracker_ = {"Test"};
+  DelayedTaskManager delayed_task_manager_;
+  std::unique_ptr<SchedulerSingleThreadTaskRunnerManager>
+      single_thread_task_runner_manager_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TaskSchedulerSingleThreadTaskRunnerManagerTest);
+};
+
+void CaptureThreadRef(PlatformThreadRef* thread_ref) {
+  ASSERT_TRUE(thread_ref);
+  *thread_ref = PlatformThread::CurrentRef();
+}
+
+void CaptureThreadPriority(ThreadPriority* thread_priority) {
+  ASSERT_TRUE(thread_priority);
+  *thread_priority = PlatformThread::GetCurrentThreadPriority();
+}
+
+void CaptureThreadName(std::string* thread_name) {
+  *thread_name = PlatformThread::GetName();
+}
+
+void ShouldNotRun() {
+  ADD_FAILURE() << "Ran a task that shouldn't run.";
+}
+
+}  // namespace
+
+TEST_F(TaskSchedulerSingleThreadTaskRunnerManagerTest, DifferentThreadsUsed) {
+  scoped_refptr<SingleThreadTaskRunner> task_runner_1 =
+      single_thread_task_runner_manager_
+          ->CreateSingleThreadTaskRunnerWithTraits(
+              {TaskShutdownBehavior::BLOCK_SHUTDOWN},
+              SingleThreadTaskRunnerThreadMode::DEDICATED);
+  scoped_refptr<SingleThreadTaskRunner> task_runner_2 =
+      single_thread_task_runner_manager_
+          ->CreateSingleThreadTaskRunnerWithTraits(
+              {TaskShutdownBehavior::BLOCK_SHUTDOWN},
+              SingleThreadTaskRunnerThreadMode::DEDICATED);
+
+  PlatformThreadRef thread_ref_1;
+  task_runner_1->PostTask(FROM_HERE,
+                          BindOnce(&CaptureThreadRef, &thread_ref_1));
+  PlatformThreadRef thread_ref_2;
+  task_runner_2->PostTask(FROM_HERE,
+                          BindOnce(&CaptureThreadRef, &thread_ref_2));
+
+  task_tracker_.Shutdown();
+
+  ASSERT_FALSE(thread_ref_1.is_null());
+  ASSERT_FALSE(thread_ref_2.is_null());
+  EXPECT_NE(thread_ref_1, thread_ref_2);
+}
+
+TEST_F(TaskSchedulerSingleThreadTaskRunnerManagerTest, SameThreadUsed) {
+  scoped_refptr<SingleThreadTaskRunner> task_runner_1 =
+      single_thread_task_runner_manager_
+          ->CreateSingleThreadTaskRunnerWithTraits(
+              {TaskShutdownBehavior::BLOCK_SHUTDOWN},
+              SingleThreadTaskRunnerThreadMode::SHARED);
+  scoped_refptr<SingleThreadTaskRunner> task_runner_2 =
+      single_thread_task_runner_manager_
+          ->CreateSingleThreadTaskRunnerWithTraits(
+              {TaskShutdownBehavior::BLOCK_SHUTDOWN},
+              SingleThreadTaskRunnerThreadMode::SHARED);
+
+  PlatformThreadRef thread_ref_1;
+  task_runner_1->PostTask(FROM_HERE,
+                          BindOnce(&CaptureThreadRef, &thread_ref_1));
+  PlatformThreadRef thread_ref_2;
+  task_runner_2->PostTask(FROM_HERE,
+                          BindOnce(&CaptureThreadRef, &thread_ref_2));
+
+  task_tracker_.Shutdown();
+
+  ASSERT_FALSE(thread_ref_1.is_null());
+  ASSERT_FALSE(thread_ref_2.is_null());
+  EXPECT_EQ(thread_ref_1, thread_ref_2);
+}
+
+TEST_F(TaskSchedulerSingleThreadTaskRunnerManagerTest,
+       RunsTasksInCurrentSequence) {
+  scoped_refptr<SingleThreadTaskRunner> task_runner_1 =
+      single_thread_task_runner_manager_
+          ->CreateSingleThreadTaskRunnerWithTraits(
+              {TaskShutdownBehavior::BLOCK_SHUTDOWN},
+              SingleThreadTaskRunnerThreadMode::DEDICATED);
+  scoped_refptr<SingleThreadTaskRunner> task_runner_2 =
+      single_thread_task_runner_manager_
+          ->CreateSingleThreadTaskRunnerWithTraits(
+              {TaskShutdownBehavior::BLOCK_SHUTDOWN},
+              SingleThreadTaskRunnerThreadMode::DEDICATED);
+
+  EXPECT_FALSE(task_runner_1->RunsTasksInCurrentSequence());
+  EXPECT_FALSE(task_runner_2->RunsTasksInCurrentSequence());
+
+  task_runner_1->PostTask(
+      FROM_HERE,
+      BindOnce(
+          [](scoped_refptr<SingleThreadTaskRunner> task_runner_1,
+             scoped_refptr<SingleThreadTaskRunner> task_runner_2) {
+            EXPECT_TRUE(task_runner_1->RunsTasksInCurrentSequence());
+            EXPECT_FALSE(task_runner_2->RunsTasksInCurrentSequence());
+          },
+          task_runner_1, task_runner_2));
+
+  task_runner_2->PostTask(
+      FROM_HERE,
+      BindOnce(
+          [](scoped_refptr<SingleThreadTaskRunner> task_runner_1,
+             scoped_refptr<SingleThreadTaskRunner> task_runner_2) {
+            EXPECT_FALSE(task_runner_1->RunsTasksInCurrentSequence());
+            EXPECT_TRUE(task_runner_2->RunsTasksInCurrentSequence());
+          },
+          task_runner_1, task_runner_2));
+
+  task_tracker_.Shutdown();
+}
+
+TEST_F(TaskSchedulerSingleThreadTaskRunnerManagerTest,
+       SharedWithBaseSyncPrimitivesDCHECKs) {
+  testing::GTEST_FLAG(death_test_style) = "threadsafe";
+  EXPECT_DCHECK_DEATH({
+    single_thread_task_runner_manager_->CreateSingleThreadTaskRunnerWithTraits(
+        {WithBaseSyncPrimitives()}, SingleThreadTaskRunnerThreadMode::SHARED);
+  });
+}
+
+// Regression test for https://crbug.com/829786
+TEST_F(TaskSchedulerSingleThreadTaskRunnerManagerTest,
+       ContinueOnShutdownDoesNotBlockBlockShutdown) {
+  WaitableEvent task_has_started;
+  WaitableEvent task_can_continue;
+
+  // Post a CONTINUE_ON_SHUTDOWN task that waits on
+  // |task_can_continue| to a shared SingleThreadTaskRunner.
+  single_thread_task_runner_manager_
+      ->CreateSingleThreadTaskRunnerWithTraits(
+          {TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
+          SingleThreadTaskRunnerThreadMode::SHARED)
+      ->PostTask(FROM_HERE, base::BindOnce(
+                                [](WaitableEvent* task_has_started,
+                                   WaitableEvent* task_can_continue) {
+                                  task_has_started->Signal();
+                                  ScopedAllowBaseSyncPrimitivesForTesting
+                                      allow_base_sync_primitives;
+                                  task_can_continue->Wait();
+                                },
+                                Unretained(&task_has_started),
+                                Unretained(&task_can_continue)));
+
+  task_has_started.Wait();
+
+  // Post a BLOCK_SHUTDOWN task to a shared SingleThreadTaskRunner.
+  single_thread_task_runner_manager_
+      ->CreateSingleThreadTaskRunnerWithTraits(
+          {TaskShutdownBehavior::BLOCK_SHUTDOWN},
+          SingleThreadTaskRunnerThreadMode::SHARED)
+      ->PostTask(FROM_HERE, DoNothing());
+
+  // Shutdown should not hang even though the first task hasn't finished.
+  task_tracker_.Shutdown();
+
+  // Let the first task finish.
+  task_can_continue.Signal();
+
+  // Tear down from the test body to prevent accesses to |task_can_continue|
+  // after it goes out of scope.
+  TearDownSingleThreadTaskRunnerManager();
+}
+
+namespace {
+
+class TaskSchedulerSingleThreadTaskRunnerManagerCommonTest
+    : public TaskSchedulerSingleThreadTaskRunnerManagerTest,
+      public ::testing::WithParamInterface<SingleThreadTaskRunnerThreadMode> {
+ public:
+  TaskSchedulerSingleThreadTaskRunnerManagerCommonTest() = default;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(
+      TaskSchedulerSingleThreadTaskRunnerManagerCommonTest);
+};
+
+}  // namespace
+
+TEST_P(TaskSchedulerSingleThreadTaskRunnerManagerCommonTest,
+       PrioritySetCorrectly) {
+  // Why are events used here instead of the task tracker?
+  // Shutting down can cause priorities to get raised. This means we have to use
+  // events to determine when a task is run.
+  scoped_refptr<SingleThreadTaskRunner> task_runner_background =
+      single_thread_task_runner_manager_
+          ->CreateSingleThreadTaskRunnerWithTraits({TaskPriority::BACKGROUND},
+                                                   GetParam());
+  scoped_refptr<SingleThreadTaskRunner> task_runner_normal =
+      single_thread_task_runner_manager_
+          ->CreateSingleThreadTaskRunnerWithTraits({TaskPriority::USER_VISIBLE},
+                                                   GetParam());
+
+  ThreadPriority thread_priority_background;
+  task_runner_background->PostTask(
+      FROM_HERE, BindOnce(&CaptureThreadPriority, &thread_priority_background));
+  WaitableEvent waitable_event_background;
+  task_runner_background->PostTask(
+      FROM_HERE,
+      BindOnce(&WaitableEvent::Signal, Unretained(&waitable_event_background)));
+
+  ThreadPriority thread_priority_normal;
+  task_runner_normal->PostTask(
+      FROM_HERE, BindOnce(&CaptureThreadPriority, &thread_priority_normal));
+  WaitableEvent waitable_event_normal;
+  task_runner_normal->PostTask(
+      FROM_HERE,
+      BindOnce(&WaitableEvent::Signal, Unretained(&waitable_event_normal)));
+
+  waitable_event_background.Wait();
+  waitable_event_normal.Wait();
+
+  if (CanUseBackgroundPriorityForSchedulerWorker())
+    EXPECT_EQ(ThreadPriority::BACKGROUND, thread_priority_background);
+  else
+    EXPECT_EQ(ThreadPriority::NORMAL, thread_priority_background);
+  EXPECT_EQ(ThreadPriority::NORMAL, thread_priority_normal);
+}
+
+TEST_P(TaskSchedulerSingleThreadTaskRunnerManagerCommonTest, ThreadNamesSet) {
+  constexpr TaskTraits foo_traits = {TaskPriority::BACKGROUND,
+                                     TaskShutdownBehavior::BLOCK_SHUTDOWN};
+  scoped_refptr<SingleThreadTaskRunner> foo_task_runner =
+      single_thread_task_runner_manager_
+          ->CreateSingleThreadTaskRunnerWithTraits(foo_traits, GetParam());
+  std::string foo_captured_name;
+  foo_task_runner->PostTask(FROM_HERE,
+                            BindOnce(&CaptureThreadName, &foo_captured_name));
+
+  constexpr TaskTraits user_blocking_traits = {
+      TaskPriority::USER_BLOCKING, MayBlock(),
+      TaskShutdownBehavior::BLOCK_SHUTDOWN};
+  scoped_refptr<SingleThreadTaskRunner> user_blocking_task_runner =
+      single_thread_task_runner_manager_
+          ->CreateSingleThreadTaskRunnerWithTraits(user_blocking_traits,
+                                                   GetParam());
+
+  std::string user_blocking_captured_name;
+  user_blocking_task_runner->PostTask(
+      FROM_HERE, BindOnce(&CaptureThreadName, &user_blocking_captured_name));
+
+  task_tracker_.Shutdown();
+
+  EXPECT_NE(std::string::npos,
+            foo_captured_name.find(
+                kEnvironmentParams[GetEnvironmentIndexForTraits(foo_traits)]
+                    .name_suffix));
+  EXPECT_NE(
+      std::string::npos,
+      user_blocking_captured_name.find(
+          kEnvironmentParams[GetEnvironmentIndexForTraits(user_blocking_traits)]
+              .name_suffix));
+
+  if (GetParam() == SingleThreadTaskRunnerThreadMode::DEDICATED) {
+    EXPECT_EQ(std::string::npos, foo_captured_name.find("Shared"));
+    EXPECT_EQ(std::string::npos, user_blocking_captured_name.find("Shared"));
+  } else {
+    EXPECT_NE(std::string::npos, foo_captured_name.find("Shared"));
+    EXPECT_NE(std::string::npos, user_blocking_captured_name.find("Shared"));
+  }
+}
+
+TEST_P(TaskSchedulerSingleThreadTaskRunnerManagerCommonTest,
+       PostTaskAfterShutdown) {
+  auto task_runner =
+      single_thread_task_runner_manager_
+          ->CreateSingleThreadTaskRunnerWithTraits(TaskTraits(), GetParam());
+  task_tracker_.Shutdown();
+  EXPECT_FALSE(task_runner->PostTask(FROM_HERE, BindOnce(&ShouldNotRun)));
+}
+
+// Verify that a Task runs shortly after its delay expires.
+TEST_P(TaskSchedulerSingleThreadTaskRunnerManagerCommonTest, PostDelayedTask) {
+  TimeTicks start_time = TimeTicks::Now();
+
+  WaitableEvent task_ran(WaitableEvent::ResetPolicy::AUTOMATIC,
+                         WaitableEvent::InitialState::NOT_SIGNALED);
+  auto task_runner =
+      single_thread_task_runner_manager_
+          ->CreateSingleThreadTaskRunnerWithTraits(TaskTraits(), GetParam());
+
+  // Wait until the task runner is up and running to make sure the test below is
+  // solely timing the delayed task, not bringing up a physical thread.
+  task_runner->PostTask(
+      FROM_HERE, BindOnce(&WaitableEvent::Signal, Unretained(&task_ran)));
+  task_ran.Wait();
+  ASSERT_TRUE(!task_ran.IsSignaled());
+
+  // Post a task with a short delay.
+  EXPECT_TRUE(task_runner->PostDelayedTask(
+      FROM_HERE, BindOnce(&WaitableEvent::Signal, Unretained(&task_ran)),
+      TestTimeouts::tiny_timeout()));
+
+  // Wait until the task runs.
+  task_ran.Wait();
+
+  // Expect the task to run after its delay expires, but no more than 250 ms
+  // after that.
+  const TimeDelta actual_delay = TimeTicks::Now() - start_time;
+  EXPECT_GE(actual_delay, TestTimeouts::tiny_timeout());
+  EXPECT_LT(actual_delay,
+            TimeDelta::FromMilliseconds(250) + TestTimeouts::tiny_timeout());
+}
+
+// Verify that posting tasks after the single-thread manager is destroyed fails
+// but doesn't crash.
+TEST_P(TaskSchedulerSingleThreadTaskRunnerManagerCommonTest,
+       PostTaskAfterDestroy) {
+  auto task_runner =
+      single_thread_task_runner_manager_
+          ->CreateSingleThreadTaskRunnerWithTraits(TaskTraits(), GetParam());
+  EXPECT_TRUE(task_runner->PostTask(FROM_HERE, DoNothing()));
+  task_tracker_.Shutdown();
+  TearDownSingleThreadTaskRunnerManager();
+  EXPECT_FALSE(task_runner->PostTask(FROM_HERE, BindOnce(&ShouldNotRun)));
+}
+
+INSTANTIATE_TEST_CASE_P(
+    AllModes,
+    TaskSchedulerSingleThreadTaskRunnerManagerCommonTest,
+    ::testing::Values(SingleThreadTaskRunnerThreadMode::SHARED,
+                      SingleThreadTaskRunnerThreadMode::DEDICATED));
+
+namespace {
+
+class CallJoinFromDifferentThread : public SimpleThread {
+ public:
+  CallJoinFromDifferentThread(
+      SchedulerSingleThreadTaskRunnerManager* manager_to_join)
+      : SimpleThread("SchedulerSingleThreadTaskRunnerManagerJoinThread"),
+        manager_to_join_(manager_to_join) {}
+
+  ~CallJoinFromDifferentThread() override = default;
+
+  void Run() override {
+    run_started_event_.Signal();
+    manager_to_join_->JoinForTesting();
+  }
+
+  void WaitForRunToStart() { run_started_event_.Wait(); }
+
+ private:
+  SchedulerSingleThreadTaskRunnerManager* const manager_to_join_;
+  WaitableEvent run_started_event_;
+
+  DISALLOW_COPY_AND_ASSIGN(CallJoinFromDifferentThread);
+};
+
+class TaskSchedulerSingleThreadTaskRunnerManagerJoinTest
+    : public TaskSchedulerSingleThreadTaskRunnerManagerTest {
+ public:
+  TaskSchedulerSingleThreadTaskRunnerManagerJoinTest() = default;
+  ~TaskSchedulerSingleThreadTaskRunnerManagerJoinTest() override = default;
+
+ protected:
+  void TearDownSingleThreadTaskRunnerManager() override {
+    // The tests themselves are responsible for calling JoinForTesting().
+    single_thread_task_runner_manager_.reset();
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TaskSchedulerSingleThreadTaskRunnerManagerJoinTest);
+};
+
+}  // namespace
+
+TEST_F(TaskSchedulerSingleThreadTaskRunnerManagerJoinTest, ConcurrentJoin) {
+  // Exercises the codepath where the workers are unavailable for unregistration
+  // because of a Join call.
+  WaitableEvent task_running;
+  WaitableEvent task_blocking;
+
+  {
+    auto task_runner = single_thread_task_runner_manager_
+                           ->CreateSingleThreadTaskRunnerWithTraits(
+                               {WithBaseSyncPrimitives()},
+                               SingleThreadTaskRunnerThreadMode::DEDICATED);
+    EXPECT_TRUE(task_runner->PostTask(
+        FROM_HERE,
+        BindOnce(&WaitableEvent::Signal, Unretained(&task_running))));
+    EXPECT_TRUE(task_runner->PostTask(
+        FROM_HERE, BindOnce(&WaitableEvent::Wait, Unretained(&task_blocking))));
+  }
+
+  task_running.Wait();
+  CallJoinFromDifferentThread join_from_different_thread(
+      single_thread_task_runner_manager_.get());
+  join_from_different_thread.Start();
+  join_from_different_thread.WaitForRunToStart();
+  task_blocking.Signal();
+  join_from_different_thread.Join();
+}
+
+TEST_F(TaskSchedulerSingleThreadTaskRunnerManagerJoinTest,
+       ConcurrentJoinExtraSkippedTask) {
+  // Tests to make sure that tasks are properly cleaned up at Join, allowing
+  // SingleThreadTaskRunners to unregister themselves.
+  WaitableEvent task_running;
+  WaitableEvent task_blocking;
+
+  {
+    auto task_runner = single_thread_task_runner_manager_
+                           ->CreateSingleThreadTaskRunnerWithTraits(
+                               {WithBaseSyncPrimitives()},
+                               SingleThreadTaskRunnerThreadMode::DEDICATED);
+    EXPECT_TRUE(task_runner->PostTask(
+        FROM_HERE,
+        BindOnce(&WaitableEvent::Signal, Unretained(&task_running))));
+    EXPECT_TRUE(task_runner->PostTask(
+        FROM_HERE, BindOnce(&WaitableEvent::Wait, Unretained(&task_blocking))));
+    EXPECT_TRUE(task_runner->PostTask(FROM_HERE, DoNothing()));
+  }
+
+  task_running.Wait();
+  CallJoinFromDifferentThread join_from_different_thread(
+      single_thread_task_runner_manager_.get());
+  join_from_different_thread.Start();
+  join_from_different_thread.WaitForRunToStart();
+  task_blocking.Signal();
+  join_from_different_thread.Join();
+}
+
+#if defined(OS_WIN)
+
+TEST_P(TaskSchedulerSingleThreadTaskRunnerManagerCommonTest,
+       COMSTAInitialized) {
+  scoped_refptr<SingleThreadTaskRunner> com_task_runner =
+      single_thread_task_runner_manager_->CreateCOMSTATaskRunnerWithTraits(
+          {TaskShutdownBehavior::BLOCK_SHUTDOWN}, GetParam());
+
+  com_task_runner->PostTask(FROM_HERE, BindOnce(&win::AssertComApartmentType,
+                                                win::ComApartmentType::STA));
+
+  task_tracker_.Shutdown();
+}
+
+TEST_F(TaskSchedulerSingleThreadTaskRunnerManagerTest, COMSTASameThreadUsed) {
+  scoped_refptr<SingleThreadTaskRunner> task_runner_1 =
+      single_thread_task_runner_manager_->CreateCOMSTATaskRunnerWithTraits(
+          {TaskShutdownBehavior::BLOCK_SHUTDOWN},
+          SingleThreadTaskRunnerThreadMode::SHARED);
+  scoped_refptr<SingleThreadTaskRunner> task_runner_2 =
+      single_thread_task_runner_manager_->CreateCOMSTATaskRunnerWithTraits(
+          {TaskShutdownBehavior::BLOCK_SHUTDOWN},
+          SingleThreadTaskRunnerThreadMode::SHARED);
+
+  PlatformThreadRef thread_ref_1;
+  task_runner_1->PostTask(FROM_HERE,
+                          BindOnce(&CaptureThreadRef, &thread_ref_1));
+  PlatformThreadRef thread_ref_2;
+  task_runner_2->PostTask(FROM_HERE,
+                          BindOnce(&CaptureThreadRef, &thread_ref_2));
+
+  task_tracker_.Shutdown();
+
+  ASSERT_FALSE(thread_ref_1.is_null());
+  ASSERT_FALSE(thread_ref_2.is_null());
+  EXPECT_EQ(thread_ref_1, thread_ref_2);
+}
+
+namespace {
+
+const wchar_t* const kTestWindowClassName =
+    L"TaskSchedulerSingleThreadTaskRunnerManagerTestWinMessageWindow";
+
+class TaskSchedulerSingleThreadTaskRunnerManagerTestWin
+    : public TaskSchedulerSingleThreadTaskRunnerManagerTest {
+ public:
+  TaskSchedulerSingleThreadTaskRunnerManagerTestWin() = default;
+
+  void SetUp() override {
+    TaskSchedulerSingleThreadTaskRunnerManagerTest::SetUp();
+    register_class_succeeded_ = RegisterTestWindowClass();
+    ASSERT_TRUE(register_class_succeeded_);
+  }
+
+  void TearDown() override {
+    if (register_class_succeeded_)
+      ::UnregisterClass(kTestWindowClassName, CURRENT_MODULE());
+
+    TaskSchedulerSingleThreadTaskRunnerManagerTest::TearDown();
+  }
+
+  HWND CreateTestWindow() {
+    return CreateWindow(kTestWindowClassName, kTestWindowClassName, 0, 0, 0, 0,
+                        0, HWND_MESSAGE, nullptr, CURRENT_MODULE(), nullptr);
+  }
+
+ private:
+  bool RegisterTestWindowClass() {
+    WNDCLASSEX window_class = {};
+    window_class.cbSize = sizeof(window_class);
+    window_class.lpfnWndProc = &::DefWindowProc;
+    window_class.hInstance = CURRENT_MODULE();
+    window_class.lpszClassName = kTestWindowClassName;
+    return !!::RegisterClassEx(&window_class);
+  }
+
+  bool register_class_succeeded_ = false;
+
+  DISALLOW_COPY_AND_ASSIGN(TaskSchedulerSingleThreadTaskRunnerManagerTestWin);
+};
+
+}  // namespace
+
+TEST_F(TaskSchedulerSingleThreadTaskRunnerManagerTestWin, PumpsMessages) {
+  scoped_refptr<SingleThreadTaskRunner> com_task_runner =
+      single_thread_task_runner_manager_->CreateCOMSTATaskRunnerWithTraits(
+          {TaskShutdownBehavior::BLOCK_SHUTDOWN},
+          SingleThreadTaskRunnerThreadMode::DEDICATED);
+  HWND hwnd = nullptr;
+  // HWNDs process messages on the thread that created them, so we have to
+  // create them within the context of the task runner to properly simulate a
+  // COM callback.
+  com_task_runner->PostTask(
+      FROM_HERE,
+      BindOnce(
+          [](TaskSchedulerSingleThreadTaskRunnerManagerTestWin* test_harness,
+             HWND* hwnd) { *hwnd = test_harness->CreateTestWindow(); },
+          Unretained(this), &hwnd));
+
+  task_tracker_.FlushForTesting();
+
+  ASSERT_NE(hwnd, nullptr);
+  // If the message pump isn't running, we will hang here. This simulates how
+  // COM would receive a callback with its own message HWND.
+  SendMessage(hwnd, WM_USER, 0, 0);
+
+  com_task_runner->PostTask(
+      FROM_HERE, BindOnce([](HWND hwnd) { ::DestroyWindow(hwnd); }, hwnd));
+
+  task_tracker_.Shutdown();
+}
+
+#endif  // defined(OS_WIN)
+
+namespace {
+
+class TaskSchedulerSingleThreadTaskRunnerManagerStartTest
+    : public TaskSchedulerSingleThreadTaskRunnerManagerTest {
+ public:
+  TaskSchedulerSingleThreadTaskRunnerManagerStartTest() = default;
+
+ private:
+  void StartSingleThreadTaskRunnerManagerFromSetUp() override {
+    // Start() is called in the test body rather than in SetUp().
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(TaskSchedulerSingleThreadTaskRunnerManagerStartTest);
+};
+
+}  // namespace
+
+// Verify that a task posted before Start() doesn't run until Start() is called.
+TEST_F(TaskSchedulerSingleThreadTaskRunnerManagerStartTest,
+       PostTaskBeforeStart) {
+  AtomicFlag manager_started;
+  WaitableEvent task_finished;
+  single_thread_task_runner_manager_
+      ->CreateSingleThreadTaskRunnerWithTraits(
+          TaskTraits(), SingleThreadTaskRunnerThreadMode::DEDICATED)
+      ->PostTask(
+          FROM_HERE,
+          BindOnce(
+              [](WaitableEvent* task_finished, AtomicFlag* manager_started) {
+                // The task should not run before Start().
+                EXPECT_TRUE(manager_started->IsSet());
+                task_finished->Signal();
+              },
+              Unretained(&task_finished), Unretained(&manager_started)));
+
+  // Wait a little bit to make sure that the task doesn't run before start.
+  // Note: This test won't catch a case where the task runs between setting
+  // |manager_started| and calling Start(). However, we expect the test to be
+  // flaky if the tested code allows that to happen.
+  PlatformThread::Sleep(TestTimeouts::tiny_timeout());
+  manager_started.Set();
+  single_thread_task_runner_manager_->Start();
+
+  // Wait for the task to complete to keep |manager_started| alive.
+  task_finished.Wait();
+}
+
+}  // namespace internal
+}  // namespace base
diff --git a/base/task_scheduler/scheduler_worker_pool_impl_unittest.cc b/base/task_scheduler/scheduler_worker_pool_impl_unittest.cc
new file mode 100644
index 0000000..f510194
--- /dev/null
+++ b/base/task_scheduler/scheduler_worker_pool_impl_unittest.cc
@@ -0,0 +1,1707 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/task_scheduler/scheduler_worker_pool_impl.h"
+
+#include <stddef.h>
+
+#include <memory>
+#include <unordered_set>
+#include <vector>
+
+#include "base/atomicops.h"
+#include "base/barrier_closure.h"
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted.h"
+#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_samples.h"
+#include "base/metrics/statistics_recorder.h"
+#include "base/synchronization/atomic_flag.h"
+#include "base/synchronization/condition_variable.h"
+#include "base/synchronization/lock.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/task_runner.h"
+#include "base/task_scheduler/delayed_task_manager.h"
+#include "base/task_scheduler/scheduler_worker_pool_params.h"
+#include "base/task_scheduler/sequence.h"
+#include "base/task_scheduler/sequence_sort_key.h"
+#include "base/task_scheduler/task_tracker.h"
+#include "base/task_scheduler/test_task_factory.h"
+#include "base/task_scheduler/test_utils.h"
+#include "base/test/bind_test_util.h"
+#include "base/test/gtest_util.h"
+#include "base/test/test_simple_task_runner.h"
+#include "base/test/test_timeouts.h"
+#include "base/threading/platform_thread.h"
+#include "base/threading/scoped_blocking_call.h"
+#include "base/threading/simple_thread.h"
+#include "base/threading/thread.h"
+#include "base/threading/thread_checker_impl.h"
+#include "base/threading/thread_local_storage.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if defined(OS_WIN)
+#include "base/win/com_init_util.h"
+#endif  // defined(OS_WIN)
+
+namespace base {
+namespace internal {
+namespace {
+
+constexpr size_t kMaxTasks = 4;
+constexpr size_t kNumThreadsPostingTasks = 4;
+constexpr size_t kNumTasksPostedPerThread = 150;
+// This can't be lower because Windows' WaitableEvent wakes up too early when a
+// small timeout is used. This results in many spurious wake ups before a worker
+// is allowed to cleanup.
+constexpr TimeDelta kReclaimTimeForCleanupTests =
+    TimeDelta::FromMilliseconds(500);
+
+// Waits on |event| in a scope where the blocking observer is null, to avoid
+// affecting the max tasks.
+void WaitWithoutBlockingObserver(WaitableEvent* event) {
+  internal::ScopedClearBlockingObserverForTesting clear_blocking_observer;
+  ScopedAllowBaseSyncPrimitivesForTesting allow_base_sync_primitives;
+  event->Wait();
+}
+
+class TaskSchedulerWorkerPoolImplTestBase {
+ protected:
+  TaskSchedulerWorkerPoolImplTestBase()
+      : service_thread_("TaskSchedulerServiceThread"){};
+
+  void CommonSetUp(TimeDelta suggested_reclaim_time = TimeDelta::Max()) {
+    CreateAndStartWorkerPool(suggested_reclaim_time, kMaxTasks);
+  }
+
+  void CommonTearDown() {
+    service_thread_.Stop();
+    task_tracker_.FlushForTesting();
+    if (worker_pool_)
+      worker_pool_->JoinForTesting();
+  }
+
+  void CreateWorkerPool() {
+    ASSERT_FALSE(worker_pool_);
+    service_thread_.Start();
+    delayed_task_manager_.Start(service_thread_.task_runner());
+    worker_pool_ = std::make_unique<SchedulerWorkerPoolImpl>(
+        "TestWorkerPool", "A", ThreadPriority::NORMAL,
+        task_tracker_.GetTrackedRef(), &delayed_task_manager_);
+    ASSERT_TRUE(worker_pool_);
+  }
+
+  virtual void StartWorkerPool(TimeDelta suggested_reclaim_time,
+                               size_t max_tasks) {
+    ASSERT_TRUE(worker_pool_);
+    worker_pool_->Start(
+        SchedulerWorkerPoolParams(max_tasks, suggested_reclaim_time), max_tasks,
+        service_thread_.task_runner(), nullptr,
+        SchedulerWorkerPoolImpl::WorkerEnvironment::NONE);
+  }
+
+  void CreateAndStartWorkerPool(TimeDelta suggested_reclaim_time,
+                                size_t max_tasks) {
+    CreateWorkerPool();
+    StartWorkerPool(suggested_reclaim_time, max_tasks);
+  }
+
+  Thread service_thread_;
+  TaskTracker task_tracker_ = {"Test"};
+
+  std::unique_ptr<SchedulerWorkerPoolImpl> worker_pool_;
+
+ private:
+  DelayedTaskManager delayed_task_manager_;
+
+  DISALLOW_COPY_AND_ASSIGN(TaskSchedulerWorkerPoolImplTestBase);
+};
+
+class TaskSchedulerWorkerPoolImplTest
+    : public TaskSchedulerWorkerPoolImplTestBase,
+      public testing::Test {
+ protected:
+  TaskSchedulerWorkerPoolImplTest() = default;
+
+  void SetUp() override { TaskSchedulerWorkerPoolImplTestBase::CommonSetUp(); }
+
+  void TearDown() override {
+    TaskSchedulerWorkerPoolImplTestBase::CommonTearDown();
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TaskSchedulerWorkerPoolImplTest);
+};
+
+class TaskSchedulerWorkerPoolImplTestParam
+    : public TaskSchedulerWorkerPoolImplTestBase,
+      public testing::TestWithParam<test::ExecutionMode> {
+ protected:
+  TaskSchedulerWorkerPoolImplTestParam() = default;
+
+  void SetUp() override { TaskSchedulerWorkerPoolImplTestBase::CommonSetUp(); }
+
+  void TearDown() override {
+    TaskSchedulerWorkerPoolImplTestBase::CommonTearDown();
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TaskSchedulerWorkerPoolImplTestParam);
+};
+
+using PostNestedTask = test::TestTaskFactory::PostNestedTask;
+
+class ThreadPostingTasksWaitIdle : public SimpleThread {
+ public:
+  // Constructs a thread that posts tasks to |worker_pool| through an
+  // |execution_mode| task runner. The thread waits until all workers in
+  // |worker_pool| are idle before posting a new task.
+  ThreadPostingTasksWaitIdle(SchedulerWorkerPoolImpl* worker_pool,
+                             test::ExecutionMode execution_mode)
+      : SimpleThread("ThreadPostingTasksWaitIdle"),
+        worker_pool_(worker_pool),
+        factory_(CreateTaskRunnerWithExecutionMode(worker_pool, execution_mode),
+                 execution_mode) {
+    DCHECK(worker_pool_);
+  }
+
+  const test::TestTaskFactory* factory() const { return &factory_; }
+
+ private:
+  void Run() override {
+    EXPECT_FALSE(factory_.task_runner()->RunsTasksInCurrentSequence());
+
+    for (size_t i = 0; i < kNumTasksPostedPerThread; ++i) {
+      worker_pool_->WaitForAllWorkersIdleForTesting();
+      EXPECT_TRUE(factory_.PostTask(PostNestedTask::NO, Closure()));
+    }
+  }
+
+  SchedulerWorkerPoolImpl* const worker_pool_;
+  const scoped_refptr<TaskRunner> task_runner_;
+  test::TestTaskFactory factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(ThreadPostingTasksWaitIdle);
+};
+
+}  // namespace
+
+TEST_P(TaskSchedulerWorkerPoolImplTestParam, PostTasksWaitAllWorkersIdle) {
+  // Create threads to post tasks. To verify that workers can sleep and be woken
+  // up when new tasks are posted, wait for all workers to become idle before
+  // posting a new task.
+  std::vector<std::unique_ptr<ThreadPostingTasksWaitIdle>>
+      threads_posting_tasks;
+  for (size_t i = 0; i < kNumThreadsPostingTasks; ++i) {
+    threads_posting_tasks.push_back(
+        std::make_unique<ThreadPostingTasksWaitIdle>(worker_pool_.get(),
+                                                     GetParam()));
+    threads_posting_tasks.back()->Start();
+  }
+
+  // Wait for all tasks to run.
+  for (const auto& thread_posting_tasks : threads_posting_tasks) {
+    thread_posting_tasks->Join();
+    thread_posting_tasks->factory()->WaitForAllTasksToRun();
+  }
+
+  // Wait until all workers are idle to be sure that no task accesses its
+  // TestTaskFactory after |thread_posting_tasks| is destroyed.
+  worker_pool_->WaitForAllWorkersIdleForTesting();
+}
+
+TEST_P(TaskSchedulerWorkerPoolImplTestParam, PostTasksWithOneAvailableWorker) {
+  // Post blocking tasks to keep all workers busy except one until |event| is
+  // signaled. Use different factories so that tasks are added to different
+  // sequences and can run simultaneously when the execution mode is SEQUENCED.
+  WaitableEvent event;
+  std::vector<std::unique_ptr<test::TestTaskFactory>> blocked_task_factories;
+  for (size_t i = 0; i < (kMaxTasks - 1); ++i) {
+    blocked_task_factories.push_back(std::make_unique<test::TestTaskFactory>(
+        CreateTaskRunnerWithExecutionMode(worker_pool_.get(), GetParam()),
+        GetParam()));
+    EXPECT_TRUE(blocked_task_factories.back()->PostTask(
+        PostNestedTask::NO,
+        BindOnce(&WaitWithoutBlockingObserver, Unretained(&event))));
+    blocked_task_factories.back()->WaitForAllTasksToRun();
+  }
+
+  // Post |kNumTasksPostedPerThread| tasks that should all run despite the fact
+  // that only one worker in |worker_pool_| isn't busy.
+  test::TestTaskFactory short_task_factory(
+      CreateTaskRunnerWithExecutionMode(worker_pool_.get(), GetParam()),
+      GetParam());
+  for (size_t i = 0; i < kNumTasksPostedPerThread; ++i)
+    EXPECT_TRUE(short_task_factory.PostTask(PostNestedTask::NO, Closure()));
+  short_task_factory.WaitForAllTasksToRun();
+
+  // Release tasks waiting on |event|.
+  event.Signal();
+
+  // Wait until all workers are idle to be sure that no task accesses
+  // its TestTaskFactory after it is destroyed.
+  worker_pool_->WaitForAllWorkersIdleForTesting();
+}
+
+TEST_P(TaskSchedulerWorkerPoolImplTestParam, Saturate) {
+  // Verify that it is possible to have |kMaxTasks| tasks/sequences running
+  // simultaneously. Use different factories so that the blocking tasks are
+  // added to different sequences and can run simultaneously when the execution
+  // mode is SEQUENCED.
+  WaitableEvent event;
+  std::vector<std::unique_ptr<test::TestTaskFactory>> factories;
+  for (size_t i = 0; i < kMaxTasks; ++i) {
+    factories.push_back(std::make_unique<test::TestTaskFactory>(
+        CreateTaskRunnerWithExecutionMode(worker_pool_.get(), GetParam()),
+        GetParam()));
+    EXPECT_TRUE(factories.back()->PostTask(
+        PostNestedTask::NO,
+        BindOnce(&WaitWithoutBlockingObserver, Unretained(&event))));
+    factories.back()->WaitForAllTasksToRun();
+  }
+
+  // Release tasks waiting on |event|.
+  event.Signal();
+
+  // Wait until all workers are idle to be sure that no task accesses
+  // its TestTaskFactory after it is destroyed.
+  worker_pool_->WaitForAllWorkersIdleForTesting();
+}
+
+#if defined(OS_WIN)
+TEST_P(TaskSchedulerWorkerPoolImplTestParam, NoEnvironment) {
+  // Verify that COM is not initialized in a SchedulerWorkerPoolImpl initialized
+  // with SchedulerWorkerPoolImpl::WorkerEnvironment::NONE.
+  scoped_refptr<TaskRunner> task_runner =
+      CreateTaskRunnerWithExecutionMode(worker_pool_.get(), GetParam());
+
+  WaitableEvent task_running;
+  task_runner->PostTask(
+      FROM_HERE, BindOnce(
+                     [](WaitableEvent* task_running) {
+                       win::AssertComApartmentType(win::ComApartmentType::NONE);
+                       task_running->Signal();
+                     },
+                     &task_running));
+
+  task_running.Wait();
+
+  worker_pool_->WaitForAllWorkersIdleForTesting();
+}
+#endif  // defined(OS_WIN)
+
+INSTANTIATE_TEST_CASE_P(Parallel,
+                        TaskSchedulerWorkerPoolImplTestParam,
+                        ::testing::Values(test::ExecutionMode::PARALLEL));
+INSTANTIATE_TEST_CASE_P(Sequenced,
+                        TaskSchedulerWorkerPoolImplTestParam,
+                        ::testing::Values(test::ExecutionMode::SEQUENCED));
+
+#if defined(OS_WIN)
+
+namespace {
+
+class TaskSchedulerWorkerPoolImplTestCOMMTAParam
+    : public TaskSchedulerWorkerPoolImplTestBase,
+      public testing::TestWithParam<test::ExecutionMode> {
+ protected:
+  TaskSchedulerWorkerPoolImplTestCOMMTAParam() = default;
+
+  void SetUp() override { TaskSchedulerWorkerPoolImplTestBase::CommonSetUp(); }
+
+  void TearDown() override {
+    TaskSchedulerWorkerPoolImplTestBase::CommonTearDown();
+  }
+
+ private:
+  void StartWorkerPool(TimeDelta suggested_reclaim_time,
+                       size_t max_tasks) override {
+    ASSERT_TRUE(worker_pool_);
+    worker_pool_->Start(
+        SchedulerWorkerPoolParams(max_tasks, suggested_reclaim_time), max_tasks,
+        service_thread_.task_runner(), nullptr,
+        SchedulerWorkerPoolImpl::WorkerEnvironment::COM_MTA);
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(TaskSchedulerWorkerPoolImplTestCOMMTAParam);
+};
+
+}  // namespace
+
+TEST_P(TaskSchedulerWorkerPoolImplTestCOMMTAParam, COMMTAInitialized) {
+  // Verify that SchedulerWorkerPoolImpl workers have a COM MTA available.
+  scoped_refptr<TaskRunner> task_runner =
+      CreateTaskRunnerWithExecutionMode(worker_pool_.get(), GetParam());
+
+  WaitableEvent task_running;
+  task_runner->PostTask(
+      FROM_HERE, BindOnce(
+                     [](WaitableEvent* task_running) {
+                       win::AssertComApartmentType(win::ComApartmentType::MTA);
+                       task_running->Signal();
+                     },
+                     &task_running));
+
+  task_running.Wait();
+
+  worker_pool_->WaitForAllWorkersIdleForTesting();
+}
+
+INSTANTIATE_TEST_CASE_P(Parallel,
+                        TaskSchedulerWorkerPoolImplTestCOMMTAParam,
+                        ::testing::Values(test::ExecutionMode::PARALLEL));
+INSTANTIATE_TEST_CASE_P(Sequenced,
+                        TaskSchedulerWorkerPoolImplTestCOMMTAParam,
+                        ::testing::Values(test::ExecutionMode::SEQUENCED));
+
+#endif  // defined(OS_WIN)
+
+namespace {
+
+class TaskSchedulerWorkerPoolImplStartInBodyTest
+    : public TaskSchedulerWorkerPoolImplTest {
+ public:
+  void SetUp() override {
+    CreateWorkerPool();
+    // Let the test start the worker pool.
+  }
+};
+
+void TaskPostedBeforeStart(PlatformThreadRef* platform_thread_ref,
+                           WaitableEvent* task_running,
+                           WaitableEvent* barrier) {
+  *platform_thread_ref = PlatformThread::CurrentRef();
+  task_running->Signal();
+  WaitWithoutBlockingObserver(barrier);
+}
+
+}  // namespace
+
+// Verify that 2 tasks posted before Start() to a SchedulerWorkerPoolImpl with
+// more than 2 workers run on different workers when Start() is called.
+TEST_F(TaskSchedulerWorkerPoolImplStartInBodyTest, PostTasksBeforeStart) {
+  PlatformThreadRef task_1_thread_ref;
+  PlatformThreadRef task_2_thread_ref;
+  WaitableEvent task_1_running;
+  WaitableEvent task_2_running;
+
+  // This event is used to prevent a task from completing before the other task
+  // starts running. If that happened, both tasks could run on the same worker
+  // and this test couldn't verify that the correct number of workers were woken
+  // up.
+  WaitableEvent barrier;
+
+  worker_pool_->CreateTaskRunnerWithTraits({WithBaseSyncPrimitives()})
+      ->PostTask(
+          FROM_HERE,
+          BindOnce(&TaskPostedBeforeStart, Unretained(&task_1_thread_ref),
+                   Unretained(&task_1_running), Unretained(&barrier)));
+  worker_pool_->CreateTaskRunnerWithTraits({WithBaseSyncPrimitives()})
+      ->PostTask(
+          FROM_HERE,
+          BindOnce(&TaskPostedBeforeStart, Unretained(&task_2_thread_ref),
+                   Unretained(&task_2_running), Unretained(&barrier)));
+
+  // Workers should not be created and tasks should not run before the pool is
+  // started.
+  EXPECT_EQ(0U, worker_pool_->NumberOfWorkersForTesting());
+  EXPECT_FALSE(task_1_running.IsSignaled());
+  EXPECT_FALSE(task_2_running.IsSignaled());
+
+  StartWorkerPool(TimeDelta::Max(), kMaxTasks);
+
+  // Tasks should run shortly after the pool is started.
+  task_1_running.Wait();
+  task_2_running.Wait();
+
+  // Tasks should run on different threads.
+  EXPECT_NE(task_1_thread_ref, task_2_thread_ref);
+
+  barrier.Signal();
+  task_tracker_.FlushForTesting();
+}
+
+// Verify that posting many tasks before Start will cause the number of workers
+// to grow to |max_tasks_| during Start.
+TEST_F(TaskSchedulerWorkerPoolImplStartInBodyTest, PostManyTasks) {
+  scoped_refptr<TaskRunner> task_runner =
+      worker_pool_->CreateTaskRunnerWithTraits({WithBaseSyncPrimitives()});
+  constexpr size_t kNumTasksPosted = 2 * kMaxTasks;
+  for (size_t i = 0; i < kNumTasksPosted; ++i)
+    task_runner->PostTask(FROM_HERE, DoNothing());
+
+  EXPECT_EQ(0U, worker_pool_->NumberOfWorkersForTesting());
+
+  StartWorkerPool(TimeDelta::Max(), kMaxTasks);
+  ASSERT_GT(kNumTasksPosted, worker_pool_->GetMaxTasksForTesting());
+  EXPECT_EQ(kMaxTasks, worker_pool_->GetMaxTasksForTesting());
+
+  EXPECT_EQ(worker_pool_->NumberOfWorkersForTesting(),
+            worker_pool_->GetMaxTasksForTesting());
+}
+
+namespace {
+
+constexpr size_t kMagicTlsValue = 42;
+
+class TaskSchedulerWorkerPoolCheckTlsReuse
+    : public TaskSchedulerWorkerPoolImplTest {
+ public:
+  void SetTlsValueAndWait() {
+    slot_.Set(reinterpret_cast<void*>(kMagicTlsValue));
+    WaitWithoutBlockingObserver(&waiter_);
+  }
+
+  void CountZeroTlsValuesAndWait(WaitableEvent* count_waiter) {
+    if (!slot_.Get())
+      subtle::NoBarrier_AtomicIncrement(&zero_tls_values_, 1);
+
+    count_waiter->Signal();
+    WaitWithoutBlockingObserver(&waiter_);
+  }
+
+ protected:
+  TaskSchedulerWorkerPoolCheckTlsReuse() = default;
+
+  void SetUp() override {
+    CreateAndStartWorkerPool(kReclaimTimeForCleanupTests, kMaxTasks);
+  }
+
+  subtle::Atomic32 zero_tls_values_ = 0;
+
+  WaitableEvent waiter_;
+
+ private:
+  ThreadLocalStorage::Slot slot_;
+
+  DISALLOW_COPY_AND_ASSIGN(TaskSchedulerWorkerPoolCheckTlsReuse);
+};
+
+}  // namespace
+
+// Checks that at least one worker has been cleaned up by checking the TLS.
+TEST_F(TaskSchedulerWorkerPoolCheckTlsReuse, CheckCleanupWorkers) {
+  // Saturate the workers and mark each worker's thread with a magic TLS value.
+  std::vector<std::unique_ptr<test::TestTaskFactory>> factories;
+  for (size_t i = 0; i < kMaxTasks; ++i) {
+    factories.push_back(std::make_unique<test::TestTaskFactory>(
+        worker_pool_->CreateTaskRunnerWithTraits({WithBaseSyncPrimitives()}),
+        test::ExecutionMode::PARALLEL));
+    ASSERT_TRUE(factories.back()->PostTask(
+        PostNestedTask::NO,
+        Bind(&TaskSchedulerWorkerPoolCheckTlsReuse::SetTlsValueAndWait,
+             Unretained(this))));
+    factories.back()->WaitForAllTasksToRun();
+  }
+
+  // Release tasks waiting on |waiter_|.
+  waiter_.Signal();
+  worker_pool_->WaitForAllWorkersIdleForTesting();
+
+  // All workers should be done running by now, so reset for the next phase.
+  waiter_.Reset();
+
+  // Wait for the worker pool to clean up at least one worker.
+  worker_pool_->WaitForWorkersCleanedUpForTesting(1U);
+
+  // Saturate and count the worker threads that do not have the magic TLS value.
+  // If the value is not there, that means we're at a new worker.
+  std::vector<std::unique_ptr<WaitableEvent>> count_waiters;
+  for (auto& factory : factories) {
+    count_waiters.push_back(std::make_unique<WaitableEvent>());
+    ASSERT_TRUE(factory->PostTask(
+          PostNestedTask::NO,
+          Bind(&TaskSchedulerWorkerPoolCheckTlsReuse::CountZeroTlsValuesAndWait,
+               Unretained(this),
+               count_waiters.back().get())));
+    factory->WaitForAllTasksToRun();
+  }
+
+  // Wait for all counters to complete.
+  for (auto& count_waiter : count_waiters)
+    count_waiter->Wait();
+
+  EXPECT_GT(subtle::NoBarrier_Load(&zero_tls_values_), 0);
+
+  // Release tasks waiting on |waiter_|.
+  waiter_.Signal();
+}
+
+namespace {
+
+class TaskSchedulerWorkerPoolHistogramTest
+    : public TaskSchedulerWorkerPoolImplTest {
+ public:
+  TaskSchedulerWorkerPoolHistogramTest() = default;
+
+ protected:
+  // Override SetUp() to allow every test case to initialize a worker pool with
+  // its own arguments.
+  void SetUp() override {}
+
+  // Floods |worker_pool_| with a single task each that blocks until
+  // |continue_event| is signaled. Every worker in the pool is blocked on
+  // |continue_event| when this method returns. Note: this helper can easily be
+  // generalized to be useful in other tests, but it's here for now because it's
+  // only used in a TaskSchedulerWorkerPoolHistogramTest at the moment.
+  void FloodPool(WaitableEvent* continue_event) {
+    ASSERT_FALSE(continue_event->IsSignaled());
+
+    auto task_runner =
+        worker_pool_->CreateTaskRunnerWithTraits({WithBaseSyncPrimitives()});
+
+    const auto max_tasks = worker_pool_->GetMaxTasksForTesting();
+
+    WaitableEvent workers_flooded;
+    RepeatingClosure all_workers_running_barrier = BarrierClosure(
+        max_tasks,
+        BindOnce(&WaitableEvent::Signal, Unretained(&workers_flooded)));
+    for (size_t i = 0; i < max_tasks; ++i) {
+      task_runner->PostTask(
+          FROM_HERE,
+          BindOnce(
+              [](OnceClosure on_running, WaitableEvent* continue_event) {
+                std::move(on_running).Run();
+                WaitWithoutBlockingObserver(continue_event);
+              },
+              all_workers_running_barrier, continue_event));
+    }
+    workers_flooded.Wait();
+  }
+
+ private:
+  std::unique_ptr<StatisticsRecorder> statistics_recorder_ =
+      StatisticsRecorder::CreateTemporaryForTesting();
+
+  DISALLOW_COPY_AND_ASSIGN(TaskSchedulerWorkerPoolHistogramTest);
+};
+
+}  // namespace
+
+TEST_F(TaskSchedulerWorkerPoolHistogramTest, NumTasksBetweenWaits) {
+  WaitableEvent event;
+  CreateAndStartWorkerPool(TimeDelta::Max(), kMaxTasks);
+  auto task_runner = worker_pool_->CreateSequencedTaskRunnerWithTraits(
+      {WithBaseSyncPrimitives()});
+
+  // Post a task.
+  task_runner->PostTask(
+      FROM_HERE, BindOnce(&WaitWithoutBlockingObserver, Unretained(&event)));
+
+  // Post 2 more tasks while the first task hasn't completed its execution. It
+  // is guaranteed that these tasks will run immediately after the first task,
+  // without allowing the worker to sleep.
+  task_runner->PostTask(FROM_HERE, DoNothing());
+  task_runner->PostTask(FROM_HERE, DoNothing());
+
+  // Allow tasks to run and wait until the SchedulerWorker is idle.
+  event.Signal();
+  worker_pool_->WaitForAllWorkersIdleForTesting();
+
+  // Wake up the SchedulerWorker that just became idle by posting a task and
+  // wait until it becomes idle again. The SchedulerWorker should record the
+  // TaskScheduler.NumTasksBetweenWaits.* histogram on wake up.
+  task_runner->PostTask(FROM_HERE, DoNothing());
+  worker_pool_->WaitForAllWorkersIdleForTesting();
+
+  // Verify that counts were recorded to the histogram as expected.
+  const auto* histogram = worker_pool_->num_tasks_between_waits_histogram();
+  EXPECT_EQ(0, histogram->SnapshotSamples()->GetCount(0));
+  EXPECT_EQ(1, histogram->SnapshotSamples()->GetCount(3));
+  EXPECT_EQ(0, histogram->SnapshotSamples()->GetCount(10));
+}
+
+// Verifies that NumTasksBetweenWaits histogram is logged as expected across
+// idle and cleanup periods.
+TEST_F(TaskSchedulerWorkerPoolHistogramTest,
+       NumTasksBetweenWaitsWithIdlePeriodAndCleanup) {
+  WaitableEvent tasks_can_exit_event;
+  CreateAndStartWorkerPool(kReclaimTimeForCleanupTests, kMaxTasks);
+
+  WaitableEvent workers_continue;
+
+  FloodPool(&workers_continue);
+
+  const auto* histogram = worker_pool_->num_tasks_between_waits_histogram();
+
+  // NumTasksBetweenWaits shouldn't be logged until idle.
+  EXPECT_EQ(0, histogram->SnapshotSamples()->GetCount(0));
+  EXPECT_EQ(0, histogram->SnapshotSamples()->GetCount(1));
+  EXPECT_EQ(0, histogram->SnapshotSamples()->GetCount(10));
+
+  // Make all workers go idle.
+  workers_continue.Signal();
+  worker_pool_->WaitForAllWorkersIdleForTesting();
+
+  // All workers should have reported a single hit in the "1" bucket per the the
+  // histogram being reported when going idle and each worker having processed
+  // precisely 1 task per the controlled flooding logic above.
+  EXPECT_EQ(0, histogram->SnapshotSamples()->GetCount(0));
+  EXPECT_EQ(static_cast<int>(kMaxTasks),
+            histogram->SnapshotSamples()->GetCount(1));
+  EXPECT_EQ(0, histogram->SnapshotSamples()->GetCount(10));
+
+  worker_pool_->WaitForWorkersCleanedUpForTesting(kMaxTasks - 1);
+
+  EXPECT_EQ(0, histogram->SnapshotSamples()->GetCount(0));
+  EXPECT_EQ(static_cast<int>(kMaxTasks),
+            histogram->SnapshotSamples()->GetCount(1));
+  EXPECT_EQ(0, histogram->SnapshotSamples()->GetCount(10));
+
+  // Flooding the pool once again (without letting any workers go idle)
+  // shouldn't affect the counts either.
+
+  workers_continue.Reset();
+  FloodPool(&workers_continue);
+
+  EXPECT_EQ(0, histogram->SnapshotSamples()->GetCount(0));
+  EXPECT_EQ(static_cast<int>(kMaxTasks),
+            histogram->SnapshotSamples()->GetCount(1));
+  EXPECT_EQ(0, histogram->SnapshotSamples()->GetCount(10));
+
+  workers_continue.Signal();
+  worker_pool_->WaitForAllWorkersIdleForTesting();
+}
+
+TEST_F(TaskSchedulerWorkerPoolHistogramTest, NumTasksBeforeCleanup) {
+  CreateWorkerPool();
+  auto histogrammed_thread_task_runner =
+      worker_pool_->CreateSequencedTaskRunnerWithTraits(
+          {WithBaseSyncPrimitives()});
+
+  // Post 3 tasks and hold the thread for idle thread stack ordering.
+  // This test assumes |histogrammed_thread_task_runner| gets assigned the same
+  // thread for each of its tasks.
+  PlatformThreadRef thread_ref;
+  histogrammed_thread_task_runner->PostTask(
+      FROM_HERE, BindOnce(
+                     [](PlatformThreadRef* thread_ref) {
+                       ASSERT_TRUE(thread_ref);
+                       *thread_ref = PlatformThread::CurrentRef();
+                     },
+                     Unretained(&thread_ref)));
+  histogrammed_thread_task_runner->PostTask(
+      FROM_HERE, BindOnce(
+                     [](PlatformThreadRef* thread_ref) {
+                       ASSERT_FALSE(thread_ref->is_null());
+                       EXPECT_EQ(*thread_ref, PlatformThread::CurrentRef());
+                     },
+                     Unretained(&thread_ref)));
+
+  WaitableEvent cleanup_thread_running;
+  WaitableEvent cleanup_thread_continue;
+  histogrammed_thread_task_runner->PostTask(
+      FROM_HERE,
+      BindOnce(
+          [](PlatformThreadRef* thread_ref,
+             WaitableEvent* cleanup_thread_running,
+             WaitableEvent* cleanup_thread_continue) {
+            ASSERT_FALSE(thread_ref->is_null());
+            EXPECT_EQ(*thread_ref, PlatformThread::CurrentRef());
+            cleanup_thread_running->Signal();
+            WaitWithoutBlockingObserver(cleanup_thread_continue);
+          },
+          Unretained(&thread_ref), Unretained(&cleanup_thread_running),
+          Unretained(&cleanup_thread_continue)));
+
+  // Start the worker pool with 2 workers, to avoid depending on the scheduler's
+  // logic to always keep one extra idle worker.
+  //
+  // The pool is started after the 3 initial tasks have been posted to ensure
+  // that they are scheduled on the same worker. If the tasks could run as they
+  // are posted, there would be a chance that:
+  // 1. Worker #1:        Runs a tasks and empties the sequence, without adding
+  //                      itself to the idle stack yet.
+  // 2. Posting thread:   Posts another task to the now empty sequence. Wakes
+  //                      up a new worker, since worker #1 isn't on the idle
+  //                      stack yet.
+  // 3: Worker #2:        Runs the tasks, violating the expectation that the 3
+  //                      initial tasks run on the same worker.
+  constexpr size_t kTwoWorkers = 2;
+  StartWorkerPool(kReclaimTimeForCleanupTests, kTwoWorkers);
+
+  // Wait until the 3rd task is scheduled.
+  cleanup_thread_running.Wait();
+
+  // To allow the SchedulerWorker associated with
+  // |histogrammed_thread_task_runner| to cleanup, make sure it isn't on top of
+  // the idle stack by waking up another SchedulerWorker via
+  // |task_runner_for_top_idle|. |histogrammed_thread_task_runner| should
+  // release and go idle first and then |task_runner_for_top_idle| should
+  // release and go idle. This allows the SchedulerWorker associated with
+  // |histogrammed_thread_task_runner| to cleanup.
+  WaitableEvent top_idle_thread_running;
+  WaitableEvent top_idle_thread_continue;
+  auto task_runner_for_top_idle =
+      worker_pool_->CreateSequencedTaskRunnerWithTraits(
+          {WithBaseSyncPrimitives()});
+  task_runner_for_top_idle->PostTask(
+      FROM_HERE, BindOnce(
+                     [](PlatformThreadRef thread_ref,
+                        WaitableEvent* top_idle_thread_running,
+                        WaitableEvent* top_idle_thread_continue) {
+                       ASSERT_FALSE(thread_ref.is_null());
+                       EXPECT_NE(thread_ref, PlatformThread::CurrentRef())
+                           << "Worker reused. Worker will not cleanup and the "
+                              "histogram value will be wrong.";
+                       top_idle_thread_running->Signal();
+                       WaitWithoutBlockingObserver(top_idle_thread_continue);
+                     },
+                     thread_ref, Unretained(&top_idle_thread_running),
+                     Unretained(&top_idle_thread_continue)));
+  top_idle_thread_running.Wait();
+  EXPECT_EQ(0U, worker_pool_->NumberOfIdleWorkersForTesting());
+  cleanup_thread_continue.Signal();
+  // Wait for the cleanup thread to also become idle.
+  worker_pool_->WaitForWorkersIdleForTesting(1U);
+  top_idle_thread_continue.Signal();
+  // Allow the thread processing the |histogrammed_thread_task_runner| work to
+  // cleanup.
+  worker_pool_->WaitForWorkersCleanedUpForTesting(1U);
+
+  // Verify that counts were recorded to the histogram as expected.
+  const auto* histogram = worker_pool_->num_tasks_before_detach_histogram();
+  EXPECT_EQ(0, histogram->SnapshotSamples()->GetCount(0));
+  EXPECT_EQ(0, histogram->SnapshotSamples()->GetCount(1));
+  EXPECT_EQ(0, histogram->SnapshotSamples()->GetCount(2));
+  EXPECT_EQ(1, histogram->SnapshotSamples()->GetCount(3));
+  EXPECT_EQ(0, histogram->SnapshotSamples()->GetCount(4));
+  EXPECT_EQ(0, histogram->SnapshotSamples()->GetCount(5));
+  EXPECT_EQ(0, histogram->SnapshotSamples()->GetCount(6));
+  EXPECT_EQ(0, histogram->SnapshotSamples()->GetCount(10));
+}
+
+namespace {
+
+class TaskSchedulerWorkerPoolStandbyPolicyTest
+    : public TaskSchedulerWorkerPoolImplTestBase,
+      public testing::Test {
+ public:
+  TaskSchedulerWorkerPoolStandbyPolicyTest() = default;
+
+  void SetUp() override {
+    TaskSchedulerWorkerPoolImplTestBase::CommonSetUp(
+        kReclaimTimeForCleanupTests);
+  }
+
+  void TearDown() override {
+    TaskSchedulerWorkerPoolImplTestBase::CommonTearDown();
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TaskSchedulerWorkerPoolStandbyPolicyTest);
+};
+
+}  // namespace
+
+TEST_F(TaskSchedulerWorkerPoolStandbyPolicyTest, InitOne) {
+  EXPECT_EQ(1U, worker_pool_->NumberOfWorkersForTesting());
+}
+
+// Verify that the SchedulerWorkerPoolImpl keeps at least one idle standby
+// thread, capacity permitting.
+TEST_F(TaskSchedulerWorkerPoolStandbyPolicyTest, VerifyStandbyThread) {
+  auto task_runner =
+      worker_pool_->CreateTaskRunnerWithTraits({WithBaseSyncPrimitives()});
+
+  WaitableEvent thread_running(WaitableEvent::ResetPolicy::AUTOMATIC);
+  WaitableEvent threads_continue;
+
+  RepeatingClosure thread_blocker = BindLambdaForTesting([&]() {
+    thread_running.Signal();
+    WaitWithoutBlockingObserver(&threads_continue);
+  });
+
+  // There should be one idle thread until we reach capacity
+  for (size_t i = 0; i < kMaxTasks; ++i) {
+    EXPECT_EQ(i + 1, worker_pool_->NumberOfWorkersForTesting());
+    task_runner->PostTask(FROM_HERE, thread_blocker);
+    thread_running.Wait();
+  }
+
+  // There should not be an extra idle thread if it means going above capacity
+  EXPECT_EQ(kMaxTasks, worker_pool_->NumberOfWorkersForTesting());
+
+  threads_continue.Signal();
+  // Wait long enough for all but one worker to clean up.
+  worker_pool_->WaitForWorkersCleanedUpForTesting(kMaxTasks - 1);
+  EXPECT_EQ(1U, worker_pool_->NumberOfWorkersForTesting());
+  // Give extra time for a worker to cleanup : none should as the pool is
+  // expected to keep a worker ready regardless of how long it was idle for.
+  PlatformThread::Sleep(kReclaimTimeForCleanupTests);
+  EXPECT_EQ(1U, worker_pool_->NumberOfWorkersForTesting());
+}
+
+// Verify that being "the" idle thread counts as being active (i.e. won't be
+// reclaimed even if not on top of the idle stack when reclaim timeout expires).
+// Regression test for https://crbug.com/847501.
+TEST_F(TaskSchedulerWorkerPoolStandbyPolicyTest,
+       InAndOutStandbyThreadIsActive) {
+  auto sequenced_task_runner =
+      worker_pool_->CreateSequencedTaskRunnerWithTraits({});
+
+  WaitableEvent timer_started;
+
+  RepeatingTimer recurring_task;
+  sequenced_task_runner->PostTask(
+      FROM_HERE, BindLambdaForTesting([&]() {
+        recurring_task.Start(FROM_HERE, kReclaimTimeForCleanupTests / 2,
+                             DoNothing());
+        timer_started.Signal();
+      }));
+
+  timer_started.Wait();
+
+  // Running a task should have brought up a new standby thread.
+  EXPECT_EQ(2U, worker_pool_->NumberOfWorkersForTesting());
+
+  // Give extra time for a worker to cleanup : none should as the two workers
+  // are both considered "active" per the timer ticking faster than the reclaim
+  // timeout.
+  PlatformThread::Sleep(kReclaimTimeForCleanupTests * 2);
+  EXPECT_EQ(2U, worker_pool_->NumberOfWorkersForTesting());
+
+  sequenced_task_runner->PostTask(
+      FROM_HERE, BindLambdaForTesting([&]() { recurring_task.Stop(); }));
+
+  // Stopping the recurring task should let the second worker be reclaimed per
+  // not being "the" standby thread for a full reclaim timeout.
+  worker_pool_->WaitForWorkersCleanedUpForTesting(1);
+  EXPECT_EQ(1U, worker_pool_->NumberOfWorkersForTesting());
+}
+
+// Verify that being "the" idle thread counts as being active but isn't sticky.
+// Regression test for https://crbug.com/847501.
+TEST_F(TaskSchedulerWorkerPoolStandbyPolicyTest, OnlyKeepActiveStandbyThreads) {
+  auto sequenced_task_runner =
+      worker_pool_->CreateSequencedTaskRunnerWithTraits({});
+
+  // Start this test like
+  // TaskSchedulerWorkerPoolStandbyPolicyTest.InAndOutStandbyThreadIsActive and
+  // give it some time to stabilize.
+  RepeatingTimer recurring_task;
+  sequenced_task_runner->PostTask(
+      FROM_HERE, BindLambdaForTesting([&]() {
+        recurring_task.Start(FROM_HERE, kReclaimTimeForCleanupTests / 2,
+                             DoNothing());
+      }));
+
+  PlatformThread::Sleep(kReclaimTimeForCleanupTests * 2);
+  EXPECT_EQ(2U, worker_pool_->NumberOfWorkersForTesting());
+
+  // Then also flood the pool (cycling the top of the idle stack).
+  {
+    auto task_runner =
+        worker_pool_->CreateTaskRunnerWithTraits({WithBaseSyncPrimitives()});
+
+    WaitableEvent thread_running(WaitableEvent::ResetPolicy::AUTOMATIC);
+    WaitableEvent threads_continue;
+
+    RepeatingClosure thread_blocker = BindLambdaForTesting([&]() {
+      thread_running.Signal();
+      WaitWithoutBlockingObserver(&threads_continue);
+    });
+
+    for (size_t i = 0; i < kMaxTasks; ++i) {
+      task_runner->PostTask(FROM_HERE, thread_blocker);
+      thread_running.Wait();
+    }
+
+    EXPECT_EQ(kMaxTasks, worker_pool_->NumberOfWorkersForTesting());
+    threads_continue.Signal();
+
+    // Flush to ensure all references to |threads_continue| are gone before it
+    // goes out of scope.
+    task_tracker_.FlushForTesting();
+  }
+
+  // All workers should clean up but two (since the timer is still running).
+  worker_pool_->WaitForWorkersCleanedUpForTesting(kMaxTasks - 2);
+  EXPECT_EQ(2U, worker_pool_->NumberOfWorkersForTesting());
+
+  // Extra time shouldn't change this.
+  PlatformThread::Sleep(kReclaimTimeForCleanupTests * 2);
+  EXPECT_EQ(2U, worker_pool_->NumberOfWorkersForTesting());
+
+  // Stopping the timer should let the number of active threads go down to one.
+  sequenced_task_runner->PostTask(
+      FROM_HERE, BindLambdaForTesting([&]() { recurring_task.Stop(); }));
+  worker_pool_->WaitForWorkersCleanedUpForTesting(1);
+  EXPECT_EQ(1U, worker_pool_->NumberOfWorkersForTesting());
+}
+
+namespace {
+
+enum class OptionalBlockingType {
+  NO_BLOCK,
+  MAY_BLOCK,
+  WILL_BLOCK,
+};
+
+struct NestedBlockingType {
+  NestedBlockingType(BlockingType first_in,
+                     OptionalBlockingType second_in,
+                     BlockingType behaves_as_in)
+      : first(first_in), second(second_in), behaves_as(behaves_as_in) {}
+
+  BlockingType first;
+  OptionalBlockingType second;
+  BlockingType behaves_as;
+};
+
+class NestedScopedBlockingCall {
+ public:
+  NestedScopedBlockingCall(const NestedBlockingType& nested_blocking_type)
+      : first_scoped_blocking_call_(nested_blocking_type.first),
+        second_scoped_blocking_call_(
+            nested_blocking_type.second == OptionalBlockingType::WILL_BLOCK
+                ? std::make_unique<ScopedBlockingCall>(BlockingType::WILL_BLOCK)
+                : (nested_blocking_type.second ==
+                           OptionalBlockingType::MAY_BLOCK
+                       ? std::make_unique<ScopedBlockingCall>(
+                             BlockingType::MAY_BLOCK)
+                       : nullptr)) {}
+
+ private:
+  ScopedBlockingCall first_scoped_blocking_call_;
+  std::unique_ptr<ScopedBlockingCall> second_scoped_blocking_call_;
+
+  DISALLOW_COPY_AND_ASSIGN(NestedScopedBlockingCall);
+};
+
+}  // namespace
+
+class TaskSchedulerWorkerPoolBlockingTest
+    : public TaskSchedulerWorkerPoolImplTestBase,
+      public testing::TestWithParam<NestedBlockingType> {
+ public:
+  TaskSchedulerWorkerPoolBlockingTest() = default;
+
+  static std::string ParamInfoToString(
+      ::testing::TestParamInfo<NestedBlockingType> param_info) {
+    std::string str = param_info.param.first == BlockingType::MAY_BLOCK
+                          ? "MAY_BLOCK"
+                          : "WILL_BLOCK";
+    if (param_info.param.second == OptionalBlockingType::MAY_BLOCK)
+      str += "_MAY_BLOCK";
+    else if (param_info.param.second == OptionalBlockingType::WILL_BLOCK)
+      str += "_WILL_BLOCK";
+    return str;
+  }
+
+  void SetUp() override {
+    TaskSchedulerWorkerPoolImplTestBase::CommonSetUp();
+    task_runner_ =
+        worker_pool_->CreateTaskRunnerWithTraits({WithBaseSyncPrimitives()});
+  }
+
+  void TearDown() override {
+    TaskSchedulerWorkerPoolImplTestBase::CommonTearDown();
+  }
+
+ protected:
+  // Saturates the worker pool with a task that first blocks, waits to be
+  // unblocked, then exits.
+  void SaturateWithBlockingTasks(
+      const NestedBlockingType& nested_blocking_type) {
+    ASSERT_FALSE(blocking_threads_running_.IsSignaled());
+
+    RepeatingClosure blocking_threads_running_closure = BarrierClosure(
+        kMaxTasks, BindOnce(&WaitableEvent::Signal,
+                            Unretained(&blocking_threads_running_)));
+
+    for (size_t i = 0; i < kMaxTasks; ++i) {
+      task_runner_->PostTask(
+          FROM_HERE,
+          BindOnce(
+              [](Closure* blocking_threads_running_closure,
+                 WaitableEvent* blocking_threads_continue_,
+                 const NestedBlockingType& nested_blocking_type) {
+                NestedScopedBlockingCall nested_scoped_blocking_call(
+                    nested_blocking_type);
+                blocking_threads_running_closure->Run();
+                WaitWithoutBlockingObserver(blocking_threads_continue_);
+              },
+              Unretained(&blocking_threads_running_closure),
+              Unretained(&blocking_threads_continue_), nested_blocking_type));
+    }
+    blocking_threads_running_.Wait();
+  }
+
+  // Returns how long we can expect a change to |max_tasks_| to occur
+  // after a task has become blocked.
+  TimeDelta GetMaxTasksChangeSleepTime() {
+    return std::max(SchedulerWorkerPoolImpl::kBlockedWorkersPollPeriod,
+                    worker_pool_->MayBlockThreshold()) +
+           TestTimeouts::tiny_timeout();
+  }
+
+  // Waits indefinitely, until |worker_pool_|'s max tasks increases to
+  // |expected_max_tasks|.
+  void ExpectMaxTasksIncreasesTo(size_t expected_max_tasks) {
+    size_t max_tasks = worker_pool_->GetMaxTasksForTesting();
+    while (max_tasks != expected_max_tasks) {
+      PlatformThread::Sleep(GetMaxTasksChangeSleepTime());
+      size_t new_max_tasks = worker_pool_->GetMaxTasksForTesting();
+      ASSERT_GE(new_max_tasks, max_tasks);
+      max_tasks = new_max_tasks;
+    }
+  }
+
+  // Unblocks tasks posted by SaturateWithBlockingTasks().
+  void UnblockTasks() { blocking_threads_continue_.Signal(); }
+
+  scoped_refptr<TaskRunner> task_runner_;
+
+ private:
+  WaitableEvent blocking_threads_running_;
+  WaitableEvent blocking_threads_continue_;
+
+  DISALLOW_COPY_AND_ASSIGN(TaskSchedulerWorkerPoolBlockingTest);
+};
+
+// Verify that BlockingScopeEntered() causes max tasks to increase and creates a
+// worker if needed. Also verify that BlockingScopeExited() decreases max tasks
+// after an increase.
+TEST_P(TaskSchedulerWorkerPoolBlockingTest, ThreadBlockedUnblocked) {
+  ASSERT_EQ(worker_pool_->GetMaxTasksForTesting(), kMaxTasks);
+
+  SaturateWithBlockingTasks(GetParam());
+  if (GetParam().behaves_as == BlockingType::MAY_BLOCK)
+    ExpectMaxTasksIncreasesTo(2 * kMaxTasks);
+  // A range of possible number of workers is accepted because of
+  // crbug.com/757897.
+  EXPECT_GE(worker_pool_->NumberOfWorkersForTesting(), kMaxTasks + 1);
+  EXPECT_LE(worker_pool_->NumberOfWorkersForTesting(), 2 * kMaxTasks);
+  EXPECT_EQ(worker_pool_->GetMaxTasksForTesting(), 2 * kMaxTasks);
+
+  UnblockTasks();
+  task_tracker_.FlushForTesting();
+  EXPECT_EQ(worker_pool_->GetMaxTasksForTesting(), kMaxTasks);
+}
+
+// Verify that tasks posted in a saturated pool before a ScopedBlockingCall will
+// execute after ScopedBlockingCall is instantiated.
+TEST_P(TaskSchedulerWorkerPoolBlockingTest, PostBeforeBlocking) {
+  WaitableEvent thread_running(WaitableEvent::ResetPolicy::AUTOMATIC);
+  WaitableEvent thread_can_block;
+  WaitableEvent threads_continue;
+
+  for (size_t i = 0; i < kMaxTasks; ++i) {
+    task_runner_->PostTask(
+        FROM_HERE,
+        BindOnce(
+            [](const NestedBlockingType& nested_blocking_type,
+               WaitableEvent* thread_running, WaitableEvent* thread_can_block,
+               WaitableEvent* threads_continue) {
+              thread_running->Signal();
+              WaitWithoutBlockingObserver(thread_can_block);
+
+              NestedScopedBlockingCall nested_scoped_blocking_call(
+                  nested_blocking_type);
+              WaitWithoutBlockingObserver(threads_continue);
+            },
+            GetParam(), Unretained(&thread_running),
+            Unretained(&thread_can_block), Unretained(&threads_continue)));
+    thread_running.Wait();
+  }
+
+  // All workers should be occupied and the pool should be saturated. Workers
+  // have not entered ScopedBlockingCall yet.
+  EXPECT_EQ(worker_pool_->NumberOfWorkersForTesting(), kMaxTasks);
+  EXPECT_EQ(worker_pool_->GetMaxTasksForTesting(), kMaxTasks);
+
+  WaitableEvent extra_threads_running;
+  WaitableEvent extra_threads_continue;
+  RepeatingClosure extra_threads_running_barrier = BarrierClosure(
+      kMaxTasks,
+      BindOnce(&WaitableEvent::Signal, Unretained(&extra_threads_running)));
+  for (size_t i = 0; i < kMaxTasks; ++i) {
+    task_runner_->PostTask(FROM_HERE,
+                           BindOnce(
+                               [](Closure* extra_threads_running_barrier,
+                                  WaitableEvent* extra_threads_continue) {
+                                 extra_threads_running_barrier->Run();
+                                 WaitWithoutBlockingObserver(
+                                     extra_threads_continue);
+                               },
+                               Unretained(&extra_threads_running_barrier),
+                               Unretained(&extra_threads_continue)));
+  }
+
+  // Allow tasks to enter ScopedBlockingCall. Workers should be created for the
+  // tasks we just posted.
+  thread_can_block.Signal();
+  if (GetParam().behaves_as == BlockingType::MAY_BLOCK)
+    ExpectMaxTasksIncreasesTo(2 * kMaxTasks);
+
+  // Should not block forever.
+  extra_threads_running.Wait();
+  EXPECT_EQ(worker_pool_->NumberOfWorkersForTesting(), 2 * kMaxTasks);
+  extra_threads_continue.Signal();
+
+  threads_continue.Signal();
+  task_tracker_.FlushForTesting();
+}
+// Verify that workers become idle when the pool is over-capacity and that
+// those workers do no work.
+TEST_P(TaskSchedulerWorkerPoolBlockingTest, WorkersIdleWhenOverCapacity) {
+  ASSERT_EQ(worker_pool_->GetMaxTasksForTesting(), kMaxTasks);
+
+  SaturateWithBlockingTasks(GetParam());
+  if (GetParam().behaves_as == BlockingType::MAY_BLOCK)
+    ExpectMaxTasksIncreasesTo(2 * kMaxTasks);
+  EXPECT_EQ(worker_pool_->GetMaxTasksForTesting(), 2 * kMaxTasks);
+  // A range of possible number of workers is accepted because of
+  // crbug.com/757897.
+  EXPECT_GE(worker_pool_->NumberOfWorkersForTesting(), kMaxTasks + 1);
+  EXPECT_LE(worker_pool_->NumberOfWorkersForTesting(), 2 * kMaxTasks);
+
+  WaitableEvent threads_running;
+  WaitableEvent threads_continue;
+
+  RepeatingClosure threads_running_barrier = BarrierClosure(
+      kMaxTasks,
+      BindOnce(&WaitableEvent::Signal, Unretained(&threads_running)));
+  // Posting these tasks should cause new workers to be created.
+  for (size_t i = 0; i < kMaxTasks; ++i) {
+    auto callback = BindOnce(
+        [](Closure* threads_running_barrier, WaitableEvent* threads_continue) {
+          threads_running_barrier->Run();
+          WaitWithoutBlockingObserver(threads_continue);
+        },
+        Unretained(&threads_running_barrier), Unretained(&threads_continue));
+    task_runner_->PostTask(FROM_HERE, std::move(callback));
+  }
+  threads_running.Wait();
+
+  ASSERT_EQ(worker_pool_->NumberOfIdleWorkersForTesting(), 0U);
+  EXPECT_EQ(worker_pool_->NumberOfWorkersForTesting(), 2 * kMaxTasks);
+
+  AtomicFlag is_exiting;
+  // These tasks should not get executed until after other tasks become
+  // unblocked.
+  for (size_t i = 0; i < kMaxTasks; ++i) {
+    task_runner_->PostTask(FROM_HERE, BindOnce(
+                                          [](AtomicFlag* is_exiting) {
+                                            EXPECT_TRUE(is_exiting->IsSet());
+                                          },
+                                          Unretained(&is_exiting)));
+  }
+
+  // The original |kMaxTasks| will finish their tasks after being
+  // unblocked. There will be work in the work queue, but the pool should now
+  // be over-capacity and workers will become idle.
+  UnblockTasks();
+  worker_pool_->WaitForWorkersIdleForTesting(kMaxTasks);
+  EXPECT_EQ(worker_pool_->NumberOfIdleWorkersForTesting(), kMaxTasks);
+
+  // Posting more tasks should not cause workers idle from the pool being over
+  // capacity to begin doing work.
+  for (size_t i = 0; i < kMaxTasks; ++i) {
+    task_runner_->PostTask(FROM_HERE, BindOnce(
+                                          [](AtomicFlag* is_exiting) {
+                                            EXPECT_TRUE(is_exiting->IsSet());
+                                          },
+                                          Unretained(&is_exiting)));
+  }
+
+  // Give time for those idle workers to possibly do work (which should not
+  // happen).
+  PlatformThread::Sleep(TestTimeouts::tiny_timeout());
+
+  is_exiting.Set();
+  // Unblocks the new workers.
+  threads_continue.Signal();
+  task_tracker_.FlushForTesting();
+}
+
+INSTANTIATE_TEST_CASE_P(
+    ,
+    TaskSchedulerWorkerPoolBlockingTest,
+    ::testing::Values(NestedBlockingType(BlockingType::MAY_BLOCK,
+                                         OptionalBlockingType::NO_BLOCK,
+                                         BlockingType::MAY_BLOCK),
+                      NestedBlockingType(BlockingType::WILL_BLOCK,
+                                         OptionalBlockingType::NO_BLOCK,
+                                         BlockingType::WILL_BLOCK),
+                      NestedBlockingType(BlockingType::MAY_BLOCK,
+                                         OptionalBlockingType::WILL_BLOCK,
+                                         BlockingType::WILL_BLOCK),
+                      NestedBlockingType(BlockingType::WILL_BLOCK,
+                                         OptionalBlockingType::MAY_BLOCK,
+                                         BlockingType::WILL_BLOCK)),
+    TaskSchedulerWorkerPoolBlockingTest::ParamInfoToString);
+
+// Verify that if a thread enters the scope of a MAY_BLOCK ScopedBlockingCall,
+// but exits the scope before the MayBlockThreshold() is reached, that the max
+// tasks does not increase.
+TEST_F(TaskSchedulerWorkerPoolBlockingTest, ThreadBlockUnblockPremature) {
+  ASSERT_EQ(worker_pool_->GetMaxTasksForTesting(), kMaxTasks);
+
+  TimeDelta max_tasks_change_sleep = GetMaxTasksChangeSleepTime();
+  worker_pool_->MaximizeMayBlockThresholdForTesting();
+
+  SaturateWithBlockingTasks(NestedBlockingType(BlockingType::MAY_BLOCK,
+                                               OptionalBlockingType::NO_BLOCK,
+                                               BlockingType::MAY_BLOCK));
+  PlatformThread::Sleep(max_tasks_change_sleep);
+  EXPECT_EQ(worker_pool_->NumberOfWorkersForTesting(), kMaxTasks);
+  EXPECT_EQ(worker_pool_->GetMaxTasksForTesting(), kMaxTasks);
+
+  UnblockTasks();
+  task_tracker_.FlushForTesting();
+  EXPECT_EQ(worker_pool_->GetMaxTasksForTesting(), kMaxTasks);
+}
+
+// Verify that if max tasks is incremented because of a MAY_BLOCK
+// ScopedBlockingCall, it isn't incremented again when there is a nested
+// WILL_BLOCK ScopedBlockingCall.
+TEST_F(TaskSchedulerWorkerPoolBlockingTest,
+       MayBlockIncreaseCapacityNestedWillBlock) {
+  ASSERT_EQ(worker_pool_->GetMaxTasksForTesting(), kMaxTasks);
+  auto task_runner =
+      worker_pool_->CreateTaskRunnerWithTraits({WithBaseSyncPrimitives()});
+  WaitableEvent can_return;
+
+  // Saturate the pool so that a MAY_BLOCK ScopedBlockingCall would increment
+  // the max tasks.
+  for (size_t i = 0; i < kMaxTasks - 1; ++i) {
+    task_runner->PostTask(FROM_HERE, BindOnce(&WaitWithoutBlockingObserver,
+                                              Unretained(&can_return)));
+  }
+
+  WaitableEvent can_instantiate_will_block;
+  WaitableEvent did_instantiate_will_block;
+
+  // Post a task that instantiates a MAY_BLOCK ScopedBlockingCall.
+  task_runner->PostTask(
+      FROM_HERE,
+      BindOnce(
+          [](WaitableEvent* can_instantiate_will_block,
+             WaitableEvent* did_instantiate_will_block,
+             WaitableEvent* can_return) {
+            ScopedBlockingCall may_block(BlockingType::MAY_BLOCK);
+            WaitWithoutBlockingObserver(can_instantiate_will_block);
+            ScopedBlockingCall will_block(BlockingType::WILL_BLOCK);
+            did_instantiate_will_block->Signal();
+            WaitWithoutBlockingObserver(can_return);
+          },
+          Unretained(&can_instantiate_will_block),
+          Unretained(&did_instantiate_will_block), Unretained(&can_return)));
+
+  // After a short delay, max tasks should be incremented.
+  ExpectMaxTasksIncreasesTo(kMaxTasks + 1);
+
+  // Wait until the task instantiates a WILL_BLOCK ScopedBlockingCall.
+  can_instantiate_will_block.Signal();
+  did_instantiate_will_block.Wait();
+
+  // Max tasks shouldn't be incremented again.
+  EXPECT_EQ(kMaxTasks + 1, worker_pool_->GetMaxTasksForTesting());
+
+  // Tear down.
+  can_return.Signal();
+  task_tracker_.FlushForTesting();
+  EXPECT_EQ(worker_pool_->GetMaxTasksForTesting(), kMaxTasks);
+}
+
+// Verify that workers that become idle due to the pool being over capacity will
+// eventually cleanup.
+TEST(TaskSchedulerWorkerPoolOverCapacityTest, VerifyCleanup) {
+  constexpr size_t kLocalMaxTasks = 3;
+
+  TaskTracker task_tracker("Test");
+  DelayedTaskManager delayed_task_manager;
+  scoped_refptr<TaskRunner> service_thread_task_runner =
+      MakeRefCounted<TestSimpleTaskRunner>();
+  delayed_task_manager.Start(service_thread_task_runner);
+  SchedulerWorkerPoolImpl worker_pool(
+      "OverCapacityTestWorkerPool", "A", ThreadPriority::NORMAL,
+      task_tracker.GetTrackedRef(), &delayed_task_manager);
+  worker_pool.Start(
+      SchedulerWorkerPoolParams(kLocalMaxTasks, kReclaimTimeForCleanupTests),
+      kLocalMaxTasks, service_thread_task_runner, nullptr,
+      SchedulerWorkerPoolImpl::WorkerEnvironment::NONE);
+
+  scoped_refptr<TaskRunner> task_runner =
+      worker_pool.CreateTaskRunnerWithTraits({WithBaseSyncPrimitives()});
+
+  WaitableEvent threads_running;
+  WaitableEvent threads_continue;
+  RepeatingClosure threads_running_barrier = BarrierClosure(
+      kLocalMaxTasks,
+      BindOnce(&WaitableEvent::Signal, Unretained(&threads_running)));
+
+  WaitableEvent blocked_call_continue;
+  RepeatingClosure closure = BindRepeating(
+      [](Closure* threads_running_barrier, WaitableEvent* threads_continue,
+         WaitableEvent* blocked_call_continue) {
+        threads_running_barrier->Run();
+        {
+          ScopedBlockingCall scoped_blocking_call(BlockingType::WILL_BLOCK);
+          WaitWithoutBlockingObserver(blocked_call_continue);
+        }
+        WaitWithoutBlockingObserver(threads_continue);
+      },
+      Unretained(&threads_running_barrier), Unretained(&threads_continue),
+      Unretained(&blocked_call_continue));
+
+  for (size_t i = 0; i < kLocalMaxTasks; ++i)
+    task_runner->PostTask(FROM_HERE, closure);
+
+  threads_running.Wait();
+
+  WaitableEvent extra_threads_running;
+  WaitableEvent extra_threads_continue;
+
+  RepeatingClosure extra_threads_running_barrier = BarrierClosure(
+      kLocalMaxTasks,
+      BindOnce(&WaitableEvent::Signal, Unretained(&extra_threads_running)));
+  // These tasks should run on the new threads from increasing max tasks.
+  for (size_t i = 0; i < kLocalMaxTasks; ++i) {
+    task_runner->PostTask(FROM_HERE,
+                          BindOnce(
+                              [](Closure* extra_threads_running_barrier,
+                                 WaitableEvent* extra_threads_continue) {
+                                extra_threads_running_barrier->Run();
+                                WaitWithoutBlockingObserver(
+                                    extra_threads_continue);
+                              },
+                              Unretained(&extra_threads_running_barrier),
+                              Unretained(&extra_threads_continue)));
+  }
+  extra_threads_running.Wait();
+
+  ASSERT_EQ(kLocalMaxTasks * 2, worker_pool.NumberOfWorkersForTesting());
+  EXPECT_EQ(kLocalMaxTasks * 2, worker_pool.GetMaxTasksForTesting());
+  blocked_call_continue.Signal();
+  extra_threads_continue.Signal();
+
+  // Periodically post tasks to ensure that posting tasks does not prevent
+  // workers that are idle due to the pool being over capacity from cleaning up.
+  for (int i = 0; i < 16; ++i) {
+    task_runner->PostDelayedTask(FROM_HERE, DoNothing(),
+                                 kReclaimTimeForCleanupTests * i * 0.5);
+  }
+
+  // Note: one worker above capacity will not get cleaned up since it's on the
+  // top of the idle stack.
+  worker_pool.WaitForWorkersCleanedUpForTesting(kLocalMaxTasks - 1);
+  EXPECT_EQ(kLocalMaxTasks + 1, worker_pool.NumberOfWorkersForTesting());
+
+  threads_continue.Signal();
+
+  worker_pool.JoinForTesting();
+}
+
+// Verify that the maximum number of workers is 256 and that hitting the max
+// leaves the pool in a valid state with regards to max tasks.
+TEST_F(TaskSchedulerWorkerPoolBlockingTest, MaximumWorkersTest) {
+  constexpr size_t kMaxNumberOfWorkers = 256;
+  constexpr size_t kNumExtraTasks = 10;
+
+  WaitableEvent early_blocking_threads_running;
+  RepeatingClosure early_threads_barrier_closure =
+      BarrierClosure(kMaxNumberOfWorkers,
+                     BindOnce(&WaitableEvent::Signal,
+                              Unretained(&early_blocking_threads_running)));
+
+  WaitableEvent early_threads_finished;
+  RepeatingClosure early_threads_finished_barrier = BarrierClosure(
+      kMaxNumberOfWorkers,
+      BindOnce(&WaitableEvent::Signal, Unretained(&early_threads_finished)));
+
+  WaitableEvent early_release_threads_continue;
+
+  // Post ScopedBlockingCall tasks to hit the worker cap.
+  for (size_t i = 0; i < kMaxNumberOfWorkers; ++i) {
+    task_runner_->PostTask(FROM_HERE,
+                           BindOnce(
+                               [](Closure* early_threads_barrier_closure,
+                                  WaitableEvent* early_release_threads_continue,
+                                  Closure* early_threads_finished) {
+                                 {
+                                   ScopedBlockingCall scoped_blocking_call(
+                                       BlockingType::WILL_BLOCK);
+                                   early_threads_barrier_closure->Run();
+                                   WaitWithoutBlockingObserver(
+                                       early_release_threads_continue);
+                                 }
+                                 early_threads_finished->Run();
+                               },
+                               Unretained(&early_threads_barrier_closure),
+                               Unretained(&early_release_threads_continue),
+                               Unretained(&early_threads_finished_barrier)));
+  }
+
+  early_blocking_threads_running.Wait();
+  EXPECT_EQ(worker_pool_->GetMaxTasksForTesting(),
+            kMaxTasks + kMaxNumberOfWorkers);
+
+  WaitableEvent late_release_thread_contine;
+  WaitableEvent late_blocking_threads_running;
+
+  RepeatingClosure late_threads_barrier_closure = BarrierClosure(
+      kNumExtraTasks, BindOnce(&WaitableEvent::Signal,
+                               Unretained(&late_blocking_threads_running)));
+
+  // Posts additional tasks. Note: we should already have |kMaxNumberOfWorkers|
+  // tasks running. These tasks should not be able to get executed yet as
+  // the pool is already at its max worker cap.
+  for (size_t i = 0; i < kNumExtraTasks; ++i) {
+    task_runner_->PostTask(
+        FROM_HERE,
+        BindOnce(
+            [](Closure* late_threads_barrier_closure,
+               WaitableEvent* late_release_thread_contine) {
+              ScopedBlockingCall scoped_blocking_call(BlockingType::WILL_BLOCK);
+              late_threads_barrier_closure->Run();
+              WaitWithoutBlockingObserver(late_release_thread_contine);
+            },
+            Unretained(&late_threads_barrier_closure),
+            Unretained(&late_release_thread_contine)));
+  }
+
+  // Give time to see if we exceed the max number of workers.
+  PlatformThread::Sleep(TestTimeouts::tiny_timeout());
+  EXPECT_LE(worker_pool_->NumberOfWorkersForTesting(), kMaxNumberOfWorkers);
+
+  early_release_threads_continue.Signal();
+  early_threads_finished.Wait();
+  late_blocking_threads_running.Wait();
+
+  WaitableEvent final_tasks_running;
+  WaitableEvent final_tasks_continue;
+  RepeatingClosure final_tasks_running_barrier = BarrierClosure(
+      kMaxTasks,
+      BindOnce(&WaitableEvent::Signal, Unretained(&final_tasks_running)));
+
+  // Verify that we are still able to saturate the pool.
+  for (size_t i = 0; i < kMaxTasks; ++i) {
+    task_runner_->PostTask(
+        FROM_HERE,
+        BindOnce(
+            [](Closure* closure, WaitableEvent* final_tasks_continue) {
+              closure->Run();
+              WaitWithoutBlockingObserver(final_tasks_continue);
+            },
+            Unretained(&final_tasks_running_barrier),
+            Unretained(&final_tasks_continue)));
+  }
+  final_tasks_running.Wait();
+  EXPECT_EQ(worker_pool_->GetMaxTasksForTesting(), kMaxTasks + kNumExtraTasks);
+  late_release_thread_contine.Signal();
+  final_tasks_continue.Signal();
+  task_tracker_.FlushForTesting();
+}
+
+// Verify that the maximum number of background tasks that can run concurrently
+// is honored.
+TEST_F(TaskSchedulerWorkerPoolImplStartInBodyTest, MaxBackgroundTasks) {
+  constexpr int kMaxBackgroundTasks = kMaxTasks / 2;
+  worker_pool_->Start(
+      SchedulerWorkerPoolParams(kMaxTasks, base::TimeDelta::Max()),
+      kMaxBackgroundTasks, service_thread_.task_runner(), nullptr,
+      SchedulerWorkerPoolImpl::WorkerEnvironment::NONE);
+  const scoped_refptr<TaskRunner> foreground_runner =
+      worker_pool_->CreateTaskRunnerWithTraits({MayBlock()});
+  const scoped_refptr<TaskRunner> background_runner =
+      worker_pool_->CreateTaskRunnerWithTraits(
+          {TaskPriority::BACKGROUND, MayBlock()});
+
+  // It should be possible to have |kMaxBackgroundTasks|
+  // TaskPriority::BACKGROUND tasks running concurrently.
+  WaitableEvent background_tasks_running;
+  WaitableEvent unblock_background_tasks;
+  RepeatingClosure background_tasks_running_barrier = BarrierClosure(
+      kMaxBackgroundTasks,
+      BindOnce(&WaitableEvent::Signal, Unretained(&background_tasks_running)));
+
+  for (int i = 0; i < kMaxBackgroundTasks; ++i) {
+    background_runner->PostTask(
+        FROM_HERE, base::BindLambdaForTesting([&]() {
+          background_tasks_running_barrier.Run();
+          WaitWithoutBlockingObserver(&unblock_background_tasks);
+        }));
+  }
+  background_tasks_running.Wait();
+
+  // No more TaskPriority::BACKGROUND task should run.
+  AtomicFlag extra_background_task_can_run;
+  WaitableEvent extra_background_task_running;
+  background_runner->PostTask(
+      FROM_HERE, base::BindLambdaForTesting([&]() {
+        EXPECT_TRUE(extra_background_task_can_run.IsSet());
+        extra_background_task_running.Signal();
+      }));
+
+  // An extra foreground task should be able to run.
+  WaitableEvent foreground_task_running;
+  foreground_runner->PostTask(
+      FROM_HERE, base::BindOnce(&WaitableEvent::Signal,
+                                Unretained(&foreground_task_running)));
+  foreground_task_running.Wait();
+
+  // Completion of the TaskPriority::BACKGROUND tasks should allow the extra
+  // TaskPriority::BACKGROUND task to run.
+  extra_background_task_can_run.Set();
+  unblock_background_tasks.Signal();
+  extra_background_task_running.Wait();
+
+  // Tear down.
+  task_tracker_.FlushForTesting();
+}
+
+namespace {
+
+class TaskSchedulerWorkerPoolBlockingCallAndMaxBackgroundTasksTest
+    : public TaskSchedulerWorkerPoolImplTestBase,
+      public testing::TestWithParam<BlockingType> {
+ public:
+  static constexpr int kMaxBackgroundTasks = kMaxTasks / 2;
+
+  TaskSchedulerWorkerPoolBlockingCallAndMaxBackgroundTasksTest() = default;
+
+  void SetUp() override {
+    CreateWorkerPool();
+    worker_pool_->Start(
+        SchedulerWorkerPoolParams(kMaxTasks, base::TimeDelta::Max()),
+        kMaxBackgroundTasks, service_thread_.task_runner(), nullptr,
+        SchedulerWorkerPoolImpl::WorkerEnvironment::NONE);
+  }
+
+  void TearDown() override {
+    TaskSchedulerWorkerPoolImplTestBase::CommonTearDown();
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(
+      TaskSchedulerWorkerPoolBlockingCallAndMaxBackgroundTasksTest);
+};
+
+}  // namespace
+
+TEST_P(TaskSchedulerWorkerPoolBlockingCallAndMaxBackgroundTasksTest,
+       BlockingCallAndMaxBackgroundTasksTest) {
+  const scoped_refptr<TaskRunner> background_runner =
+      worker_pool_->CreateTaskRunnerWithTraits(
+          {TaskPriority::BACKGROUND, MayBlock()});
+
+  // Post |kMaxBackgroundTasks| TaskPriority::BACKGROUND tasks that block in a
+  // ScopedBlockingCall.
+  WaitableEvent blocking_background_tasks_running;
+  WaitableEvent unblock_blocking_background_tasks;
+  RepeatingClosure blocking_background_tasks_running_barrier =
+      BarrierClosure(kMaxBackgroundTasks,
+                     BindOnce(&WaitableEvent::Signal,
+                              Unretained(&blocking_background_tasks_running)));
+  for (int i = 0; i < kMaxBackgroundTasks; ++i) {
+    background_runner->PostTask(
+        FROM_HERE, base::BindLambdaForTesting([&]() {
+          blocking_background_tasks_running_barrier.Run();
+          ScopedBlockingCall scoped_blocking_call(GetParam());
+          WaitWithoutBlockingObserver(&unblock_blocking_background_tasks);
+        }));
+  }
+  blocking_background_tasks_running.Wait();
+
+  // Post an extra |kMaxBackgroundTasks| TaskPriority::BACKGROUND tasks. They
+  // should be able to run, because the existing TaskPriority::BACKGROUND tasks
+  // are blocked within a ScopedBlockingCall.
+  //
+  // Note: We block the tasks until they have all started running to make sure
+  // that it is possible to run an extra |kMaxBackgroundTasks| concurrently.
+  WaitableEvent background_tasks_running;
+  WaitableEvent unblock_background_tasks;
+  RepeatingClosure background_tasks_running_barrier = BarrierClosure(
+      kMaxBackgroundTasks,
+      BindOnce(&WaitableEvent::Signal, Unretained(&background_tasks_running)));
+  for (int i = 0; i < kMaxBackgroundTasks; ++i) {
+    background_runner->PostTask(
+        FROM_HERE, base::BindLambdaForTesting([&]() {
+          background_tasks_running_barrier.Run();
+          WaitWithoutBlockingObserver(&unblock_background_tasks);
+        }));
+  }
+  background_tasks_running.Wait();
+
+  // Unblock all tasks and tear down.
+  unblock_blocking_background_tasks.Signal();
+  unblock_background_tasks.Signal();
+  task_tracker_.FlushForTesting();
+}
+
+INSTANTIATE_TEST_CASE_P(
+    MayBlock,
+    TaskSchedulerWorkerPoolBlockingCallAndMaxBackgroundTasksTest,
+    ::testing::Values(BlockingType::MAY_BLOCK));
+INSTANTIATE_TEST_CASE_P(
+    WillBlock,
+    TaskSchedulerWorkerPoolBlockingCallAndMaxBackgroundTasksTest,
+    ::testing::Values(BlockingType::WILL_BLOCK));
+
+// Verify that worker detachement doesn't race with worker cleanup, regression
+// test for https://crbug.com/810464.
+TEST_F(TaskSchedulerWorkerPoolImplStartInBodyTest, RacyCleanup) {
+#if defined(OS_FUCHSIA)
+  // Fuchsia + QEMU doesn't deal well with *many* threads being
+  // created/destroyed at once: https://crbug.com/816575.
+  constexpr size_t kLocalMaxTasks = 16;
+#else   // defined(OS_FUCHSIA)
+  constexpr size_t kLocalMaxTasks = 256;
+#endif  // defined(OS_FUCHSIA)
+  constexpr TimeDelta kReclaimTimeForRacyCleanupTest =
+      TimeDelta::FromMilliseconds(10);
+
+  worker_pool_->Start(
+      SchedulerWorkerPoolParams(kLocalMaxTasks, kReclaimTimeForRacyCleanupTest),
+      kLocalMaxTasks, service_thread_.task_runner(), nullptr,
+      SchedulerWorkerPoolImpl::WorkerEnvironment::NONE);
+
+  scoped_refptr<TaskRunner> task_runner =
+      worker_pool_->CreateTaskRunnerWithTraits({WithBaseSyncPrimitives()});
+
+  WaitableEvent threads_running;
+  WaitableEvent unblock_threads;
+  RepeatingClosure threads_running_barrier = BarrierClosure(
+      kLocalMaxTasks,
+      BindOnce(&WaitableEvent::Signal, Unretained(&threads_running)));
+
+  for (size_t i = 0; i < kLocalMaxTasks; ++i) {
+    task_runner->PostTask(
+        FROM_HERE,
+        BindOnce(
+            [](OnceClosure on_running, WaitableEvent* unblock_threads) {
+              std::move(on_running).Run();
+              WaitWithoutBlockingObserver(unblock_threads);
+            },
+            threads_running_barrier, Unretained(&unblock_threads)));
+  }
+
+  // Wait for all workers to be ready and release them all at once.
+  threads_running.Wait();
+  unblock_threads.Signal();
+
+  // Sleep to wakeup precisely when all workers are going to try to cleanup per
+  // being idle.
+  PlatformThread::Sleep(kReclaimTimeForRacyCleanupTest);
+
+  worker_pool_->JoinForTesting();
+
+  // Unwinding this test will be racy if worker cleanup can race with
+  // SchedulerWorkerPoolImpl destruction : https://crbug.com/810464.
+  worker_pool_.reset();
+}
+
+}  // namespace internal
+}  // namespace base
diff --git a/base/task_scheduler/scheduler_worker_stack_unittest.cc b/base/task_scheduler/scheduler_worker_stack_unittest.cc
new file mode 100644
index 0000000..83d693a
--- /dev/null
+++ b/base/task_scheduler/scheduler_worker_stack_unittest.cc
@@ -0,0 +1,254 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/task_scheduler/scheduler_worker_stack.h"
+
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted.h"
+#include "base/task_scheduler/scheduler_worker.h"
+#include "base/task_scheduler/sequence.h"
+#include "base/task_scheduler/task_tracker.h"
+#include "base/test/gtest_util.h"
+#include "base/threading/platform_thread.h"
+#include "base/time/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace internal {
+
+namespace {
+
+class MockSchedulerWorkerDelegate : public SchedulerWorker::Delegate {
+ public:
+  void OnCanScheduleSequence(scoped_refptr<Sequence> sequence) override {
+    ADD_FAILURE() << "Unexpected call to OnCanScheduleSequence().";
+  }
+  SchedulerWorker::ThreadLabel GetThreadLabel() const override {
+    return SchedulerWorker::ThreadLabel::DEDICATED;
+  }
+  void OnMainEntry(const SchedulerWorker* worker) override {}
+  scoped_refptr<Sequence> GetWork(SchedulerWorker* worker) override {
+    return nullptr;
+  }
+  void DidRunTask() override {
+    ADD_FAILURE() << "Unexpected call to DidRunTask()";
+  }
+  void ReEnqueueSequence(scoped_refptr<Sequence> sequence) override {
+    ADD_FAILURE() << "Unexpected call to ReEnqueueSequence()";
+  }
+  TimeDelta GetSleepTimeout() override {
+    return TimeDelta::Max();
+  }
+};
+
+class TaskSchedulerWorkerStackTest : public testing::Test {
+ protected:
+  void SetUp() override {
+    worker_a_ = MakeRefCounted<SchedulerWorker>(
+        ThreadPriority::NORMAL, WrapUnique(new MockSchedulerWorkerDelegate),
+        task_tracker_.GetTrackedRef());
+    ASSERT_TRUE(worker_a_);
+    worker_b_ = MakeRefCounted<SchedulerWorker>(
+        ThreadPriority::NORMAL, WrapUnique(new MockSchedulerWorkerDelegate),
+        task_tracker_.GetTrackedRef());
+    ASSERT_TRUE(worker_b_);
+    worker_c_ = MakeRefCounted<SchedulerWorker>(
+        ThreadPriority::NORMAL, WrapUnique(new MockSchedulerWorkerDelegate),
+        task_tracker_.GetTrackedRef());
+    ASSERT_TRUE(worker_c_);
+  }
+
+ private:
+  TaskTracker task_tracker_ = {"Test"};
+
+ protected:
+  scoped_refptr<SchedulerWorker> worker_a_;
+  scoped_refptr<SchedulerWorker> worker_b_;
+  scoped_refptr<SchedulerWorker> worker_c_;
+};
+
+}  // namespace
+
+// Verify that Push() and Pop() add/remove values in FIFO order.
+TEST_F(TaskSchedulerWorkerStackTest, PushPop) {
+  SchedulerWorkerStack stack;
+  EXPECT_EQ(nullptr, stack.Pop());
+
+  EXPECT_TRUE(stack.IsEmpty());
+  EXPECT_EQ(0U, stack.Size());
+
+  stack.Push(worker_a_.get());
+  EXPECT_FALSE(stack.IsEmpty());
+  EXPECT_EQ(1U, stack.Size());
+
+  stack.Push(worker_b_.get());
+  EXPECT_FALSE(stack.IsEmpty());
+  EXPECT_EQ(2U, stack.Size());
+
+  stack.Push(worker_c_.get());
+  EXPECT_FALSE(stack.IsEmpty());
+  EXPECT_EQ(3U, stack.Size());
+
+  EXPECT_EQ(worker_c_.get(), stack.Pop());
+  EXPECT_FALSE(stack.IsEmpty());
+  EXPECT_EQ(2U, stack.Size());
+
+  stack.Push(worker_c_.get());
+  EXPECT_FALSE(stack.IsEmpty());
+  EXPECT_EQ(3U, stack.Size());
+
+  EXPECT_EQ(worker_c_.get(), stack.Pop());
+  EXPECT_FALSE(stack.IsEmpty());
+  EXPECT_EQ(2U, stack.Size());
+
+  EXPECT_EQ(worker_b_.get(), stack.Pop());
+  EXPECT_FALSE(stack.IsEmpty());
+  EXPECT_EQ(1U, stack.Size());
+
+  EXPECT_EQ(worker_a_.get(), stack.Pop());
+  EXPECT_TRUE(stack.IsEmpty());
+  EXPECT_EQ(0U, stack.Size());
+
+  EXPECT_EQ(nullptr, stack.Pop());
+}
+
+// Verify that Peek() returns the correct values in FIFO order.
+TEST_F(TaskSchedulerWorkerStackTest, PeekPop) {
+  SchedulerWorkerStack stack;
+  EXPECT_EQ(nullptr, stack.Peek());
+
+  EXPECT_TRUE(stack.IsEmpty());
+  EXPECT_EQ(0U, stack.Size());
+
+  stack.Push(worker_a_.get());
+  EXPECT_EQ(worker_a_.get(), stack.Peek());
+  EXPECT_FALSE(stack.IsEmpty());
+  EXPECT_EQ(1U, stack.Size());
+
+  stack.Push(worker_b_.get());
+  EXPECT_EQ(worker_b_.get(), stack.Peek());
+  EXPECT_FALSE(stack.IsEmpty());
+  EXPECT_EQ(2U, stack.Size());
+
+  stack.Push(worker_c_.get());
+  EXPECT_EQ(worker_c_.get(), stack.Peek());
+  EXPECT_FALSE(stack.IsEmpty());
+  EXPECT_EQ(3U, stack.Size());
+
+  EXPECT_EQ(worker_c_.get(), stack.Pop());
+  EXPECT_EQ(worker_b_.get(), stack.Peek());
+  EXPECT_FALSE(stack.IsEmpty());
+  EXPECT_EQ(2U, stack.Size());
+
+  EXPECT_EQ(worker_b_.get(), stack.Pop());
+  EXPECT_EQ(worker_a_.get(), stack.Peek());
+  EXPECT_FALSE(stack.IsEmpty());
+  EXPECT_EQ(1U, stack.Size());
+
+  EXPECT_EQ(worker_a_.get(), stack.Pop());
+  EXPECT_TRUE(stack.IsEmpty());
+  EXPECT_EQ(0U, stack.Size());
+
+  EXPECT_EQ(nullptr, stack.Peek());
+}
+
+// Verify that Contains() returns true for workers on the stack.
+TEST_F(TaskSchedulerWorkerStackTest, Contains) {
+  SchedulerWorkerStack stack;
+  EXPECT_FALSE(stack.Contains(worker_a_.get()));
+  EXPECT_FALSE(stack.Contains(worker_b_.get()));
+  EXPECT_FALSE(stack.Contains(worker_c_.get()));
+
+  stack.Push(worker_a_.get());
+  EXPECT_TRUE(stack.Contains(worker_a_.get()));
+  EXPECT_FALSE(stack.Contains(worker_b_.get()));
+  EXPECT_FALSE(stack.Contains(worker_c_.get()));
+
+  stack.Push(worker_b_.get());
+  EXPECT_TRUE(stack.Contains(worker_a_.get()));
+  EXPECT_TRUE(stack.Contains(worker_b_.get()));
+  EXPECT_FALSE(stack.Contains(worker_c_.get()));
+
+  stack.Push(worker_c_.get());
+  EXPECT_TRUE(stack.Contains(worker_a_.get()));
+  EXPECT_TRUE(stack.Contains(worker_b_.get()));
+  EXPECT_TRUE(stack.Contains(worker_c_.get()));
+
+  stack.Pop();
+  EXPECT_TRUE(stack.Contains(worker_a_.get()));
+  EXPECT_TRUE(stack.Contains(worker_b_.get()));
+  EXPECT_FALSE(stack.Contains(worker_c_.get()));
+
+  stack.Pop();
+  EXPECT_TRUE(stack.Contains(worker_a_.get()));
+  EXPECT_FALSE(stack.Contains(worker_b_.get()));
+  EXPECT_FALSE(stack.Contains(worker_c_.get()));
+
+  stack.Pop();
+  EXPECT_FALSE(stack.Contains(worker_a_.get()));
+  EXPECT_FALSE(stack.Contains(worker_b_.get()));
+  EXPECT_FALSE(stack.Contains(worker_c_.get()));
+}
+
+// Verify that a value can be removed by Remove().
+TEST_F(TaskSchedulerWorkerStackTest, Remove) {
+  SchedulerWorkerStack stack;
+  EXPECT_TRUE(stack.IsEmpty());
+  EXPECT_EQ(0U, stack.Size());
+
+  stack.Push(worker_a_.get());
+  EXPECT_FALSE(stack.IsEmpty());
+  EXPECT_EQ(1U, stack.Size());
+
+  stack.Push(worker_b_.get());
+  EXPECT_FALSE(stack.IsEmpty());
+  EXPECT_EQ(2U, stack.Size());
+
+  stack.Push(worker_c_.get());
+  EXPECT_FALSE(stack.IsEmpty());
+  EXPECT_EQ(3U, stack.Size());
+
+  stack.Remove(worker_b_.get());
+  EXPECT_FALSE(stack.IsEmpty());
+  EXPECT_EQ(2U, stack.Size());
+
+  EXPECT_EQ(worker_c_.get(), stack.Pop());
+  EXPECT_FALSE(stack.IsEmpty());
+  EXPECT_EQ(1U, stack.Size());
+
+  EXPECT_EQ(worker_a_.get(), stack.Pop());
+  EXPECT_TRUE(stack.IsEmpty());
+  EXPECT_EQ(0U, stack.Size());
+}
+
+// Verify that a value can be pushed again after it has been removed.
+TEST_F(TaskSchedulerWorkerStackTest, PushAfterRemove) {
+  SchedulerWorkerStack stack;
+  EXPECT_EQ(0U, stack.Size());
+
+  stack.Push(worker_a_.get());
+  EXPECT_EQ(1U, stack.Size());
+
+  // Need to also push worker B for this test as it's illegal to Remove() the
+  // top of the stack.
+  stack.Push(worker_b_.get());
+  EXPECT_EQ(2U, stack.Size());
+
+  stack.Remove(worker_a_.get());
+  EXPECT_EQ(1U, stack.Size());
+
+  stack.Push(worker_a_.get());
+  EXPECT_EQ(2U, stack.Size());
+}
+
+// Verify that Push() DCHECKs when a value is inserted twice.
+TEST_F(TaskSchedulerWorkerStackTest, PushTwice) {
+  SchedulerWorkerStack stack;
+  stack.Push(worker_a_.get());
+  EXPECT_DCHECK_DEATH({ stack.Push(worker_a_.get()); });
+}
+
+}  // namespace internal
+}  // namespace base
diff --git a/base/task_scheduler/scheduler_worker_unittest.cc b/base/task_scheduler/scheduler_worker_unittest.cc
new file mode 100644
index 0000000..1112f55
--- /dev/null
+++ b/base/task_scheduler/scheduler_worker_unittest.cc
@@ -0,0 +1,897 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/task_scheduler/scheduler_worker.h"
+
+#include <stddef.h>
+
+#include <memory>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted.h"
+#include "base/synchronization/condition_variable.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/task_scheduler/environment_config.h"
+#include "base/task_scheduler/scheduler_lock.h"
+#include "base/task_scheduler/scheduler_worker_observer.h"
+#include "base/task_scheduler/sequence.h"
+#include "base/task_scheduler/task.h"
+#include "base/task_scheduler/task_tracker.h"
+#include "base/task_scheduler/test_utils.h"
+#include "base/test/test_timeouts.h"
+#include "base/threading/platform_thread.h"
+#include "base/threading/simple_thread.h"
+#include "base/time/time.h"
+#include "build/build_config.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if defined(OS_WIN)
+#include <objbase.h>
+
+#include "base/win/com_init_check_hook.h"
+#endif
+
+using testing::_;
+using testing::Mock;
+using testing::Ne;
+using testing::StrictMock;
+
+namespace base {
+namespace internal {
+namespace {
+
+const size_t kNumSequencesPerTest = 150;
+
+class SchedulerWorkerDefaultDelegate : public SchedulerWorker::Delegate {
+ public:
+  SchedulerWorkerDefaultDelegate() = default;
+
+  // SchedulerWorker::Delegate:
+  void OnCanScheduleSequence(scoped_refptr<Sequence> sequence) override {
+    ADD_FAILURE() << "Unexpected call to OnCanScheduleSequence().";
+  }
+  SchedulerWorker::ThreadLabel GetThreadLabel() const override {
+    return SchedulerWorker::ThreadLabel::DEDICATED;
+  }
+  void OnMainEntry(const SchedulerWorker* worker) override {}
+  scoped_refptr<Sequence> GetWork(SchedulerWorker* worker) override {
+    return nullptr;
+  }
+  void DidRunTask() override {
+    ADD_FAILURE() << "Unexpected call to DidRunTask()";
+  }
+  void ReEnqueueSequence(scoped_refptr<Sequence> sequence) override {
+    ADD_FAILURE() << "Unexpected call to ReEnqueueSequence()";
+  }
+  TimeDelta GetSleepTimeout() override { return TimeDelta::Max(); }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SchedulerWorkerDefaultDelegate);
+};
+
+// The test parameter is the number of Tasks per Sequence returned by GetWork().
+class TaskSchedulerWorkerTest : public testing::TestWithParam<size_t> {
+ protected:
+  TaskSchedulerWorkerTest()
+      : num_get_work_cv_(lock_.CreateConditionVariable()) {}
+
+  void SetUp() override {
+    worker_ = MakeRefCounted<SchedulerWorker>(
+        ThreadPriority::NORMAL,
+        std::make_unique<TestSchedulerWorkerDelegate>(this),
+        task_tracker_.GetTrackedRef());
+    ASSERT_TRUE(worker_);
+    worker_->Start();
+    worker_set_.Signal();
+    main_entry_called_.Wait();
+  }
+
+  void TearDown() override {
+    // |worker_| needs to be released before ~TaskTracker() as it holds a
+    // TrackedRef to it.
+    worker_->JoinForTesting();
+    worker_ = nullptr;
+  }
+
+  size_t TasksPerSequence() const { return GetParam(); }
+
+  // Wait until GetWork() has been called |num_get_work| times.
+  void WaitForNumGetWork(size_t num_get_work) {
+    AutoSchedulerLock auto_lock(lock_);
+    while (num_get_work_ < num_get_work)
+      num_get_work_cv_->Wait();
+  }
+
+  void SetMaxGetWork(size_t max_get_work) {
+    AutoSchedulerLock auto_lock(lock_);
+    max_get_work_ = max_get_work;
+  }
+
+  void SetNumSequencesToCreate(size_t num_sequences_to_create) {
+    AutoSchedulerLock auto_lock(lock_);
+    EXPECT_EQ(0U, num_sequences_to_create_);
+    num_sequences_to_create_ = num_sequences_to_create;
+  }
+
+  size_t NumRunTasks() {
+    AutoSchedulerLock auto_lock(lock_);
+    return num_run_tasks_;
+  }
+
+  std::vector<scoped_refptr<Sequence>> CreatedSequences() {
+    AutoSchedulerLock auto_lock(lock_);
+    return created_sequences_;
+  }
+
+  std::vector<scoped_refptr<Sequence>> EnqueuedSequences() {
+    AutoSchedulerLock auto_lock(lock_);
+    return re_enqueued_sequences_;
+  }
+
+  scoped_refptr<SchedulerWorker> worker_;
+
+ private:
+  class TestSchedulerWorkerDelegate : public SchedulerWorkerDefaultDelegate {
+   public:
+    TestSchedulerWorkerDelegate(TaskSchedulerWorkerTest* outer)
+        : outer_(outer) {}
+
+    ~TestSchedulerWorkerDelegate() override {
+      EXPECT_FALSE(IsCallToDidRunTaskExpected());
+    }
+
+    // SchedulerWorker::Delegate:
+    void OnMainEntry(const SchedulerWorker* worker) override {
+      outer_->worker_set_.Wait();
+      EXPECT_EQ(outer_->worker_.get(), worker);
+      EXPECT_FALSE(IsCallToDidRunTaskExpected());
+
+      // Without synchronization, OnMainEntry() could be called twice without
+      // generating an error.
+      AutoSchedulerLock auto_lock(outer_->lock_);
+      EXPECT_FALSE(outer_->main_entry_called_.IsSignaled());
+      outer_->main_entry_called_.Signal();
+    }
+
+    scoped_refptr<Sequence> GetWork(SchedulerWorker* worker) override {
+      EXPECT_FALSE(IsCallToDidRunTaskExpected());
+      EXPECT_EQ(outer_->worker_.get(), worker);
+
+      {
+        AutoSchedulerLock auto_lock(outer_->lock_);
+
+        // Increment the number of times that this method has been called.
+        ++outer_->num_get_work_;
+        outer_->num_get_work_cv_->Signal();
+
+        // Verify that this method isn't called more times than expected.
+        EXPECT_LE(outer_->num_get_work_, outer_->max_get_work_);
+
+        // Check if a Sequence should be returned.
+        if (outer_->num_sequences_to_create_ == 0)
+          return nullptr;
+        --outer_->num_sequences_to_create_;
+      }
+
+      // Create a Sequence with TasksPerSequence() Tasks.
+      scoped_refptr<Sequence> sequence(new Sequence);
+      for (size_t i = 0; i < outer_->TasksPerSequence(); ++i) {
+        Task task(FROM_HERE,
+                  BindOnce(&TaskSchedulerWorkerTest::RunTaskCallback,
+                           Unretained(outer_)),
+                  TaskTraits(), TimeDelta());
+        EXPECT_TRUE(outer_->task_tracker_.WillPostTask(&task));
+        sequence->PushTask(std::move(task));
+      }
+
+      ExpectCallToDidRunTask();
+
+      {
+        // Add the Sequence to the vector of created Sequences.
+        AutoSchedulerLock auto_lock(outer_->lock_);
+        outer_->created_sequences_.push_back(sequence);
+      }
+
+      sequence = outer_->task_tracker_.WillScheduleSequence(std::move(sequence),
+                                                            nullptr);
+      EXPECT_TRUE(sequence);
+      return sequence;
+    }
+
+    void DidRunTask() override {
+      AutoSchedulerLock auto_lock(expect_did_run_task_lock_);
+      EXPECT_TRUE(expect_did_run_task_);
+      expect_did_run_task_ = false;
+    }
+
+    // This override verifies that |sequence| contains the expected number of
+    // Tasks and adds it to |enqueued_sequences_|. Unlike a normal
+    // EnqueueSequence implementation, it doesn't reinsert |sequence| into a
+    // queue for further execution.
+    void ReEnqueueSequence(scoped_refptr<Sequence> sequence) override {
+      EXPECT_FALSE(IsCallToDidRunTaskExpected());
+      EXPECT_GT(outer_->TasksPerSequence(), 1U);
+
+      // Verify that |sequence| contains TasksPerSequence() - 1 Tasks.
+      for (size_t i = 0; i < outer_->TasksPerSequence() - 1; ++i) {
+        EXPECT_TRUE(sequence->TakeTask());
+        EXPECT_EQ(i == outer_->TasksPerSequence() - 2, sequence->Pop());
+      }
+
+      // Add |sequence| to |re_enqueued_sequences_|.
+      AutoSchedulerLock auto_lock(outer_->lock_);
+      outer_->re_enqueued_sequences_.push_back(std::move(sequence));
+      EXPECT_LE(outer_->re_enqueued_sequences_.size(),
+                outer_->created_sequences_.size());
+    }
+
+   private:
+    // Expect a call to DidRunTask() before the next call to any other method of
+    // this delegate.
+    void ExpectCallToDidRunTask() {
+      AutoSchedulerLock auto_lock(expect_did_run_task_lock_);
+      expect_did_run_task_ = true;
+    }
+
+    bool IsCallToDidRunTaskExpected() const {
+      AutoSchedulerLock auto_lock(expect_did_run_task_lock_);
+      return expect_did_run_task_;
+    }
+
+    TaskSchedulerWorkerTest* outer_;
+
+    // Synchronizes access to |expect_did_run_task_|.
+    mutable SchedulerLock expect_did_run_task_lock_;
+
+    // Whether the next method called on this delegate should be DidRunTask().
+    bool expect_did_run_task_ = false;
+
+    DISALLOW_COPY_AND_ASSIGN(TestSchedulerWorkerDelegate);
+  };
+
+  void RunTaskCallback() {
+    AutoSchedulerLock auto_lock(lock_);
+    ++num_run_tasks_;
+    EXPECT_LE(num_run_tasks_, created_sequences_.size());
+  }
+
+  TaskTracker task_tracker_ = {"Test"};
+
+  // Synchronizes access to all members below.
+  mutable SchedulerLock lock_;
+
+  // Signaled once OnMainEntry() has been called.
+  WaitableEvent main_entry_called_;
+
+  // Number of Sequences that should be created by GetWork(). When this
+  // is 0, GetWork() returns nullptr.
+  size_t num_sequences_to_create_ = 0;
+
+  // Number of times that GetWork() has been called.
+  size_t num_get_work_ = 0;
+
+  // Maximum number of times that GetWork() can be called.
+  size_t max_get_work_ = 0;
+
+  // Condition variable signaled when |num_get_work_| is incremented.
+  std::unique_ptr<ConditionVariable> num_get_work_cv_;
+
+  // Sequences created by GetWork().
+  std::vector<scoped_refptr<Sequence>> created_sequences_;
+
+  // Sequences passed to EnqueueSequence().
+  std::vector<scoped_refptr<Sequence>> re_enqueued_sequences_;
+
+  // Number of times that RunTaskCallback() has been called.
+  size_t num_run_tasks_ = 0;
+
+  // Signaled after |worker_| is set.
+  WaitableEvent worker_set_;
+
+  DISALLOW_COPY_AND_ASSIGN(TaskSchedulerWorkerTest);
+};
+
+}  // namespace
+
+// Verify that when GetWork() continuously returns Sequences, all Tasks in these
+// Sequences run successfully. The test wakes up the SchedulerWorker once.
+TEST_P(TaskSchedulerWorkerTest, ContinuousWork) {
+  // Set GetWork() to return |kNumSequencesPerTest| Sequences before starting to
+  // return nullptr.
+  SetNumSequencesToCreate(kNumSequencesPerTest);
+
+  // Expect |kNumSequencesPerTest| calls to GetWork() in which it returns a
+  // Sequence and one call in which its returns nullptr.
+  const size_t kExpectedNumGetWork = kNumSequencesPerTest + 1;
+  SetMaxGetWork(kExpectedNumGetWork);
+
+  // Wake up |worker_| and wait until GetWork() has been invoked the
+  // expected amount of times.
+  worker_->WakeUp();
+  WaitForNumGetWork(kExpectedNumGetWork);
+
+  // All tasks should have run.
+  EXPECT_EQ(kNumSequencesPerTest, NumRunTasks());
+
+  // If Sequences returned by GetWork() contain more than one Task, they aren't
+  // empty after the worker pops Tasks from them and thus should be returned to
+  // EnqueueSequence().
+  if (TasksPerSequence() > 1)
+    EXPECT_EQ(CreatedSequences(), EnqueuedSequences());
+  else
+    EXPECT_TRUE(EnqueuedSequences().empty());
+}
+
+// Verify that when GetWork() alternates between returning a Sequence and
+// returning nullptr, all Tasks in the returned Sequences run successfully. The
+// test wakes up the SchedulerWorker once for each Sequence.
+TEST_P(TaskSchedulerWorkerTest, IntermittentWork) {
+  for (size_t i = 0; i < kNumSequencesPerTest; ++i) {
+    // Set GetWork() to return 1 Sequence before starting to return
+    // nullptr.
+    SetNumSequencesToCreate(1);
+
+    // Expect |i + 1| calls to GetWork() in which it returns a Sequence and
+    // |i + 1| calls in which it returns nullptr.
+    const size_t expected_num_get_work = 2 * (i + 1);
+    SetMaxGetWork(expected_num_get_work);
+
+    // Wake up |worker_| and wait until GetWork() has been invoked
+    // the expected amount of times.
+    worker_->WakeUp();
+    WaitForNumGetWork(expected_num_get_work);
+
+    // The Task should have run
+    EXPECT_EQ(i + 1, NumRunTasks());
+
+    // If Sequences returned by GetWork() contain more than one Task, they
+    // aren't empty after the worker pops Tasks from them and thus should be
+    // returned to EnqueueSequence().
+    if (TasksPerSequence() > 1)
+      EXPECT_EQ(CreatedSequences(), EnqueuedSequences());
+    else
+      EXPECT_TRUE(EnqueuedSequences().empty());
+  }
+}
+
+INSTANTIATE_TEST_CASE_P(OneTaskPerSequence,
+                        TaskSchedulerWorkerTest,
+                        ::testing::Values(1));
+INSTANTIATE_TEST_CASE_P(TwoTasksPerSequence,
+                        TaskSchedulerWorkerTest,
+                        ::testing::Values(2));
+
+namespace {
+
+class ControllableCleanupDelegate : public SchedulerWorkerDefaultDelegate {
+ public:
+  class Controls : public RefCountedThreadSafe<Controls> {
+   public:
+    Controls() = default;
+
+    void HaveWorkBlock() { work_running_.Reset(); }
+
+    void UnblockWork() { work_running_.Signal(); }
+
+    void WaitForWorkToRun() { work_processed_.Wait(); }
+
+    void WaitForCleanupRequest() { cleanup_requested_.Wait(); }
+
+    void WaitForDelegateDestroy() { destroyed_.Wait(); }
+
+    void WaitForMainExit() { exited_.Wait(); }
+
+    void set_expect_get_work(bool expect_get_work) {
+      expect_get_work_ = expect_get_work;
+    }
+
+    void ResetState() {
+      work_running_.Signal();
+      work_processed_.Reset();
+      cleanup_requested_.Reset();
+      exited_.Reset();
+      work_requested_ = false;
+    }
+
+    void set_can_cleanup(bool can_cleanup) { can_cleanup_ = can_cleanup; }
+
+   private:
+    friend class ControllableCleanupDelegate;
+    friend class RefCountedThreadSafe<Controls>;
+    ~Controls() = default;
+
+    WaitableEvent work_running_{WaitableEvent::ResetPolicy::MANUAL,
+                                WaitableEvent::InitialState::SIGNALED};
+    WaitableEvent work_processed_;
+    WaitableEvent cleanup_requested_;
+    WaitableEvent destroyed_;
+    WaitableEvent exited_;
+
+    bool expect_get_work_ = true;
+    bool can_cleanup_ = false;
+    bool work_requested_ = false;
+
+    DISALLOW_COPY_AND_ASSIGN(Controls);
+  };
+
+  ControllableCleanupDelegate(TaskTracker* task_tracker)
+      : task_tracker_(task_tracker), controls_(new Controls()) {}
+
+  ~ControllableCleanupDelegate() override { controls_->destroyed_.Signal(); }
+
+  scoped_refptr<Sequence> GetWork(SchedulerWorker* worker)
+      override {
+    EXPECT_TRUE(controls_->expect_get_work_);
+
+    // Sends one item of work to signal |work_processed_|. On subsequent calls,
+    // sends nullptr to indicate there's no more work to be done.
+    if (controls_->work_requested_) {
+      if (CanCleanup(worker)) {
+        OnCleanup();
+        worker->Cleanup();
+        controls_->set_expect_get_work(false);
+      }
+      return nullptr;
+    }
+
+    controls_->work_requested_ = true;
+    scoped_refptr<Sequence> sequence(new Sequence);
+    Task task(
+        FROM_HERE,
+        BindOnce(
+            [](WaitableEvent* work_processed, WaitableEvent* work_running) {
+              work_processed->Signal();
+              work_running->Wait();
+            },
+            Unretained(&controls_->work_processed_),
+            Unretained(&controls_->work_running_)),
+        {WithBaseSyncPrimitives(), TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
+        TimeDelta());
+    EXPECT_TRUE(task_tracker_->WillPostTask(&task));
+    sequence->PushTask(std::move(task));
+    sequence =
+        task_tracker_->WillScheduleSequence(std::move(sequence), nullptr);
+    EXPECT_TRUE(sequence);
+    return sequence;
+  }
+
+  void DidRunTask() override {}
+
+  void OnMainExit(SchedulerWorker* worker) override {
+    controls_->exited_.Signal();
+  }
+
+  bool CanCleanup(SchedulerWorker* worker) {
+    // Saving |can_cleanup_| now so that callers waiting on |cleanup_requested_|
+    // have the thread go to sleep and then allow timing out.
+    bool can_cleanup = controls_->can_cleanup_;
+    controls_->cleanup_requested_.Signal();
+    return can_cleanup;
+  }
+
+  void OnCleanup() {
+    EXPECT_TRUE(controls_->can_cleanup_);
+    EXPECT_TRUE(controls_->cleanup_requested_.IsSignaled());
+  }
+
+  // ControllableCleanupDelegate:
+  scoped_refptr<Controls> controls() { return controls_; }
+
+ private:
+  scoped_refptr<Sequence> work_sequence_;
+  TaskTracker* const task_tracker_;
+  scoped_refptr<Controls> controls_;
+
+  DISALLOW_COPY_AND_ASSIGN(ControllableCleanupDelegate);
+};
+
+class MockedControllableCleanupDelegate : public ControllableCleanupDelegate {
+ public:
+  MockedControllableCleanupDelegate(TaskTracker* task_tracker)
+      : ControllableCleanupDelegate(task_tracker){};
+  ~MockedControllableCleanupDelegate() override = default;
+
+  // SchedulerWorker::Delegate:
+  MOCK_METHOD1(OnMainEntry, void(const SchedulerWorker* worker));
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockedControllableCleanupDelegate);
+};
+
+}  // namespace
+
+// Verify that calling SchedulerWorker::Cleanup() from GetWork() causes
+// the SchedulerWorker's thread to exit.
+TEST(TaskSchedulerWorkerTest, WorkerCleanupFromGetWork) {
+  TaskTracker task_tracker("Test");
+  // Will be owned by SchedulerWorker.
+  MockedControllableCleanupDelegate* delegate =
+      new StrictMock<MockedControllableCleanupDelegate>(&task_tracker);
+  scoped_refptr<ControllableCleanupDelegate::Controls> controls =
+      delegate->controls();
+  controls->set_can_cleanup(true);
+  EXPECT_CALL(*delegate, OnMainEntry(_));
+  auto worker = MakeRefCounted<SchedulerWorker>(ThreadPriority::NORMAL,
+                                                WrapUnique(delegate),
+                                                task_tracker.GetTrackedRef());
+  worker->Start();
+  worker->WakeUp();
+  controls->WaitForWorkToRun();
+  Mock::VerifyAndClear(delegate);
+  controls->WaitForMainExit();
+}
+
+TEST(TaskSchedulerWorkerTest, WorkerCleanupDuringWork) {
+  TaskTracker task_tracker("Test");
+  // Will be owned by SchedulerWorker.
+  // No mock here as that's reasonably covered by other tests and the delegate
+  // may destroy on a different thread. Mocks aren't designed with that in mind.
+  std::unique_ptr<ControllableCleanupDelegate> delegate =
+      std::make_unique<ControllableCleanupDelegate>(&task_tracker);
+  scoped_refptr<ControllableCleanupDelegate::Controls> controls =
+      delegate->controls();
+
+  controls->HaveWorkBlock();
+
+  auto worker = MakeRefCounted<SchedulerWorker>(ThreadPriority::NORMAL,
+                                                std::move(delegate),
+                                                task_tracker.GetTrackedRef());
+  worker->Start();
+  worker->WakeUp();
+
+  controls->WaitForWorkToRun();
+  worker->Cleanup();
+  worker = nullptr;
+  controls->UnblockWork();
+  controls->WaitForDelegateDestroy();
+}
+
+TEST(TaskSchedulerWorkerTest, WorkerCleanupDuringWait) {
+  TaskTracker task_tracker("Test");
+  // Will be owned by SchedulerWorker.
+  // No mock here as that's reasonably covered by other tests and the delegate
+  // may destroy on a different thread. Mocks aren't designed with that in mind.
+  std::unique_ptr<ControllableCleanupDelegate> delegate =
+      std::make_unique<ControllableCleanupDelegate>(&task_tracker);
+  scoped_refptr<ControllableCleanupDelegate::Controls> controls =
+      delegate->controls();
+
+  auto worker = MakeRefCounted<SchedulerWorker>(ThreadPriority::NORMAL,
+                                                std::move(delegate),
+                                                task_tracker.GetTrackedRef());
+  worker->Start();
+  worker->WakeUp();
+
+  controls->WaitForCleanupRequest();
+  worker->Cleanup();
+  worker = nullptr;
+  controls->WaitForDelegateDestroy();
+}
+
+TEST(TaskSchedulerWorkerTest, WorkerCleanupDuringShutdown) {
+  TaskTracker task_tracker("Test");
+  // Will be owned by SchedulerWorker.
+  // No mock here as that's reasonably covered by other tests and the delegate
+  // may destroy on a different thread. Mocks aren't designed with that in mind.
+  std::unique_ptr<ControllableCleanupDelegate> delegate =
+      std::make_unique<ControllableCleanupDelegate>(&task_tracker);
+  scoped_refptr<ControllableCleanupDelegate::Controls> controls =
+      delegate->controls();
+
+  controls->HaveWorkBlock();
+
+  auto worker = MakeRefCounted<SchedulerWorker>(ThreadPriority::NORMAL,
+                                                std::move(delegate),
+                                                task_tracker.GetTrackedRef());
+  worker->Start();
+  worker->WakeUp();
+
+  controls->WaitForWorkToRun();
+  task_tracker.Shutdown();
+  worker->Cleanup();
+  worker = nullptr;
+  controls->UnblockWork();
+  controls->WaitForDelegateDestroy();
+}
+
+// Verify that Start() is a no-op after Cleanup().
+TEST(TaskSchedulerWorkerTest, CleanupBeforeStart) {
+  TaskTracker task_tracker("Test");
+  // Will be owned by SchedulerWorker.
+  // No mock here as that's reasonably covered by other tests and the delegate
+  // may destroy on a different thread. Mocks aren't designed with that in mind.
+  std::unique_ptr<ControllableCleanupDelegate> delegate =
+      std::make_unique<ControllableCleanupDelegate>(&task_tracker);
+  scoped_refptr<ControllableCleanupDelegate::Controls> controls =
+      delegate->controls();
+  controls->set_expect_get_work(false);
+
+  auto worker = MakeRefCounted<SchedulerWorker>(ThreadPriority::NORMAL,
+                                                std::move(delegate),
+                                                task_tracker.GetTrackedRef());
+
+  worker->Cleanup();
+  worker->Start();
+
+  EXPECT_FALSE(worker->ThreadAliveForTesting());
+}
+
+namespace {
+
+class CallJoinFromDifferentThread : public SimpleThread {
+ public:
+  CallJoinFromDifferentThread(SchedulerWorker* worker_to_join)
+      : SimpleThread("SchedulerWorkerJoinThread"),
+        worker_to_join_(worker_to_join) {}
+
+  ~CallJoinFromDifferentThread() override = default;
+
+  void Run() override {
+    run_started_event_.Signal();
+    worker_to_join_->JoinForTesting();
+  }
+
+  void WaitForRunToStart() { run_started_event_.Wait(); }
+
+ private:
+  SchedulerWorker* const worker_to_join_;
+  WaitableEvent run_started_event_;
+  DISALLOW_COPY_AND_ASSIGN(CallJoinFromDifferentThread);
+};
+
+}  // namespace
+
+TEST(TaskSchedulerWorkerTest, WorkerCleanupDuringJoin) {
+  TaskTracker task_tracker("Test");
+  // Will be owned by SchedulerWorker.
+  // No mock here as that's reasonably covered by other tests and the
+  // delegate may destroy on a different thread. Mocks aren't designed with that
+  // in mind.
+  std::unique_ptr<ControllableCleanupDelegate> delegate =
+      std::make_unique<ControllableCleanupDelegate>(&task_tracker);
+  scoped_refptr<ControllableCleanupDelegate::Controls> controls =
+      delegate->controls();
+
+  controls->HaveWorkBlock();
+
+  auto worker = MakeRefCounted<SchedulerWorker>(ThreadPriority::NORMAL,
+                                                std::move(delegate),
+                                                task_tracker.GetTrackedRef());
+  worker->Start();
+  worker->WakeUp();
+
+  controls->WaitForWorkToRun();
+  CallJoinFromDifferentThread join_from_different_thread(worker.get());
+  join_from_different_thread.Start();
+  join_from_different_thread.WaitForRunToStart();
+  // Sleep here to give the other thread a chance to call JoinForTesting().
+  // Receiving a signal that Run() was called doesn't mean JoinForTesting() was
+  // necessarily called, and we can't signal after JoinForTesting() as
+  // JoinForTesting() blocks until we call UnblockWork().
+  PlatformThread::Sleep(TestTimeouts::tiny_timeout());
+  worker->Cleanup();
+  worker = nullptr;
+  controls->UnblockWork();
+  controls->WaitForDelegateDestroy();
+  join_from_different_thread.Join();
+}
+
+namespace {
+
+class ExpectThreadPriorityDelegate : public SchedulerWorkerDefaultDelegate {
+ public:
+  ExpectThreadPriorityDelegate()
+      : priority_verified_in_get_work_event_(
+            WaitableEvent::ResetPolicy::AUTOMATIC,
+            WaitableEvent::InitialState::NOT_SIGNALED),
+        expected_thread_priority_(ThreadPriority::BACKGROUND) {}
+
+  void SetExpectedThreadPriority(ThreadPriority expected_thread_priority) {
+    expected_thread_priority_ = expected_thread_priority;
+  }
+
+  void WaitForPriorityVerifiedInGetWork() {
+    priority_verified_in_get_work_event_.Wait();
+  }
+
+  // SchedulerWorker::Delegate:
+  void OnMainEntry(const SchedulerWorker* worker) override {
+    VerifyThreadPriority();
+  }
+  scoped_refptr<Sequence> GetWork(SchedulerWorker* worker) override {
+    VerifyThreadPriority();
+    priority_verified_in_get_work_event_.Signal();
+    return nullptr;
+  }
+
+ private:
+  void VerifyThreadPriority() {
+    AutoSchedulerLock auto_lock(expected_thread_priority_lock_);
+    EXPECT_EQ(expected_thread_priority_,
+              PlatformThread::GetCurrentThreadPriority());
+  }
+
+  // Signaled after GetWork() has verified the priority of the worker thread.
+  WaitableEvent priority_verified_in_get_work_event_;
+
+  // Synchronizes access to |expected_thread_priority_|.
+  SchedulerLock expected_thread_priority_lock_;
+
+  // Expected thread priority for the next call to OnMainEntry() or GetWork().
+  ThreadPriority expected_thread_priority_;
+
+  DISALLOW_COPY_AND_ASSIGN(ExpectThreadPriorityDelegate);
+};
+
+}  // namespace
+
+TEST(TaskSchedulerWorkerTest, BumpPriorityOfAliveThreadDuringShutdown) {
+  if (!CanUseBackgroundPriorityForSchedulerWorker())
+    return;
+
+  TaskTracker task_tracker("Test");
+
+  std::unique_ptr<ExpectThreadPriorityDelegate> delegate(
+      new ExpectThreadPriorityDelegate);
+  ExpectThreadPriorityDelegate* delegate_raw = delegate.get();
+  delegate_raw->SetExpectedThreadPriority(ThreadPriority::BACKGROUND);
+  auto worker = MakeRefCounted<SchedulerWorker>(ThreadPriority::BACKGROUND,
+                                                std::move(delegate),
+                                                task_tracker.GetTrackedRef());
+  worker->Start();
+
+  // Verify that the initial thread priority is BACKGROUND (or NORMAL if thread
+  // priority can't be increased).
+  worker->WakeUp();
+  delegate_raw->WaitForPriorityVerifiedInGetWork();
+
+  // Verify that the thread priority is bumped to NORMAL during shutdown.
+  delegate_raw->SetExpectedThreadPriority(ThreadPriority::NORMAL);
+  task_tracker.SetHasShutdownStartedForTesting();
+  worker->WakeUp();
+  delegate_raw->WaitForPriorityVerifiedInGetWork();
+
+  worker->JoinForTesting();
+}
+
+namespace {
+
+class VerifyCallsToObserverDelegate : public SchedulerWorkerDefaultDelegate {
+ public:
+  VerifyCallsToObserverDelegate(test::MockSchedulerWorkerObserver* observer)
+      : observer_(observer) {}
+
+  // SchedulerWorker::Delegate:
+  void OnMainEntry(const SchedulerWorker* worker) override {
+    Mock::VerifyAndClear(observer_);
+  }
+
+  void OnMainExit(SchedulerWorker* worker) override {
+    EXPECT_CALL(*observer_, OnSchedulerWorkerMainExit());
+  }
+
+ private:
+  test::MockSchedulerWorkerObserver* const observer_;
+
+  DISALLOW_COPY_AND_ASSIGN(VerifyCallsToObserverDelegate);
+};
+
+}  // namespace
+
+// Flaky: crbug.com/846121
+#if defined(OS_LINUX) && defined(ADDRESS_SANITIZER)
+#define MAYBE_SchedulerWorkerObserver DISABLED_SchedulerWorkerObserver
+#else
+#define MAYBE_SchedulerWorkerObserver SchedulerWorkerObserver
+#endif
+
+// Verify that the SchedulerWorkerObserver is notified when the worker enters
+// and exits its main function.
+TEST(TaskSchedulerWorkerTest, MAYBE_SchedulerWorkerObserver) {
+  StrictMock<test::MockSchedulerWorkerObserver> observer;
+  {
+    TaskTracker task_tracker("Test");
+    auto delegate = std::make_unique<VerifyCallsToObserverDelegate>(&observer);
+    auto worker = MakeRefCounted<SchedulerWorker>(ThreadPriority::NORMAL,
+                                                  std::move(delegate),
+                                                  task_tracker.GetTrackedRef());
+
+    EXPECT_CALL(observer, OnSchedulerWorkerMainEntry());
+    worker->Start(&observer);
+    worker->Cleanup();
+    worker = nullptr;
+  }
+  Mock::VerifyAndClear(&observer);
+}
+
+#if defined(OS_WIN)
+
+namespace {
+
+class CoInitializeDelegate : public SchedulerWorkerDefaultDelegate {
+ public:
+  CoInitializeDelegate() = default;
+
+  scoped_refptr<Sequence> GetWork(SchedulerWorker* worker) override {
+    EXPECT_FALSE(get_work_returned_.IsSignaled());
+    EXPECT_EQ(E_UNEXPECTED, coinitialize_hresult_);
+
+    coinitialize_hresult_ = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
+    if (SUCCEEDED(coinitialize_hresult_))
+      CoUninitialize();
+
+    get_work_returned_.Signal();
+    return nullptr;
+  }
+
+  void WaitUntilGetWorkReturned() { get_work_returned_.Wait(); }
+
+  HRESULT coinitialize_hresult() const { return coinitialize_hresult_; }
+
+ private:
+  WaitableEvent get_work_returned_;
+  HRESULT coinitialize_hresult_ = E_UNEXPECTED;
+
+  DISALLOW_COPY_AND_ASSIGN(CoInitializeDelegate);
+};
+
+}  // namespace
+
+TEST(TaskSchedulerWorkerTest, BackwardCompatibilityEnabled) {
+  TaskTracker task_tracker("Test");
+  auto delegate = std::make_unique<CoInitializeDelegate>();
+  CoInitializeDelegate* const delegate_raw = delegate.get();
+
+  // Create a worker with backward compatibility ENABLED. Wake it up and wait
+  // until GetWork() returns.
+  auto worker = MakeRefCounted<SchedulerWorker>(
+      ThreadPriority::NORMAL, std::move(delegate), task_tracker.GetTrackedRef(),
+      nullptr, SchedulerBackwardCompatibility::INIT_COM_STA);
+  worker->Start();
+  worker->WakeUp();
+  delegate_raw->WaitUntilGetWorkReturned();
+
+  // The call to CoInitializeEx() should have returned S_FALSE to indicate that
+  // the COM library was already initialized on the thread.
+  // See SchedulerWorker::Thread::ThreadMain for why we expect two different
+  // results here.
+#if defined(COM_INIT_CHECK_HOOK_ENABLED)
+  EXPECT_EQ(S_OK, delegate_raw->coinitialize_hresult());
+#else
+  EXPECT_EQ(S_FALSE, delegate_raw->coinitialize_hresult());
+#endif
+
+  worker->JoinForTesting();
+}
+
+TEST(TaskSchedulerWorkerTest, BackwardCompatibilityDisabled) {
+  TaskTracker task_tracker("Test");
+  auto delegate = std::make_unique<CoInitializeDelegate>();
+  CoInitializeDelegate* const delegate_raw = delegate.get();
+
+  // Create a worker with backward compatibility DISABLED. Wake it up and wait
+  // until GetWork() returns.
+  auto worker = MakeRefCounted<SchedulerWorker>(
+      ThreadPriority::NORMAL, std::move(delegate), task_tracker.GetTrackedRef(),
+      nullptr, SchedulerBackwardCompatibility::DISABLED);
+  worker->Start();
+  worker->WakeUp();
+  delegate_raw->WaitUntilGetWorkReturned();
+
+  // The call to CoInitializeEx() should have returned S_OK to indicate that the
+  // COM library wasn't already initialized on the thread.
+  EXPECT_EQ(S_OK, delegate_raw->coinitialize_hresult());
+
+  worker->JoinForTesting();
+}
+
+#endif  // defined(OS_WIN)
+
+}  // namespace internal
+}  // namespace base
diff --git a/base/task_scheduler/task_scheduler_impl_unittest.cc b/base/task_scheduler/task_scheduler_impl_unittest.cc
new file mode 100644
index 0000000..94c5293
--- /dev/null
+++ b/base/task_scheduler/task_scheduler_impl_unittest.cc
@@ -0,0 +1,841 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/task_scheduler/task_scheduler_impl.h"
+
+#include <stddef.h>
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback.h"
+#include "base/cfi_buildflags.h"
+#include "base/debug/stack_trace.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/metrics/field_trial.h"
+#include "base/metrics/field_trial_params.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/task_scheduler/environment_config.h"
+#include "base/task_scheduler/scheduler_worker_observer.h"
+#include "base/task_scheduler/scheduler_worker_pool_params.h"
+#include "base/task_scheduler/task_traits.h"
+#include "base/task_scheduler/test_task_factory.h"
+#include "base/task_scheduler/test_utils.h"
+#include "base/test/gtest_util.h"
+#include "base/test/test_timeouts.h"
+#include "base/threading/platform_thread.h"
+#include "base/threading/sequence_local_storage_slot.h"
+#include "base/threading/simple_thread.h"
+#include "base/threading/thread.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/time/time.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if defined(OS_POSIX)
+#include <unistd.h>
+
+#include "base/debug/leak_annotations.h"
+#include "base/files/file_descriptor_watcher_posix.h"
+#include "base/files/file_util.h"
+#include "base/posix/eintr_wrapper.h"
+#endif  // defined(OS_POSIX)
+
+#if defined(OS_WIN)
+#include "base/win/com_init_util.h"
+#endif  // defined(OS_WIN)
+
+namespace base {
+namespace internal {
+
+namespace {
+
+struct TraitsExecutionModePair {
+  TraitsExecutionModePair(const TaskTraits& traits,
+                          test::ExecutionMode execution_mode)
+      : traits(traits), execution_mode(execution_mode) {}
+
+  TaskTraits traits;
+  test::ExecutionMode execution_mode;
+};
+
+#if DCHECK_IS_ON()
+// Returns whether I/O calls are allowed on the current thread.
+bool GetIOAllowed() {
+  const bool previous_value = ThreadRestrictions::SetIOAllowed(true);
+  ThreadRestrictions::SetIOAllowed(previous_value);
+  return previous_value;
+}
+#endif
+
+// Verify that the current thread priority and I/O restrictions are appropriate
+// to run a Task with |traits|.
+// Note: ExecutionMode is verified inside TestTaskFactory.
+void VerifyTaskEnvironment(const TaskTraits& traits) {
+  EXPECT_EQ(CanUseBackgroundPriorityForSchedulerWorker() &&
+                    traits.priority() == TaskPriority::BACKGROUND
+                ? ThreadPriority::BACKGROUND
+                : ThreadPriority::NORMAL,
+            PlatformThread::GetCurrentThreadPriority());
+
+#if DCHECK_IS_ON()
+  // The #if above is required because GetIOAllowed() always returns true when
+  // !DCHECK_IS_ON(), even when |traits| don't allow file I/O.
+  EXPECT_EQ(traits.may_block(), GetIOAllowed());
+#endif
+
+  // Verify that the thread the task is running on is named as expected.
+  const std::string current_thread_name(PlatformThread::GetName());
+  EXPECT_NE(std::string::npos, current_thread_name.find("TaskScheduler"));
+
+  if (current_thread_name.find("SingleThread") != std::string::npos) {
+    // For now, single-threaded background tasks run on their own threads.
+    // TODO(fdoray): Run single-threaded background tasks on foreground workers
+    // on platforms that don't support background thread priority.
+    EXPECT_NE(
+        std::string::npos,
+        current_thread_name.find(traits.priority() == TaskPriority::BACKGROUND
+                                     ? "Background"
+                                     : "Foreground"));
+  } else {
+    EXPECT_NE(std::string::npos,
+              current_thread_name.find(
+                  CanUseBackgroundPriorityForSchedulerWorker() &&
+                          traits.priority() == TaskPriority::BACKGROUND
+                      ? "Background"
+                      : "Foreground"));
+  }
+  EXPECT_EQ(traits.may_block(),
+            current_thread_name.find("Blocking") != std::string::npos);
+}
+
+void VerifyTaskEnvironmentAndSignalEvent(const TaskTraits& traits,
+                                         WaitableEvent* event) {
+  DCHECK(event);
+  VerifyTaskEnvironment(traits);
+  event->Signal();
+}
+
+void VerifyTimeAndTaskEnvironmentAndSignalEvent(const TaskTraits& traits,
+                                                TimeTicks expected_time,
+                                                WaitableEvent* event) {
+  DCHECK(event);
+  EXPECT_LE(expected_time, TimeTicks::Now());
+  VerifyTaskEnvironment(traits);
+  event->Signal();
+}
+
+scoped_refptr<TaskRunner> CreateTaskRunnerWithTraitsAndExecutionMode(
+    TaskScheduler* scheduler,
+    const TaskTraits& traits,
+    test::ExecutionMode execution_mode,
+    SingleThreadTaskRunnerThreadMode default_single_thread_task_runner_mode =
+        SingleThreadTaskRunnerThreadMode::SHARED) {
+  switch (execution_mode) {
+    case test::ExecutionMode::PARALLEL:
+      return scheduler->CreateTaskRunnerWithTraits(traits);
+    case test::ExecutionMode::SEQUENCED:
+      return scheduler->CreateSequencedTaskRunnerWithTraits(traits);
+    case test::ExecutionMode::SINGLE_THREADED: {
+      return scheduler->CreateSingleThreadTaskRunnerWithTraits(
+          traits, default_single_thread_task_runner_mode);
+    }
+  }
+  ADD_FAILURE() << "Unknown ExecutionMode";
+  return nullptr;
+}
+
+class ThreadPostingTasks : public SimpleThread {
+ public:
+  // Creates a thread that posts Tasks to |scheduler| with |traits| and
+  // |execution_mode|.
+  ThreadPostingTasks(TaskSchedulerImpl* scheduler,
+                     const TaskTraits& traits,
+                     test::ExecutionMode execution_mode)
+      : SimpleThread("ThreadPostingTasks"),
+        traits_(traits),
+        factory_(CreateTaskRunnerWithTraitsAndExecutionMode(scheduler,
+                                                            traits,
+                                                            execution_mode),
+                 execution_mode) {}
+
+  void WaitForAllTasksToRun() { factory_.WaitForAllTasksToRun(); }
+
+ private:
+  void Run() override {
+    EXPECT_FALSE(factory_.task_runner()->RunsTasksInCurrentSequence());
+
+    const size_t kNumTasksPerThread = 150;
+    for (size_t i = 0; i < kNumTasksPerThread; ++i) {
+      factory_.PostTask(test::TestTaskFactory::PostNestedTask::NO,
+                        Bind(&VerifyTaskEnvironment, traits_));
+    }
+  }
+
+  const TaskTraits traits_;
+  test::TestTaskFactory factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(ThreadPostingTasks);
+};
+
+// Returns a vector with a TraitsExecutionModePair for each valid
+// combination of {ExecutionMode, TaskPriority, MayBlock()}.
+std::vector<TraitsExecutionModePair> GetTraitsExecutionModePairs() {
+  std::vector<TraitsExecutionModePair> params;
+
+  const test::ExecutionMode execution_modes[] = {
+      test::ExecutionMode::PARALLEL, test::ExecutionMode::SEQUENCED,
+      test::ExecutionMode::SINGLE_THREADED};
+
+  for (test::ExecutionMode execution_mode : execution_modes) {
+    for (size_t priority_index = static_cast<size_t>(TaskPriority::LOWEST);
+         priority_index <= static_cast<size_t>(TaskPriority::HIGHEST);
+         ++priority_index) {
+      const TaskPriority priority = static_cast<TaskPriority>(priority_index);
+      params.push_back(TraitsExecutionModePair({priority}, execution_mode));
+      params.push_back(TraitsExecutionModePair({MayBlock()}, execution_mode));
+    }
+  }
+
+  return params;
+}
+
+class TaskSchedulerImplTest
+    : public testing::TestWithParam<TraitsExecutionModePair> {
+ protected:
+  TaskSchedulerImplTest() : scheduler_("Test"), field_trial_list_(nullptr) {}
+
+  void EnableAllTasksUserBlocking() {
+    constexpr char kFieldTrialName[] = "BrowserScheduler";
+    constexpr char kFieldTrialTestGroup[] = "DummyGroup";
+    std::map<std::string, std::string> variation_params;
+    variation_params["AllTasksUserBlocking"] = "true";
+    base::AssociateFieldTrialParams(kFieldTrialName, kFieldTrialTestGroup,
+                                    variation_params);
+    base::FieldTrialList::CreateFieldTrial(kFieldTrialName,
+                                           kFieldTrialTestGroup);
+  }
+
+  void set_scheduler_worker_observer(
+      SchedulerWorkerObserver* scheduler_worker_observer) {
+    scheduler_worker_observer_ = scheduler_worker_observer;
+  }
+
+  void StartTaskScheduler() {
+    constexpr TimeDelta kSuggestedReclaimTime = TimeDelta::FromSeconds(30);
+    constexpr int kMaxNumBackgroundThreads = 1;
+    constexpr int kMaxNumBackgroundBlockingThreads = 3;
+    constexpr int kMaxNumForegroundThreads = 4;
+    constexpr int kMaxNumForegroundBlockingThreads = 12;
+
+    scheduler_.Start(
+        {{kMaxNumBackgroundThreads, kSuggestedReclaimTime},
+         {kMaxNumBackgroundBlockingThreads, kSuggestedReclaimTime},
+         {kMaxNumForegroundThreads, kSuggestedReclaimTime},
+         {kMaxNumForegroundBlockingThreads, kSuggestedReclaimTime}},
+        scheduler_worker_observer_);
+  }
+
+  void TearDown() override {
+    if (did_tear_down_)
+      return;
+
+    scheduler_.FlushForTesting();
+    scheduler_.JoinForTesting();
+    did_tear_down_ = true;
+  }
+
+  TaskSchedulerImpl scheduler_;
+
+ private:
+  base::FieldTrialList field_trial_list_;
+  SchedulerWorkerObserver* scheduler_worker_observer_ = nullptr;
+  bool did_tear_down_ = false;
+
+  DISALLOW_COPY_AND_ASSIGN(TaskSchedulerImplTest);
+};
+
+}  // namespace
+
+// Verifies that a Task posted via PostDelayedTaskWithTraits with parameterized
+// TaskTraits and no delay runs on a thread with the expected priority and I/O
+// restrictions. The ExecutionMode parameter is ignored by this test.
+TEST_P(TaskSchedulerImplTest, PostDelayedTaskWithTraitsNoDelay) {
+  StartTaskScheduler();
+  WaitableEvent task_ran;
+  scheduler_.PostDelayedTaskWithTraits(
+      FROM_HERE, GetParam().traits,
+      BindOnce(&VerifyTaskEnvironmentAndSignalEvent, GetParam().traits,
+               Unretained(&task_ran)),
+      TimeDelta());
+  task_ran.Wait();
+}
+
+// Verifies that a Task posted via PostDelayedTaskWithTraits with parameterized
+// TaskTraits and a non-zero delay runs on a thread with the expected priority
+// and I/O restrictions after the delay expires. The ExecutionMode parameter is
+// ignored by this test.
+TEST_P(TaskSchedulerImplTest, PostDelayedTaskWithTraitsWithDelay) {
+  StartTaskScheduler();
+  WaitableEvent task_ran;
+  scheduler_.PostDelayedTaskWithTraits(
+      FROM_HERE, GetParam().traits,
+      BindOnce(&VerifyTimeAndTaskEnvironmentAndSignalEvent, GetParam().traits,
+               TimeTicks::Now() + TestTimeouts::tiny_timeout(),
+               Unretained(&task_ran)),
+      TestTimeouts::tiny_timeout());
+  task_ran.Wait();
+}
+
+// Verifies that Tasks posted via a TaskRunner with parameterized TaskTraits and
+// ExecutionMode run on a thread with the expected priority and I/O restrictions
+// and respect the characteristics of their ExecutionMode.
+TEST_P(TaskSchedulerImplTest, PostTasksViaTaskRunner) {
+  StartTaskScheduler();
+  test::TestTaskFactory factory(
+      CreateTaskRunnerWithTraitsAndExecutionMode(&scheduler_, GetParam().traits,
+                                                 GetParam().execution_mode),
+      GetParam().execution_mode);
+  EXPECT_FALSE(factory.task_runner()->RunsTasksInCurrentSequence());
+
+  const size_t kNumTasksPerTest = 150;
+  for (size_t i = 0; i < kNumTasksPerTest; ++i) {
+    factory.PostTask(test::TestTaskFactory::PostNestedTask::NO,
+                     Bind(&VerifyTaskEnvironment, GetParam().traits));
+  }
+
+  factory.WaitForAllTasksToRun();
+}
+
+// Verifies that a task posted via PostDelayedTaskWithTraits without a delay
+// doesn't run before Start() is called.
+TEST_P(TaskSchedulerImplTest, PostDelayedTaskWithTraitsNoDelayBeforeStart) {
+  WaitableEvent task_running;
+  scheduler_.PostDelayedTaskWithTraits(
+      FROM_HERE, GetParam().traits,
+      BindOnce(&VerifyTaskEnvironmentAndSignalEvent, GetParam().traits,
+               Unretained(&task_running)),
+      TimeDelta());
+
+  // Wait a little bit to make sure that the task doesn't run before Start().
+  // Note: This test won't catch a case where the task runs just after the check
+  // and before Start(). However, we expect the test to be flaky if the tested
+  // code allows that to happen.
+  PlatformThread::Sleep(TestTimeouts::tiny_timeout());
+  EXPECT_FALSE(task_running.IsSignaled());
+
+  StartTaskScheduler();
+  task_running.Wait();
+}
+
+// Verifies that a task posted via PostDelayedTaskWithTraits with a delay
+// doesn't run before Start() is called.
+TEST_P(TaskSchedulerImplTest, PostDelayedTaskWithTraitsWithDelayBeforeStart) {
+  WaitableEvent task_running;
+  scheduler_.PostDelayedTaskWithTraits(
+      FROM_HERE, GetParam().traits,
+      BindOnce(&VerifyTimeAndTaskEnvironmentAndSignalEvent, GetParam().traits,
+               TimeTicks::Now() + TestTimeouts::tiny_timeout(),
+               Unretained(&task_running)),
+      TestTimeouts::tiny_timeout());
+
+  // Wait a little bit to make sure that the task doesn't run before Start().
+  // Note: This test won't catch a case where the task runs just after the check
+  // and before Start(). However, we expect the test to be flaky if the tested
+  // code allows that to happen.
+  PlatformThread::Sleep(TestTimeouts::tiny_timeout());
+  EXPECT_FALSE(task_running.IsSignaled());
+
+  StartTaskScheduler();
+  task_running.Wait();
+}
+
+// Verifies that a task posted via a TaskRunner doesn't run before Start() is
+// called.
+TEST_P(TaskSchedulerImplTest, PostTaskViaTaskRunnerBeforeStart) {
+  WaitableEvent task_running;
+  CreateTaskRunnerWithTraitsAndExecutionMode(&scheduler_, GetParam().traits,
+                                             GetParam().execution_mode)
+      ->PostTask(FROM_HERE,
+                 BindOnce(&VerifyTaskEnvironmentAndSignalEvent,
+                          GetParam().traits, Unretained(&task_running)));
+
+  // Wait a little bit to make sure that the task doesn't run before Start().
+  // Note: This test won't catch a case where the task runs just after the check
+  // and before Start(). However, we expect the test to be flaky if the tested
+  // code allows that to happen.
+  PlatformThread::Sleep(TestTimeouts::tiny_timeout());
+  EXPECT_FALSE(task_running.IsSignaled());
+
+  StartTaskScheduler();
+
+  // This should not hang if the task runs after Start().
+  task_running.Wait();
+}
+
+// Verify that all tasks posted to a TaskRunner after Start() run in a
+// USER_BLOCKING environment when the AllTasksUserBlocking variation param of
+// the BrowserScheduler experiment is true.
+TEST_P(TaskSchedulerImplTest, AllTasksAreUserBlockingTaskRunner) {
+  EnableAllTasksUserBlocking();
+  StartTaskScheduler();
+
+  WaitableEvent task_running;
+  CreateTaskRunnerWithTraitsAndExecutionMode(&scheduler_, GetParam().traits,
+                                             GetParam().execution_mode)
+      ->PostTask(FROM_HERE,
+                 BindOnce(&VerifyTaskEnvironmentAndSignalEvent,
+                          TaskTraits::Override(GetParam().traits,
+                                               {TaskPriority::USER_BLOCKING}),
+                          Unretained(&task_running)));
+  task_running.Wait();
+}
+
+// Verify that all tasks posted via PostDelayedTaskWithTraits() after Start()
+// run in a USER_BLOCKING environment when the AllTasksUserBlocking variation
+// param of the BrowserScheduler experiment is true.
+TEST_P(TaskSchedulerImplTest, AllTasksAreUserBlocking) {
+  EnableAllTasksUserBlocking();
+  StartTaskScheduler();
+
+  WaitableEvent task_running;
+  // Ignore |params.execution_mode| in this test.
+  scheduler_.PostDelayedTaskWithTraits(
+      FROM_HERE, GetParam().traits,
+      BindOnce(&VerifyTaskEnvironmentAndSignalEvent,
+               TaskTraits::Override(GetParam().traits,
+                                    {TaskPriority::USER_BLOCKING}),
+               Unretained(&task_running)),
+      TimeDelta());
+  task_running.Wait();
+}
+
+// Verifies that FlushAsyncForTesting() calls back correctly for all trait and
+// execution mode pairs.
+TEST_P(TaskSchedulerImplTest, FlushAsyncForTestingSimple) {
+  StartTaskScheduler();
+
+  WaitableEvent unblock_task;
+  CreateTaskRunnerWithTraitsAndExecutionMode(
+      &scheduler_,
+      TaskTraits::Override(GetParam().traits, {WithBaseSyncPrimitives()}),
+      GetParam().execution_mode, SingleThreadTaskRunnerThreadMode::DEDICATED)
+      ->PostTask(FROM_HERE,
+                 BindOnce(&WaitableEvent::Wait, Unretained(&unblock_task)));
+
+  WaitableEvent flush_event;
+  scheduler_.FlushAsyncForTesting(
+      BindOnce(&WaitableEvent::Signal, Unretained(&flush_event)));
+  PlatformThread::Sleep(TestTimeouts::tiny_timeout());
+  EXPECT_FALSE(flush_event.IsSignaled());
+
+  unblock_task.Signal();
+
+  flush_event.Wait();
+}
+
+INSTANTIATE_TEST_CASE_P(OneTraitsExecutionModePair,
+                        TaskSchedulerImplTest,
+                        ::testing::ValuesIn(GetTraitsExecutionModePairs()));
+
+// Spawns threads that simultaneously post Tasks to TaskRunners with various
+// TaskTraits and ExecutionModes. Verifies that each Task runs on a thread with
+// the expected priority and I/O restrictions and respects the characteristics
+// of its ExecutionMode.
+TEST_F(TaskSchedulerImplTest, MultipleTraitsExecutionModePairs) {
+  StartTaskScheduler();
+  std::vector<std::unique_ptr<ThreadPostingTasks>> threads_posting_tasks;
+  for (const auto& traits_execution_mode_pair : GetTraitsExecutionModePairs()) {
+    threads_posting_tasks.push_back(WrapUnique(
+        new ThreadPostingTasks(&scheduler_, traits_execution_mode_pair.traits,
+                               traits_execution_mode_pair.execution_mode)));
+    threads_posting_tasks.back()->Start();
+  }
+
+  for (const auto& thread : threads_posting_tasks) {
+    thread->WaitForAllTasksToRun();
+    thread->Join();
+  }
+}
+
+TEST_F(TaskSchedulerImplTest,
+       GetMaxConcurrentNonBlockedTasksWithTraitsDeprecated) {
+  StartTaskScheduler();
+
+  // GetMaxConcurrentNonBlockedTasksWithTraitsDeprecated() does not support
+  // TaskPriority::BACKGROUND.
+  EXPECT_DCHECK_DEATH({
+    scheduler_.GetMaxConcurrentNonBlockedTasksWithTraitsDeprecated(
+        {TaskPriority::BACKGROUND});
+  });
+  EXPECT_DCHECK_DEATH({
+    scheduler_.GetMaxConcurrentNonBlockedTasksWithTraitsDeprecated(
+        {MayBlock(), TaskPriority::BACKGROUND});
+  });
+
+  EXPECT_EQ(4, scheduler_.GetMaxConcurrentNonBlockedTasksWithTraitsDeprecated(
+                   {TaskPriority::USER_VISIBLE}));
+  EXPECT_EQ(12, scheduler_.GetMaxConcurrentNonBlockedTasksWithTraitsDeprecated(
+                    {MayBlock(), TaskPriority::USER_VISIBLE}));
+  EXPECT_EQ(4, scheduler_.GetMaxConcurrentNonBlockedTasksWithTraitsDeprecated(
+                   {TaskPriority::USER_BLOCKING}));
+  EXPECT_EQ(12, scheduler_.GetMaxConcurrentNonBlockedTasksWithTraitsDeprecated(
+                    {MayBlock(), TaskPriority::USER_BLOCKING}));
+}
+
+// Verify that the RunsTasksInCurrentSequence() method of a SequencedTaskRunner
+// returns false when called from a task that isn't part of the sequence.
+TEST_F(TaskSchedulerImplTest, SequencedRunsTasksInCurrentSequence) {
+  StartTaskScheduler();
+  auto single_thread_task_runner =
+      scheduler_.CreateSingleThreadTaskRunnerWithTraits(
+          TaskTraits(), SingleThreadTaskRunnerThreadMode::SHARED);
+  auto sequenced_task_runner =
+      scheduler_.CreateSequencedTaskRunnerWithTraits(TaskTraits());
+
+  WaitableEvent task_ran;
+  single_thread_task_runner->PostTask(
+      FROM_HERE,
+      BindOnce(
+          [](scoped_refptr<TaskRunner> sequenced_task_runner,
+             WaitableEvent* task_ran) {
+            EXPECT_FALSE(sequenced_task_runner->RunsTasksInCurrentSequence());
+            task_ran->Signal();
+          },
+          sequenced_task_runner, Unretained(&task_ran)));
+  task_ran.Wait();
+}
+
+// Verify that the RunsTasksInCurrentSequence() method of a
+// SingleThreadTaskRunner returns false when called from a task that isn't part
+// of the sequence.
+TEST_F(TaskSchedulerImplTest, SingleThreadRunsTasksInCurrentSequence) {
+  StartTaskScheduler();
+  auto sequenced_task_runner =
+      scheduler_.CreateSequencedTaskRunnerWithTraits(TaskTraits());
+  auto single_thread_task_runner =
+      scheduler_.CreateSingleThreadTaskRunnerWithTraits(
+          TaskTraits(), SingleThreadTaskRunnerThreadMode::SHARED);
+
+  WaitableEvent task_ran;
+  sequenced_task_runner->PostTask(
+      FROM_HERE,
+      BindOnce(
+          [](scoped_refptr<TaskRunner> single_thread_task_runner,
+             WaitableEvent* task_ran) {
+            EXPECT_FALSE(
+                single_thread_task_runner->RunsTasksInCurrentSequence());
+            task_ran->Signal();
+          },
+          single_thread_task_runner, Unretained(&task_ran)));
+  task_ran.Wait();
+}
+
+#if defined(OS_WIN)
+TEST_F(TaskSchedulerImplTest, COMSTATaskRunnersRunWithCOMSTA) {
+  StartTaskScheduler();
+  auto com_sta_task_runner = scheduler_.CreateCOMSTATaskRunnerWithTraits(
+      TaskTraits(), SingleThreadTaskRunnerThreadMode::SHARED);
+
+  WaitableEvent task_ran;
+  com_sta_task_runner->PostTask(
+      FROM_HERE, Bind(
+                     [](WaitableEvent* task_ran) {
+                       win::AssertComApartmentType(win::ComApartmentType::STA);
+                       task_ran->Signal();
+                     },
+                     Unretained(&task_ran)));
+  task_ran.Wait();
+}
+#endif  // defined(OS_WIN)
+
+TEST_F(TaskSchedulerImplTest, DelayedTasksNotRunAfterShutdown) {
+  StartTaskScheduler();
+  // As with delayed tasks in general, this is racy. If the task does happen to
+  // run after Shutdown within the timeout, it will fail this test.
+  //
+  // The timeout should be set sufficiently long enough to ensure that the
+  // delayed task did not run. 2x is generally good enough.
+  //
+  // A non-racy way to do this would be to post two sequenced tasks:
+  // 1) Regular Post Task: A WaitableEvent.Wait
+  // 2) Delayed Task: ADD_FAILURE()
+  // and signalling the WaitableEvent after Shutdown() on a different thread
+  // since Shutdown() will block. However, the cost of managing this extra
+  // thread was deemed to be too great for the unlikely race.
+  scheduler_.PostDelayedTaskWithTraits(FROM_HERE, TaskTraits(),
+                                       BindOnce([]() { ADD_FAILURE(); }),
+                                       TestTimeouts::tiny_timeout());
+  scheduler_.Shutdown();
+  PlatformThread::Sleep(TestTimeouts::tiny_timeout() * 2);
+}
+
+#if defined(OS_POSIX)
+
+TEST_F(TaskSchedulerImplTest, FileDescriptorWatcherNoOpsAfterShutdown) {
+  StartTaskScheduler();
+
+  int pipes[2];
+  ASSERT_EQ(0, pipe(pipes));
+
+  scoped_refptr<TaskRunner> blocking_task_runner =
+      scheduler_.CreateSequencedTaskRunnerWithTraits(
+          {TaskShutdownBehavior::BLOCK_SHUTDOWN});
+  blocking_task_runner->PostTask(
+      FROM_HERE,
+      BindOnce(
+          [](int read_fd) {
+            std::unique_ptr<FileDescriptorWatcher::Controller> controller =
+                FileDescriptorWatcher::WatchReadable(
+                    read_fd, BindRepeating([]() { NOTREACHED(); }));
+
+            // This test is for components that intentionally leak their
+            // watchers at shutdown. We can't clean |controller| up because its
+            // destructor will assert that it's being called from the correct
+            // sequence. After the task scheduler is shutdown, it is not
+            // possible to run tasks on this sequence.
+            //
+            // Note: Do not inline the controller.release() call into the
+            //       ANNOTATE_LEAKING_OBJECT_PTR as the annotation is removed
+            //       by the preprocessor in non-LEAK_SANITIZER builds,
+            //       effectively breaking this test.
+            ANNOTATE_LEAKING_OBJECT_PTR(controller.get());
+            controller.release();
+          },
+          pipes[0]));
+
+  scheduler_.Shutdown();
+
+  constexpr char kByte = '!';
+  ASSERT_TRUE(WriteFileDescriptor(pipes[1], &kByte, sizeof(kByte)));
+
+  // Give a chance for the file watcher to fire before closing the handles.
+  PlatformThread::Sleep(TestTimeouts::tiny_timeout());
+
+  EXPECT_EQ(0, IGNORE_EINTR(close(pipes[0])));
+  EXPECT_EQ(0, IGNORE_EINTR(close(pipes[1])));
+}
+#endif  // defined(OS_POSIX)
+
+// Verify that tasks posted on the same sequence access the same values on
+// SequenceLocalStorage, and tasks on different sequences see different values.
+TEST_F(TaskSchedulerImplTest, SequenceLocalStorage) {
+  StartTaskScheduler();
+
+  SequenceLocalStorageSlot<int> slot;
+  auto sequenced_task_runner1 =
+      scheduler_.CreateSequencedTaskRunnerWithTraits(TaskTraits());
+  auto sequenced_task_runner2 =
+      scheduler_.CreateSequencedTaskRunnerWithTraits(TaskTraits());
+
+  sequenced_task_runner1->PostTask(
+      FROM_HERE,
+      BindOnce([](SequenceLocalStorageSlot<int>* slot) { slot->Set(11); },
+               &slot));
+
+  sequenced_task_runner1->PostTask(FROM_HERE,
+                                   BindOnce(
+                                       [](SequenceLocalStorageSlot<int>* slot) {
+                                         EXPECT_EQ(slot->Get(), 11);
+                                       },
+                                       &slot));
+
+  sequenced_task_runner2->PostTask(FROM_HERE,
+                                   BindOnce(
+                                       [](SequenceLocalStorageSlot<int>* slot) {
+                                         EXPECT_NE(slot->Get(), 11);
+                                       },
+                                       &slot));
+
+  scheduler_.FlushForTesting();
+}
+
+TEST_F(TaskSchedulerImplTest, FlushAsyncNoTasks) {
+  StartTaskScheduler();
+  bool called_back = false;
+  scheduler_.FlushAsyncForTesting(
+      BindOnce([](bool* called_back) { *called_back = true; },
+               Unretained(&called_back)));
+  EXPECT_TRUE(called_back);
+}
+
+namespace {
+
+// Verifies that |query| is found on the current stack. Ignores failures if this
+// configuration doesn't have symbols.
+void VerifyHasStringOnStack(const std::string& query) {
+  const std::string stack = debug::StackTrace().ToString();
+  SCOPED_TRACE(stack);
+  const bool found_on_stack = stack.find(query) != std::string::npos;
+  const bool stack_has_symbols =
+      stack.find("SchedulerWorker") != std::string::npos;
+  EXPECT_TRUE(found_on_stack || !stack_has_symbols) << query;
+}
+
+}  // namespace
+
+#if defined(OS_POSIX)
+// Many POSIX bots flakily crash on |debug::StackTrace().ToString()|,
+// https://crbug.com/840429.
+#define MAYBE_IdentifiableStacks DISABLED_IdentifiableStacks
+#elif defined(OS_WIN) && \
+    (defined(ADDRESS_SANITIZER) || BUILDFLAG(CFI_CAST_CHECK))
+// Hangs on WinASan and WinCFI (grabbing StackTrace() too slow?),
+// https://crbug.com/845010#c7.
+#define MAYBE_IdentifiableStacks DISABLED_IdentifiableStacks
+#else
+#define MAYBE_IdentifiableStacks IdentifiableStacks
+#endif
+
+// Integration test that verifies that workers have a frame on their stacks
+// which easily identifies the type of worker (useful to diagnose issues from
+// logs without memory dumps).
+TEST_F(TaskSchedulerImplTest, MAYBE_IdentifiableStacks) {
+  StartTaskScheduler();
+
+  scheduler_.CreateSequencedTaskRunnerWithTraits({})->PostTask(
+      FROM_HERE, BindOnce(&VerifyHasStringOnStack, "RunPooledWorker"));
+  scheduler_.CreateSequencedTaskRunnerWithTraits({TaskPriority::BACKGROUND})
+      ->PostTask(FROM_HERE, BindOnce(&VerifyHasStringOnStack,
+                                     "RunBackgroundPooledWorker"));
+
+  scheduler_
+      .CreateSingleThreadTaskRunnerWithTraits(
+          {}, SingleThreadTaskRunnerThreadMode::SHARED)
+      ->PostTask(FROM_HERE,
+                 BindOnce(&VerifyHasStringOnStack, "RunSharedWorker"));
+  scheduler_
+      .CreateSingleThreadTaskRunnerWithTraits(
+          {TaskPriority::BACKGROUND}, SingleThreadTaskRunnerThreadMode::SHARED)
+      ->PostTask(FROM_HERE, BindOnce(&VerifyHasStringOnStack,
+                                     "RunBackgroundSharedWorker"));
+
+  scheduler_
+      .CreateSingleThreadTaskRunnerWithTraits(
+          {}, SingleThreadTaskRunnerThreadMode::DEDICATED)
+      ->PostTask(FROM_HERE,
+                 BindOnce(&VerifyHasStringOnStack, "RunDedicatedWorker"));
+  scheduler_
+      .CreateSingleThreadTaskRunnerWithTraits(
+          {TaskPriority::BACKGROUND},
+          SingleThreadTaskRunnerThreadMode::DEDICATED)
+      ->PostTask(FROM_HERE, BindOnce(&VerifyHasStringOnStack,
+                                     "RunBackgroundDedicatedWorker"));
+
+#if defined(OS_WIN)
+  scheduler_
+      .CreateCOMSTATaskRunnerWithTraits(
+          {}, SingleThreadTaskRunnerThreadMode::SHARED)
+      ->PostTask(FROM_HERE,
+                 BindOnce(&VerifyHasStringOnStack, "RunSharedCOMWorker"));
+  scheduler_
+      .CreateCOMSTATaskRunnerWithTraits(
+          {TaskPriority::BACKGROUND}, SingleThreadTaskRunnerThreadMode::SHARED)
+      ->PostTask(FROM_HERE, BindOnce(&VerifyHasStringOnStack,
+                                     "RunBackgroundSharedCOMWorker"));
+
+  scheduler_
+      .CreateCOMSTATaskRunnerWithTraits(
+          {}, SingleThreadTaskRunnerThreadMode::DEDICATED)
+      ->PostTask(FROM_HERE,
+                 BindOnce(&VerifyHasStringOnStack, "RunDedicatedCOMWorker"));
+  scheduler_
+      .CreateCOMSTATaskRunnerWithTraits(
+          {TaskPriority::BACKGROUND},
+          SingleThreadTaskRunnerThreadMode::DEDICATED)
+      ->PostTask(FROM_HERE, BindOnce(&VerifyHasStringOnStack,
+                                     "RunBackgroundDedicatedCOMWorker"));
+#endif  // defined(OS_WIN)
+
+  scheduler_.FlushForTesting();
+}
+
+TEST_F(TaskSchedulerImplTest, SchedulerWorkerObserver) {
+  testing::StrictMock<test::MockSchedulerWorkerObserver> observer;
+  set_scheduler_worker_observer(&observer);
+
+  // A worker should be created for each pool. After that, 8 threads should be
+  // created for single-threaded work (16 on Windows).
+  const int kExpectedNumPoolWorkers =
+      CanUseBackgroundPriorityForSchedulerWorker() ? 4 : 2;
+#if defined(OS_WIN)
+  const int kExpectedNumSingleThreadedWorkers = 16;
+#else
+  const int kExpectedNumSingleThreadedWorkers = 8;
+#endif
+  const int kExpectedNumWorkers =
+      kExpectedNumPoolWorkers + kExpectedNumSingleThreadedWorkers;
+
+  EXPECT_CALL(observer, OnSchedulerWorkerMainEntry())
+      .Times(kExpectedNumWorkers);
+
+  StartTaskScheduler();
+
+  std::vector<scoped_refptr<SingleThreadTaskRunner>> task_runners;
+
+  task_runners.push_back(scheduler_.CreateSingleThreadTaskRunnerWithTraits(
+      {TaskPriority::BACKGROUND}, SingleThreadTaskRunnerThreadMode::SHARED));
+  task_runners.push_back(scheduler_.CreateSingleThreadTaskRunnerWithTraits(
+      {TaskPriority::BACKGROUND, MayBlock()},
+      SingleThreadTaskRunnerThreadMode::SHARED));
+  task_runners.push_back(scheduler_.CreateSingleThreadTaskRunnerWithTraits(
+      {TaskPriority::USER_BLOCKING}, SingleThreadTaskRunnerThreadMode::SHARED));
+  task_runners.push_back(scheduler_.CreateSingleThreadTaskRunnerWithTraits(
+      {TaskPriority::USER_BLOCKING, MayBlock()},
+      SingleThreadTaskRunnerThreadMode::SHARED));
+
+  task_runners.push_back(scheduler_.CreateSingleThreadTaskRunnerWithTraits(
+      {TaskPriority::BACKGROUND}, SingleThreadTaskRunnerThreadMode::DEDICATED));
+  task_runners.push_back(scheduler_.CreateSingleThreadTaskRunnerWithTraits(
+      {TaskPriority::BACKGROUND, MayBlock()},
+      SingleThreadTaskRunnerThreadMode::DEDICATED));
+  task_runners.push_back(scheduler_.CreateSingleThreadTaskRunnerWithTraits(
+      {TaskPriority::USER_BLOCKING},
+      SingleThreadTaskRunnerThreadMode::DEDICATED));
+  task_runners.push_back(scheduler_.CreateSingleThreadTaskRunnerWithTraits(
+      {TaskPriority::USER_BLOCKING, MayBlock()},
+      SingleThreadTaskRunnerThreadMode::DEDICATED));
+
+#if defined(OS_WIN)
+  task_runners.push_back(scheduler_.CreateCOMSTATaskRunnerWithTraits(
+      {TaskPriority::BACKGROUND}, SingleThreadTaskRunnerThreadMode::SHARED));
+  task_runners.push_back(scheduler_.CreateCOMSTATaskRunnerWithTraits(
+      {TaskPriority::BACKGROUND, MayBlock()},
+      SingleThreadTaskRunnerThreadMode::SHARED));
+  task_runners.push_back(scheduler_.CreateCOMSTATaskRunnerWithTraits(
+      {TaskPriority::USER_BLOCKING}, SingleThreadTaskRunnerThreadMode::SHARED));
+  task_runners.push_back(scheduler_.CreateCOMSTATaskRunnerWithTraits(
+      {TaskPriority::USER_BLOCKING, MayBlock()},
+      SingleThreadTaskRunnerThreadMode::SHARED));
+
+  task_runners.push_back(scheduler_.CreateCOMSTATaskRunnerWithTraits(
+      {TaskPriority::BACKGROUND}, SingleThreadTaskRunnerThreadMode::DEDICATED));
+  task_runners.push_back(scheduler_.CreateCOMSTATaskRunnerWithTraits(
+      {TaskPriority::BACKGROUND, MayBlock()},
+      SingleThreadTaskRunnerThreadMode::DEDICATED));
+  task_runners.push_back(scheduler_.CreateCOMSTATaskRunnerWithTraits(
+      {TaskPriority::USER_BLOCKING},
+      SingleThreadTaskRunnerThreadMode::DEDICATED));
+  task_runners.push_back(scheduler_.CreateCOMSTATaskRunnerWithTraits(
+      {TaskPriority::USER_BLOCKING, MayBlock()},
+      SingleThreadTaskRunnerThreadMode::DEDICATED));
+#endif
+
+  for (auto& task_runner : task_runners)
+    task_runner->PostTask(FROM_HERE, DoNothing());
+
+  EXPECT_CALL(observer, OnSchedulerWorkerMainExit()).Times(kExpectedNumWorkers);
+
+  // Allow single-threaded workers to be released.
+  task_runners.clear();
+
+  TearDown();
+}
+
+}  // namespace internal
+}  // namespace base
diff --git a/base/task_scheduler/task_tracker_posix_unittest.cc b/base/task_scheduler/task_tracker_posix_unittest.cc
new file mode 100644
index 0000000..3ca7533
--- /dev/null
+++ b/base/task_scheduler/task_tracker_posix_unittest.cc
@@ -0,0 +1,101 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/task_scheduler/task_tracker_posix.h"
+
+#include <unistd.h>
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/files/file_descriptor_watcher_posix.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted.h"
+#include "base/message_loop/message_loop.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/run_loop.h"
+#include "base/sequence_token.h"
+#include "base/task_scheduler/task.h"
+#include "base/task_scheduler/task_traits.h"
+#include "base/task_scheduler/test_utils.h"
+#include "base/test/null_task_runner.h"
+#include "base/threading/thread.h"
+#include "base/time/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace internal {
+
+namespace {
+
+class TaskSchedulerTaskTrackerPosixTest : public testing::Test {
+ public:
+  TaskSchedulerTaskTrackerPosixTest() : service_thread_("ServiceThread") {
+    Thread::Options service_thread_options;
+    service_thread_options.message_loop_type = MessageLoop::TYPE_IO;
+    service_thread_.StartWithOptions(service_thread_options);
+    tracker_.set_watch_file_descriptor_message_loop(
+        static_cast<MessageLoopForIO*>(service_thread_.message_loop()));
+  }
+
+ protected:
+  Thread service_thread_;
+  TaskTrackerPosix tracker_ = {"Test"};
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TaskSchedulerTaskTrackerPosixTest);
+};
+
+}  // namespace
+
+// Verify that TaskTrackerPosix runs a Task it receives.
+TEST_F(TaskSchedulerTaskTrackerPosixTest, RunTask) {
+  bool did_run = false;
+  Task task(FROM_HERE,
+            Bind([](bool* did_run) { *did_run = true; }, Unretained(&did_run)),
+            TaskTraits(), TimeDelta());
+
+  EXPECT_TRUE(tracker_.WillPostTask(&task));
+
+  auto sequence = test::CreateSequenceWithTask(std::move(task));
+  EXPECT_EQ(sequence, tracker_.WillScheduleSequence(sequence, nullptr));
+  // Expect RunAndPopNextTask to return nullptr since |sequence| is empty after
+  // popping a task from it.
+  EXPECT_FALSE(tracker_.RunAndPopNextTask(sequence, nullptr));
+
+  EXPECT_TRUE(did_run);
+}
+
+// Verify that FileDescriptorWatcher::WatchReadable() can be called from a task
+// running in TaskTrackerPosix without a crash.
+TEST_F(TaskSchedulerTaskTrackerPosixTest, FileDescriptorWatcher) {
+  int fds[2];
+  ASSERT_EQ(0, pipe(fds));
+  Task task(FROM_HERE,
+            Bind(IgnoreResult(&FileDescriptorWatcher::WatchReadable), fds[0],
+                 DoNothing()),
+            TaskTraits(), TimeDelta());
+  // FileDescriptorWatcher::WatchReadable needs a SequencedTaskRunnerHandle.
+  task.sequenced_task_runner_ref = MakeRefCounted<NullTaskRunner>();
+
+  EXPECT_TRUE(tracker_.WillPostTask(&task));
+
+  auto sequence = test::CreateSequenceWithTask(std::move(task));
+  EXPECT_EQ(sequence, tracker_.WillScheduleSequence(sequence, nullptr));
+  // Expect RunAndPopNextTask to return nullptr since |sequence| is empty after
+  // popping a task from it.
+  EXPECT_FALSE(tracker_.RunAndPopNextTask(sequence, nullptr));
+
+  // Join the service thread to make sure that the read watch is registered and
+  // unregistered before file descriptors are closed.
+  service_thread_.Stop();
+
+  EXPECT_EQ(0, IGNORE_EINTR(close(fds[0])));
+  EXPECT_EQ(0, IGNORE_EINTR(close(fds[1])));
+}
+
+}  // namespace internal
+}  // namespace base
diff --git a/base/task_scheduler/task_tracker_unittest.cc b/base/task_scheduler/task_tracker_unittest.cc
new file mode 100644
index 0000000..159c9a9
--- /dev/null
+++ b/base/task_scheduler/task_tracker_unittest.cc
@@ -0,0 +1,1364 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/task_scheduler/task_tracker.h"
+
+#include <stdint.h>
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted.h"
+#include "base/metrics/histogram_base.h"
+#include "base/metrics/histogram_samples.h"
+#include "base/metrics/statistics_recorder.h"
+#include "base/sequence_token.h"
+#include "base/sequenced_task_runner.h"
+#include "base/single_thread_task_runner.h"
+#include "base/synchronization/atomic_flag.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/task_scheduler/scheduler_lock.h"
+#include "base/task_scheduler/task.h"
+#include "base/task_scheduler/task_traits.h"
+#include "base/task_scheduler/test_utils.h"
+#include "base/test/gtest_util.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "base/test/test_simple_task_runner.h"
+#include "base/test/test_timeouts.h"
+#include "base/threading/platform_thread.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "base/threading/simple_thread.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace internal {
+
+namespace {
+
+constexpr size_t kLoadTestNumIterations = 75;
+
+class MockCanScheduleSequenceObserver : public CanScheduleSequenceObserver {
+ public:
+  void OnCanScheduleSequence(scoped_refptr<Sequence> sequence) override {
+    MockOnCanScheduleSequence(sequence.get());
+  }
+
+  MOCK_METHOD1(MockOnCanScheduleSequence, void(Sequence*));
+};
+
+// Invokes a closure asynchronously.
+class CallbackThread : public SimpleThread {
+ public:
+  explicit CallbackThread(const Closure& closure)
+      : SimpleThread("CallbackThread"), closure_(closure) {}
+
+  // Returns true once the callback returns.
+  bool has_returned() { return has_returned_.IsSet(); }
+
+ private:
+  void Run() override {
+    closure_.Run();
+    has_returned_.Set();
+  }
+
+  const Closure closure_;
+  AtomicFlag has_returned_;
+
+  DISALLOW_COPY_AND_ASSIGN(CallbackThread);
+};
+
+class ThreadPostingAndRunningTask : public SimpleThread {
+ public:
+  enum class Action {
+    WILL_POST,
+    RUN,
+    WILL_POST_AND_RUN,
+  };
+
+  ThreadPostingAndRunningTask(TaskTracker* tracker,
+                              Task* task,
+                              Action action,
+                              bool expect_post_succeeds)
+      : SimpleThread("ThreadPostingAndRunningTask"),
+        tracker_(tracker),
+        owned_task_(FROM_HERE, OnceClosure(), TaskTraits(), TimeDelta()),
+        task_(task),
+        action_(action),
+        expect_post_succeeds_(expect_post_succeeds) {
+    EXPECT_TRUE(task_);
+
+    // Ownership of the Task is required to run it.
+    EXPECT_NE(Action::RUN, action_);
+    EXPECT_NE(Action::WILL_POST_AND_RUN, action_);
+  }
+
+  ThreadPostingAndRunningTask(TaskTracker* tracker,
+                              Task task,
+                              Action action,
+                              bool expect_post_succeeds)
+      : SimpleThread("ThreadPostingAndRunningTask"),
+        tracker_(tracker),
+        owned_task_(std::move(task)),
+        task_(&owned_task_),
+        action_(action),
+        expect_post_succeeds_(expect_post_succeeds) {
+    EXPECT_TRUE(owned_task_.task);
+  }
+
+ private:
+  void Run() override {
+    bool post_succeeded = true;
+    if (action_ == Action::WILL_POST || action_ == Action::WILL_POST_AND_RUN) {
+      post_succeeded = tracker_->WillPostTask(task_);
+      EXPECT_EQ(expect_post_succeeds_, post_succeeded);
+    }
+    if (post_succeeded &&
+        (action_ == Action::RUN || action_ == Action::WILL_POST_AND_RUN)) {
+      EXPECT_TRUE(owned_task_.task);
+
+      testing::StrictMock<MockCanScheduleSequenceObserver>
+          never_notified_observer;
+      auto sequence = tracker_->WillScheduleSequence(
+          test::CreateSequenceWithTask(std::move(owned_task_)),
+          &never_notified_observer);
+      ASSERT_TRUE(sequence);
+      // Expect RunAndPopNextTask to return nullptr since |sequence| is empty
+      // after popping a task from it.
+      EXPECT_FALSE(tracker_->RunAndPopNextTask(std::move(sequence),
+                                               &never_notified_observer));
+    }
+  }
+
+  TaskTracker* const tracker_;
+  Task owned_task_;
+  Task* task_;
+  const Action action_;
+  const bool expect_post_succeeds_;
+
+  DISALLOW_COPY_AND_ASSIGN(ThreadPostingAndRunningTask);
+};
+
+class ScopedSetSingletonAllowed {
+ public:
+  ScopedSetSingletonAllowed(bool singleton_allowed)
+      : previous_value_(
+            ThreadRestrictions::SetSingletonAllowed(singleton_allowed)) {}
+  ~ScopedSetSingletonAllowed() {
+    ThreadRestrictions::SetSingletonAllowed(previous_value_);
+  }
+
+ private:
+  const bool previous_value_;
+};
+
+class TaskSchedulerTaskTrackerTest
+    : public testing::TestWithParam<TaskShutdownBehavior> {
+ protected:
+  TaskSchedulerTaskTrackerTest() = default;
+
+  // Creates a task with |shutdown_behavior|.
+  Task CreateTask(TaskShutdownBehavior shutdown_behavior) {
+    return Task(
+        FROM_HERE,
+        Bind(&TaskSchedulerTaskTrackerTest::RunTaskCallback, Unretained(this)),
+        TaskTraits(shutdown_behavior), TimeDelta());
+  }
+
+  void DispatchAndRunTaskWithTracker(Task task) {
+    auto sequence = tracker_.WillScheduleSequence(
+        test::CreateSequenceWithTask(std::move(task)),
+        &never_notified_observer_);
+    ASSERT_TRUE(sequence);
+    tracker_.RunAndPopNextTask(std::move(sequence), &never_notified_observer_);
+  }
+
+  // Calls tracker_->Shutdown() on a new thread. When this returns, Shutdown()
+  // method has been entered on the new thread, but it hasn't necessarily
+  // returned.
+  void CallShutdownAsync() {
+    ASSERT_FALSE(thread_calling_shutdown_);
+    thread_calling_shutdown_.reset(new CallbackThread(
+        Bind(&TaskTracker::Shutdown, Unretained(&tracker_))));
+    thread_calling_shutdown_->Start();
+    while (!tracker_.HasShutdownStarted())
+      PlatformThread::YieldCurrentThread();
+  }
+
+  void WaitForAsyncIsShutdownComplete() {
+    ASSERT_TRUE(thread_calling_shutdown_);
+    thread_calling_shutdown_->Join();
+    EXPECT_TRUE(thread_calling_shutdown_->has_returned());
+    EXPECT_TRUE(tracker_.IsShutdownComplete());
+  }
+
+  void VerifyAsyncShutdownInProgress() {
+    ASSERT_TRUE(thread_calling_shutdown_);
+    EXPECT_FALSE(thread_calling_shutdown_->has_returned());
+    EXPECT_TRUE(tracker_.HasShutdownStarted());
+    EXPECT_FALSE(tracker_.IsShutdownComplete());
+  }
+
+  // Calls tracker_->FlushForTesting() on a new thread.
+  void CallFlushFromAnotherThread() {
+    ASSERT_FALSE(thread_calling_flush_);
+    thread_calling_flush_.reset(new CallbackThread(
+        Bind(&TaskTracker::FlushForTesting, Unretained(&tracker_))));
+    thread_calling_flush_->Start();
+  }
+
+  void WaitForAsyncFlushReturned() {
+    ASSERT_TRUE(thread_calling_flush_);
+    thread_calling_flush_->Join();
+    EXPECT_TRUE(thread_calling_flush_->has_returned());
+  }
+
+  void VerifyAsyncFlushInProgress() {
+    ASSERT_TRUE(thread_calling_flush_);
+    EXPECT_FALSE(thread_calling_flush_->has_returned());
+  }
+
+  size_t NumTasksExecuted() {
+    AutoSchedulerLock auto_lock(lock_);
+    return num_tasks_executed_;
+  }
+
+  TaskTracker tracker_ = {"Test"};
+  testing::StrictMock<MockCanScheduleSequenceObserver> never_notified_observer_;
+
+ private:
+  void RunTaskCallback() {
+    AutoSchedulerLock auto_lock(lock_);
+    ++num_tasks_executed_;
+  }
+
+  std::unique_ptr<CallbackThread> thread_calling_shutdown_;
+  std::unique_ptr<CallbackThread> thread_calling_flush_;
+
+  // Synchronizes accesses to |num_tasks_executed_|.
+  SchedulerLock lock_;
+
+  size_t num_tasks_executed_ = 0;
+
+  DISALLOW_COPY_AND_ASSIGN(TaskSchedulerTaskTrackerTest);
+};
+
+#define WAIT_FOR_ASYNC_SHUTDOWN_COMPLETED() \
+  do {                                      \
+    SCOPED_TRACE("");                       \
+    WaitForAsyncIsShutdownComplete();       \
+  } while (false)
+
+#define VERIFY_ASYNC_SHUTDOWN_IN_PROGRESS() \
+  do {                                      \
+    SCOPED_TRACE("");                       \
+    VerifyAsyncShutdownInProgress();        \
+  } while (false)
+
+#define WAIT_FOR_ASYNC_FLUSH_RETURNED() \
+  do {                                  \
+    SCOPED_TRACE("");                   \
+    WaitForAsyncFlushReturned();        \
+  } while (false)
+
+#define VERIFY_ASYNC_FLUSH_IN_PROGRESS() \
+  do {                                   \
+    SCOPED_TRACE("");                    \
+    VerifyAsyncFlushInProgress();        \
+  } while (false)
+
+}  // namespace
+
+TEST_P(TaskSchedulerTaskTrackerTest, WillPostAndRunBeforeShutdown) {
+  Task task(CreateTask(GetParam()));
+
+  // Inform |task_tracker_| that |task| will be posted.
+  EXPECT_TRUE(tracker_.WillPostTask(&task));
+
+  // Run the task.
+  EXPECT_EQ(0U, NumTasksExecuted());
+
+  DispatchAndRunTaskWithTracker(std::move(task));
+  EXPECT_EQ(1U, NumTasksExecuted());
+
+  // Shutdown() shouldn't block.
+  tracker_.Shutdown();
+}
+
+TEST_P(TaskSchedulerTaskTrackerTest, WillPostAndRunLongTaskBeforeShutdown) {
+  // Create a task that signals |task_running| and blocks until |task_barrier|
+  // is signaled.
+  WaitableEvent task_running(WaitableEvent::ResetPolicy::AUTOMATIC,
+                             WaitableEvent::InitialState::NOT_SIGNALED);
+  WaitableEvent task_barrier(WaitableEvent::ResetPolicy::AUTOMATIC,
+                             WaitableEvent::InitialState::NOT_SIGNALED);
+  Task blocked_task(
+      FROM_HERE,
+      Bind(
+          [](WaitableEvent* task_running, WaitableEvent* task_barrier) {
+            task_running->Signal();
+            task_barrier->Wait();
+          },
+          Unretained(&task_running), Unretained(&task_barrier)),
+      TaskTraits(WithBaseSyncPrimitives(), GetParam()), TimeDelta());
+
+  // Inform |task_tracker_| that |blocked_task| will be posted.
+  EXPECT_TRUE(tracker_.WillPostTask(&blocked_task));
+
+  // Create a thread to run the task. Wait until the task starts running.
+  ThreadPostingAndRunningTask thread_running_task(
+      &tracker_, std::move(blocked_task),
+      ThreadPostingAndRunningTask::Action::RUN, false);
+  thread_running_task.Start();
+  task_running.Wait();
+
+  // Initiate shutdown after the task has started to run.
+  CallShutdownAsync();
+
+  if (GetParam() == TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN) {
+    // Shutdown should complete even with a CONTINUE_ON_SHUTDOWN in progress.
+    WAIT_FOR_ASYNC_SHUTDOWN_COMPLETED();
+  } else {
+    // Shutdown should block with any non CONTINUE_ON_SHUTDOWN task in progress.
+    VERIFY_ASYNC_SHUTDOWN_IN_PROGRESS();
+  }
+
+  // Unblock the task.
+  task_barrier.Signal();
+  thread_running_task.Join();
+
+  // Shutdown should now complete for a non CONTINUE_ON_SHUTDOWN task.
+  if (GetParam() != TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN)
+    WAIT_FOR_ASYNC_SHUTDOWN_COMPLETED();
+}
+
+TEST_P(TaskSchedulerTaskTrackerTest, WillPostBeforeShutdownRunDuringShutdown) {
+  // Inform |task_tracker_| that a task will be posted.
+  Task task(CreateTask(GetParam()));
+  EXPECT_TRUE(tracker_.WillPostTask(&task));
+
+  // Inform |task_tracker_| that a BLOCK_SHUTDOWN task will be posted just to
+  // block shutdown.
+  Task block_shutdown_task(CreateTask(TaskShutdownBehavior::BLOCK_SHUTDOWN));
+  EXPECT_TRUE(tracker_.WillPostTask(&block_shutdown_task));
+
+  // Call Shutdown() asynchronously.
+  CallShutdownAsync();
+  VERIFY_ASYNC_SHUTDOWN_IN_PROGRESS();
+
+  // Try to run |task|. It should only run it it's BLOCK_SHUTDOWN. Otherwise it
+  // should be discarded.
+  EXPECT_EQ(0U, NumTasksExecuted());
+  const bool should_run = GetParam() == TaskShutdownBehavior::BLOCK_SHUTDOWN;
+
+  DispatchAndRunTaskWithTracker(std::move(task));
+  EXPECT_EQ(should_run ? 1U : 0U, NumTasksExecuted());
+  VERIFY_ASYNC_SHUTDOWN_IN_PROGRESS();
+
+  // Unblock shutdown by running the remaining BLOCK_SHUTDOWN task.
+  DispatchAndRunTaskWithTracker(std::move(block_shutdown_task));
+  EXPECT_EQ(should_run ? 2U : 1U, NumTasksExecuted());
+  WAIT_FOR_ASYNC_SHUTDOWN_COMPLETED();
+}
+
+TEST_P(TaskSchedulerTaskTrackerTest, WillPostBeforeShutdownRunAfterShutdown) {
+  // Inform |task_tracker_| that a task will be posted.
+  Task task(CreateTask(GetParam()));
+  EXPECT_TRUE(tracker_.WillPostTask(&task));
+
+  // Call Shutdown() asynchronously.
+  CallShutdownAsync();
+  EXPECT_EQ(0U, NumTasksExecuted());
+
+  if (GetParam() == TaskShutdownBehavior::BLOCK_SHUTDOWN) {
+    VERIFY_ASYNC_SHUTDOWN_IN_PROGRESS();
+
+    // Run the task to unblock shutdown.
+    DispatchAndRunTaskWithTracker(std::move(task));
+    EXPECT_EQ(1U, NumTasksExecuted());
+    WAIT_FOR_ASYNC_SHUTDOWN_COMPLETED();
+
+    // It is not possible to test running a BLOCK_SHUTDOWN task posted before
+    // shutdown after shutdown because Shutdown() won't return if there are
+    // pending BLOCK_SHUTDOWN tasks.
+  } else {
+    WAIT_FOR_ASYNC_SHUTDOWN_COMPLETED();
+
+    // The task shouldn't be allowed to run after shutdown.
+    DispatchAndRunTaskWithTracker(std::move(task));
+    EXPECT_EQ(0U, NumTasksExecuted());
+  }
+}
+
+TEST_P(TaskSchedulerTaskTrackerTest, WillPostAndRunDuringShutdown) {
+  // Inform |task_tracker_| that a BLOCK_SHUTDOWN task will be posted just to
+  // block shutdown.
+  Task block_shutdown_task(CreateTask(TaskShutdownBehavior::BLOCK_SHUTDOWN));
+  EXPECT_TRUE(tracker_.WillPostTask(&block_shutdown_task));
+
+  // Call Shutdown() asynchronously.
+  CallShutdownAsync();
+  VERIFY_ASYNC_SHUTDOWN_IN_PROGRESS();
+
+  if (GetParam() == TaskShutdownBehavior::BLOCK_SHUTDOWN) {
+    // Inform |task_tracker_| that a BLOCK_SHUTDOWN task will be posted.
+    Task task(CreateTask(GetParam()));
+    EXPECT_TRUE(tracker_.WillPostTask(&task));
+
+    // Run the BLOCK_SHUTDOWN task.
+    EXPECT_EQ(0U, NumTasksExecuted());
+    DispatchAndRunTaskWithTracker(std::move(task));
+    EXPECT_EQ(1U, NumTasksExecuted());
+  } else {
+    // It shouldn't be allowed to post a non BLOCK_SHUTDOWN task.
+    Task task(CreateTask(GetParam()));
+    EXPECT_FALSE(tracker_.WillPostTask(&task));
+
+    // Don't try to run the task, because it wasn't allowed to be posted.
+  }
+
+  // Unblock shutdown by running |block_shutdown_task|.
+  VERIFY_ASYNC_SHUTDOWN_IN_PROGRESS();
+  DispatchAndRunTaskWithTracker(std::move(block_shutdown_task));
+  EXPECT_EQ(GetParam() == TaskShutdownBehavior::BLOCK_SHUTDOWN ? 2U : 1U,
+            NumTasksExecuted());
+  WAIT_FOR_ASYNC_SHUTDOWN_COMPLETED();
+}
+
+TEST_P(TaskSchedulerTaskTrackerTest, WillPostAfterShutdown) {
+  tracker_.Shutdown();
+
+  Task task(CreateTask(GetParam()));
+
+  // |task_tracker_| shouldn't allow a task to be posted after shutdown.
+  EXPECT_FALSE(tracker_.WillPostTask(&task));
+}
+
+// Verify that BLOCK_SHUTDOWN and SKIP_ON_SHUTDOWN tasks can
+// AssertSingletonAllowed() but CONTINUE_ON_SHUTDOWN tasks can't.
+TEST_P(TaskSchedulerTaskTrackerTest, SingletonAllowed) {
+  const bool can_use_singletons =
+      (GetParam() != TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN);
+
+  Task task(FROM_HERE, BindOnce(&ThreadRestrictions::AssertSingletonAllowed),
+            TaskTraits(GetParam()), TimeDelta());
+  EXPECT_TRUE(tracker_.WillPostTask(&task));
+
+  // Set the singleton allowed bit to the opposite of what it is expected to be
+  // when |tracker| runs |task| to verify that |tracker| actually sets the
+  // correct value.
+  ScopedSetSingletonAllowed scoped_singleton_allowed(!can_use_singletons);
+
+  // Running the task should fail iff the task isn't allowed to use singletons.
+  if (can_use_singletons) {
+    DispatchAndRunTaskWithTracker(std::move(task));
+  } else {
+    EXPECT_DCHECK_DEATH({ DispatchAndRunTaskWithTracker(std::move(task)); });
+  }
+}
+
+// Verify that AssertIOAllowed() succeeds only for a MayBlock() task.
+TEST_P(TaskSchedulerTaskTrackerTest, IOAllowed) {
+  // Unset the IO allowed bit. Expect TaskTracker to set it before running a
+  // task with the MayBlock() trait.
+  ThreadRestrictions::SetIOAllowed(false);
+  Task task_with_may_block(FROM_HERE, Bind([]() {
+                             // Shouldn't fail.
+                             AssertBlockingAllowed();
+                           }),
+                           TaskTraits(MayBlock(), GetParam()), TimeDelta());
+  EXPECT_TRUE(tracker_.WillPostTask(&task_with_may_block));
+  DispatchAndRunTaskWithTracker(std::move(task_with_may_block));
+
+  // Set the IO allowed bit. Expect TaskTracker to unset it before running a
+  // task without the MayBlock() trait.
+  ThreadRestrictions::SetIOAllowed(true);
+  Task task_without_may_block(
+      FROM_HERE,
+      Bind([]() { EXPECT_DCHECK_DEATH({ AssertBlockingAllowed(); }); }),
+      TaskTraits(GetParam()), TimeDelta());
+  EXPECT_TRUE(tracker_.WillPostTask(&task_without_may_block));
+  DispatchAndRunTaskWithTracker(std::move(task_without_may_block));
+}
+
+static void RunTaskRunnerHandleVerificationTask(TaskTracker* tracker,
+                                                Task verify_task) {
+  // Pretend |verify_task| is posted to respect TaskTracker's contract.
+  EXPECT_TRUE(tracker->WillPostTask(&verify_task));
+
+  // Confirm that the test conditions are right (no TaskRunnerHandles set
+  // already).
+  EXPECT_FALSE(ThreadTaskRunnerHandle::IsSet());
+  EXPECT_FALSE(SequencedTaskRunnerHandle::IsSet());
+
+  testing::StrictMock<MockCanScheduleSequenceObserver> never_notified_observer;
+  auto sequence = tracker->WillScheduleSequence(
+      test::CreateSequenceWithTask(std::move(verify_task)),
+      &never_notified_observer);
+  ASSERT_TRUE(sequence);
+  tracker->RunAndPopNextTask(std::move(sequence), &never_notified_observer);
+
+  // TaskRunnerHandle state is reset outside of task's scope.
+  EXPECT_FALSE(ThreadTaskRunnerHandle::IsSet());
+  EXPECT_FALSE(SequencedTaskRunnerHandle::IsSet());
+}
+
+static void VerifyNoTaskRunnerHandle() {
+  EXPECT_FALSE(ThreadTaskRunnerHandle::IsSet());
+  EXPECT_FALSE(SequencedTaskRunnerHandle::IsSet());
+}
+
+TEST_P(TaskSchedulerTaskTrackerTest, TaskRunnerHandleIsNotSetOnParallel) {
+  // Create a task that will verify that TaskRunnerHandles are not set in its
+  // scope per no TaskRunner ref being set to it.
+  Task verify_task(FROM_HERE, BindOnce(&VerifyNoTaskRunnerHandle),
+                   TaskTraits(GetParam()), TimeDelta());
+
+  RunTaskRunnerHandleVerificationTask(&tracker_, std::move(verify_task));
+}
+
+static void VerifySequencedTaskRunnerHandle(
+    const SequencedTaskRunner* expected_task_runner) {
+  EXPECT_FALSE(ThreadTaskRunnerHandle::IsSet());
+  EXPECT_TRUE(SequencedTaskRunnerHandle::IsSet());
+  EXPECT_EQ(expected_task_runner, SequencedTaskRunnerHandle::Get());
+}
+
+TEST_P(TaskSchedulerTaskTrackerTest,
+       SequencedTaskRunnerHandleIsSetOnSequenced) {
+  scoped_refptr<SequencedTaskRunner> test_task_runner(new TestSimpleTaskRunner);
+
+  // Create a task that will verify that SequencedTaskRunnerHandle is properly
+  // set to |test_task_runner| in its scope per |sequenced_task_runner_ref|
+  // being set to it.
+  Task verify_task(FROM_HERE,
+                   BindOnce(&VerifySequencedTaskRunnerHandle,
+                            Unretained(test_task_runner.get())),
+                   TaskTraits(GetParam()), TimeDelta());
+  verify_task.sequenced_task_runner_ref = test_task_runner;
+
+  RunTaskRunnerHandleVerificationTask(&tracker_, std::move(verify_task));
+}
+
+static void VerifyThreadTaskRunnerHandle(
+    const SingleThreadTaskRunner* expected_task_runner) {
+  EXPECT_TRUE(ThreadTaskRunnerHandle::IsSet());
+  // SequencedTaskRunnerHandle inherits ThreadTaskRunnerHandle for thread.
+  EXPECT_TRUE(SequencedTaskRunnerHandle::IsSet());
+  EXPECT_EQ(expected_task_runner, ThreadTaskRunnerHandle::Get());
+}
+
+TEST_P(TaskSchedulerTaskTrackerTest,
+       ThreadTaskRunnerHandleIsSetOnSingleThreaded) {
+  scoped_refptr<SingleThreadTaskRunner> test_task_runner(
+      new TestSimpleTaskRunner);
+
+  // Create a task that will verify that ThreadTaskRunnerHandle is properly set
+  // to |test_task_runner| in its scope per |single_thread_task_runner_ref|
+  // being set on it.
+  Task verify_task(FROM_HERE,
+                   BindOnce(&VerifyThreadTaskRunnerHandle,
+                            Unretained(test_task_runner.get())),
+                   TaskTraits(GetParam()), TimeDelta());
+  verify_task.single_thread_task_runner_ref = test_task_runner;
+
+  RunTaskRunnerHandleVerificationTask(&tracker_, std::move(verify_task));
+}
+
+TEST_P(TaskSchedulerTaskTrackerTest, FlushPendingDelayedTask) {
+  Task delayed_task(FROM_HERE, DoNothing(), TaskTraits(GetParam()),
+                    TimeDelta::FromDays(1));
+  tracker_.WillPostTask(&delayed_task);
+  // FlushForTesting() should return even if the delayed task didn't run.
+  tracker_.FlushForTesting();
+}
+
+TEST_P(TaskSchedulerTaskTrackerTest, FlushAsyncForTestingPendingDelayedTask) {
+  Task delayed_task(FROM_HERE, DoNothing(), TaskTraits(GetParam()),
+                    TimeDelta::FromDays(1));
+  tracker_.WillPostTask(&delayed_task);
+  // FlushAsyncForTesting() should callback even if the delayed task didn't run.
+  bool called_back = false;
+  tracker_.FlushAsyncForTesting(
+      BindOnce([](bool* called_back) { *called_back = true; },
+               Unretained(&called_back)));
+  EXPECT_TRUE(called_back);
+}
+
+TEST_P(TaskSchedulerTaskTrackerTest, FlushPendingUndelayedTask) {
+  Task undelayed_task(FROM_HERE, DoNothing(), TaskTraits(GetParam()),
+                      TimeDelta());
+  tracker_.WillPostTask(&undelayed_task);
+
+  // FlushForTesting() shouldn't return before the undelayed task runs.
+  CallFlushFromAnotherThread();
+  PlatformThread::Sleep(TestTimeouts::tiny_timeout());
+  VERIFY_ASYNC_FLUSH_IN_PROGRESS();
+
+  // FlushForTesting() should return after the undelayed task runs.
+  DispatchAndRunTaskWithTracker(std::move(undelayed_task));
+  WAIT_FOR_ASYNC_FLUSH_RETURNED();
+}
+
+TEST_P(TaskSchedulerTaskTrackerTest, FlushAsyncForTestingPendingUndelayedTask) {
+  Task undelayed_task(FROM_HERE, DoNothing(), TaskTraits(GetParam()),
+                      TimeDelta());
+  tracker_.WillPostTask(&undelayed_task);
+
+  // FlushAsyncForTesting() shouldn't callback before the undelayed task runs.
+  WaitableEvent event;
+  tracker_.FlushAsyncForTesting(
+      BindOnce(&WaitableEvent::Signal, Unretained(&event)));
+  PlatformThread::Sleep(TestTimeouts::tiny_timeout());
+  EXPECT_FALSE(event.IsSignaled());
+
+  // FlushAsyncForTesting() should callback after the undelayed task runs.
+  DispatchAndRunTaskWithTracker(std::move(undelayed_task));
+  event.Wait();
+}
+
+TEST_P(TaskSchedulerTaskTrackerTest, PostTaskDuringFlush) {
+  Task undelayed_task(FROM_HERE, DoNothing(), TaskTraits(GetParam()),
+                      TimeDelta());
+  tracker_.WillPostTask(&undelayed_task);
+
+  // FlushForTesting() shouldn't return before the undelayed task runs.
+  CallFlushFromAnotherThread();
+  PlatformThread::Sleep(TestTimeouts::tiny_timeout());
+  VERIFY_ASYNC_FLUSH_IN_PROGRESS();
+
+  // Simulate posting another undelayed task.
+  Task other_undelayed_task(FROM_HERE, DoNothing(), TaskTraits(GetParam()),
+                            TimeDelta());
+  tracker_.WillPostTask(&other_undelayed_task);
+
+  // Run the first undelayed task.
+  DispatchAndRunTaskWithTracker(std::move(undelayed_task));
+
+  // FlushForTesting() shouldn't return before the second undelayed task runs.
+  PlatformThread::Sleep(TestTimeouts::tiny_timeout());
+  VERIFY_ASYNC_FLUSH_IN_PROGRESS();
+
+  // FlushForTesting() should return after the second undelayed task runs.
+  DispatchAndRunTaskWithTracker(std::move(other_undelayed_task));
+  WAIT_FOR_ASYNC_FLUSH_RETURNED();
+}
+
+TEST_P(TaskSchedulerTaskTrackerTest, PostTaskDuringFlushAsyncForTesting) {
+  Task undelayed_task(FROM_HERE, DoNothing(), TaskTraits(GetParam()),
+                      TimeDelta());
+  tracker_.WillPostTask(&undelayed_task);
+
+  // FlushAsyncForTesting() shouldn't callback before the undelayed task runs.
+  WaitableEvent event;
+  tracker_.FlushAsyncForTesting(
+      BindOnce(&WaitableEvent::Signal, Unretained(&event)));
+  PlatformThread::Sleep(TestTimeouts::tiny_timeout());
+  EXPECT_FALSE(event.IsSignaled());
+
+  // Simulate posting another undelayed task.
+  Task other_undelayed_task(FROM_HERE, DoNothing(), TaskTraits(GetParam()),
+                            TimeDelta());
+  tracker_.WillPostTask(&other_undelayed_task);
+
+  // Run the first undelayed task.
+  DispatchAndRunTaskWithTracker(std::move(undelayed_task));
+
+  // FlushAsyncForTesting() shouldn't callback before the second undelayed task
+  // runs.
+  PlatformThread::Sleep(TestTimeouts::tiny_timeout());
+  EXPECT_FALSE(event.IsSignaled());
+
+  // FlushAsyncForTesting() should callback after the second undelayed task
+  // runs.
+  DispatchAndRunTaskWithTracker(std::move(other_undelayed_task));
+  event.Wait();
+}
+
+TEST_P(TaskSchedulerTaskTrackerTest, RunDelayedTaskDuringFlush) {
+  // Simulate posting a delayed and an undelayed task.
+  Task delayed_task(FROM_HERE, DoNothing(), TaskTraits(GetParam()),
+                    TimeDelta::FromDays(1));
+  tracker_.WillPostTask(&delayed_task);
+  Task undelayed_task(FROM_HERE, DoNothing(), TaskTraits(GetParam()),
+                      TimeDelta());
+  tracker_.WillPostTask(&undelayed_task);
+
+  // FlushForTesting() shouldn't return before the undelayed task runs.
+  CallFlushFromAnotherThread();
+  PlatformThread::Sleep(TestTimeouts::tiny_timeout());
+  VERIFY_ASYNC_FLUSH_IN_PROGRESS();
+
+  // Run the delayed task.
+  DispatchAndRunTaskWithTracker(std::move(delayed_task));
+
+  // FlushForTesting() shouldn't return since there is still a pending undelayed
+  // task.
+  PlatformThread::Sleep(TestTimeouts::tiny_timeout());
+  VERIFY_ASYNC_FLUSH_IN_PROGRESS();
+
+  // Run the undelayed task.
+  DispatchAndRunTaskWithTracker(std::move(undelayed_task));
+
+  // FlushForTesting() should now return.
+  WAIT_FOR_ASYNC_FLUSH_RETURNED();
+}
+
+TEST_P(TaskSchedulerTaskTrackerTest, RunDelayedTaskDuringFlushAsyncForTesting) {
+  // Simulate posting a delayed and an undelayed task.
+  Task delayed_task(FROM_HERE, DoNothing(), TaskTraits(GetParam()),
+                    TimeDelta::FromDays(1));
+  tracker_.WillPostTask(&delayed_task);
+  Task undelayed_task(FROM_HERE, DoNothing(), TaskTraits(GetParam()),
+                      TimeDelta());
+  tracker_.WillPostTask(&undelayed_task);
+
+  // FlushAsyncForTesting() shouldn't callback before the undelayed task runs.
+  WaitableEvent event;
+  tracker_.FlushAsyncForTesting(
+      BindOnce(&WaitableEvent::Signal, Unretained(&event)));
+  PlatformThread::Sleep(TestTimeouts::tiny_timeout());
+  EXPECT_FALSE(event.IsSignaled());
+
+  // Run the delayed task.
+  DispatchAndRunTaskWithTracker(std::move(delayed_task));
+
+  // FlushAsyncForTesting() shouldn't callback since there is still a pending
+  // undelayed task.
+  PlatformThread::Sleep(TestTimeouts::tiny_timeout());
+  EXPECT_FALSE(event.IsSignaled());
+
+  // Run the undelayed task.
+  DispatchAndRunTaskWithTracker(std::move(undelayed_task));
+
+  // FlushAsyncForTesting() should now callback.
+  event.Wait();
+}
+
+TEST_P(TaskSchedulerTaskTrackerTest, FlushAfterShutdown) {
+  if (GetParam() == TaskShutdownBehavior::BLOCK_SHUTDOWN)
+    return;
+
+  // Simulate posting a task.
+  Task undelayed_task(FROM_HERE, DoNothing(), TaskTraits(GetParam()),
+                      TimeDelta());
+  tracker_.WillPostTask(&undelayed_task);
+
+  // Shutdown() should return immediately since there are no pending
+  // BLOCK_SHUTDOWN tasks.
+  tracker_.Shutdown();
+
+  // FlushForTesting() should return immediately after shutdown, even if an
+  // undelayed task hasn't run.
+  tracker_.FlushForTesting();
+}
+
+TEST_P(TaskSchedulerTaskTrackerTest, FlushAfterShutdownAsync) {
+  if (GetParam() == TaskShutdownBehavior::BLOCK_SHUTDOWN)
+    return;
+
+  // Simulate posting a task.
+  Task undelayed_task(FROM_HERE, DoNothing(), TaskTraits(GetParam()),
+                      TimeDelta());
+  tracker_.WillPostTask(&undelayed_task);
+
+  // Shutdown() should return immediately since there are no pending
+  // BLOCK_SHUTDOWN tasks.
+  tracker_.Shutdown();
+
+  // FlushForTesting() should callback immediately after shutdown, even if an
+  // undelayed task hasn't run.
+  bool called_back = false;
+  tracker_.FlushAsyncForTesting(
+      BindOnce([](bool* called_back) { *called_back = true; },
+               Unretained(&called_back)));
+  EXPECT_TRUE(called_back);
+}
+
+TEST_P(TaskSchedulerTaskTrackerTest, ShutdownDuringFlush) {
+  if (GetParam() == TaskShutdownBehavior::BLOCK_SHUTDOWN)
+    return;
+
+  // Simulate posting a task.
+  Task undelayed_task(FROM_HERE, DoNothing(), TaskTraits(GetParam()),
+                      TimeDelta());
+  tracker_.WillPostTask(&undelayed_task);
+
+  // FlushForTesting() shouldn't return before the undelayed task runs or
+  // shutdown completes.
+  CallFlushFromAnotherThread();
+  PlatformThread::Sleep(TestTimeouts::tiny_timeout());
+  VERIFY_ASYNC_FLUSH_IN_PROGRESS();
+
+  // Shutdown() should return immediately since there are no pending
+  // BLOCK_SHUTDOWN tasks.
+  tracker_.Shutdown();
+
+  // FlushForTesting() should now return, even if an undelayed task hasn't run.
+  WAIT_FOR_ASYNC_FLUSH_RETURNED();
+}
+
+TEST_P(TaskSchedulerTaskTrackerTest, ShutdownDuringFlushAsyncForTesting) {
+  if (GetParam() == TaskShutdownBehavior::BLOCK_SHUTDOWN)
+    return;
+
+  // Simulate posting a task.
+  Task undelayed_task(FROM_HERE, DoNothing(), TaskTraits(GetParam()),
+                      TimeDelta());
+  tracker_.WillPostTask(&undelayed_task);
+
+  // FlushAsyncForTesting() shouldn't callback before the undelayed task runs or
+  // shutdown completes.
+  WaitableEvent event;
+  tracker_.FlushAsyncForTesting(
+      BindOnce(&WaitableEvent::Signal, Unretained(&event)));
+  PlatformThread::Sleep(TestTimeouts::tiny_timeout());
+  EXPECT_FALSE(event.IsSignaled());
+
+  // Shutdown() should return immediately since there are no pending
+  // BLOCK_SHUTDOWN tasks.
+  tracker_.Shutdown();
+
+  // FlushAsyncForTesting() should now callback, even if an undelayed task
+  // hasn't run.
+  event.Wait();
+}
+
+TEST_P(TaskSchedulerTaskTrackerTest, DoublePendingFlushAsyncForTestingFails) {
+  Task undelayed_task(FROM_HERE, DoNothing(), TaskTraits(GetParam()),
+                      TimeDelta());
+  tracker_.WillPostTask(&undelayed_task);
+
+  // FlushAsyncForTesting() shouldn't callback before the undelayed task runs.
+  bool called_back = false;
+  tracker_.FlushAsyncForTesting(
+      BindOnce([](bool* called_back) { *called_back = true; },
+               Unretained(&called_back)));
+  EXPECT_FALSE(called_back);
+  EXPECT_DCHECK_DEATH({ tracker_.FlushAsyncForTesting(BindOnce([]() {})); });
+}
+
+INSTANTIATE_TEST_CASE_P(
+    ContinueOnShutdown,
+    TaskSchedulerTaskTrackerTest,
+    ::testing::Values(TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN));
+INSTANTIATE_TEST_CASE_P(
+    SkipOnShutdown,
+    TaskSchedulerTaskTrackerTest,
+    ::testing::Values(TaskShutdownBehavior::SKIP_ON_SHUTDOWN));
+INSTANTIATE_TEST_CASE_P(
+    BlockShutdown,
+    TaskSchedulerTaskTrackerTest,
+    ::testing::Values(TaskShutdownBehavior::BLOCK_SHUTDOWN));
+
+namespace {
+
+void ExpectSequenceToken(SequenceToken sequence_token) {
+  EXPECT_EQ(sequence_token, SequenceToken::GetForCurrentThread());
+}
+
+}  // namespace
+
+// Verify that SequenceToken::GetForCurrentThread() returns the Sequence's token
+// when a Task runs.
+TEST_F(TaskSchedulerTaskTrackerTest, CurrentSequenceToken) {
+  scoped_refptr<Sequence> sequence = MakeRefCounted<Sequence>();
+
+  const SequenceToken sequence_token = sequence->token();
+  Task task(FROM_HERE, Bind(&ExpectSequenceToken, sequence_token), TaskTraits(),
+            TimeDelta());
+  tracker_.WillPostTask(&task);
+
+  sequence->PushTask(std::move(task));
+
+  EXPECT_FALSE(SequenceToken::GetForCurrentThread().IsValid());
+  sequence = tracker_.WillScheduleSequence(std::move(sequence),
+                                           &never_notified_observer_);
+  ASSERT_TRUE(sequence);
+  tracker_.RunAndPopNextTask(std::move(sequence), &never_notified_observer_);
+  EXPECT_FALSE(SequenceToken::GetForCurrentThread().IsValid());
+}
+
+TEST_F(TaskSchedulerTaskTrackerTest, LoadWillPostAndRunBeforeShutdown) {
+  // Post and run tasks asynchronously.
+  std::vector<std::unique_ptr<ThreadPostingAndRunningTask>> threads;
+
+  for (size_t i = 0; i < kLoadTestNumIterations; ++i) {
+    threads.push_back(std::make_unique<ThreadPostingAndRunningTask>(
+        &tracker_, CreateTask(TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN),
+        ThreadPostingAndRunningTask::Action::WILL_POST_AND_RUN, true));
+    threads.back()->Start();
+
+    threads.push_back(std::make_unique<ThreadPostingAndRunningTask>(
+        &tracker_, CreateTask(TaskShutdownBehavior::SKIP_ON_SHUTDOWN),
+        ThreadPostingAndRunningTask::Action::WILL_POST_AND_RUN, true));
+    threads.back()->Start();
+
+    threads.push_back(std::make_unique<ThreadPostingAndRunningTask>(
+        &tracker_, CreateTask(TaskShutdownBehavior::BLOCK_SHUTDOWN),
+        ThreadPostingAndRunningTask::Action::WILL_POST_AND_RUN, true));
+    threads.back()->Start();
+  }
+
+  for (const auto& thread : threads)
+    thread->Join();
+
+  // Expect all tasks to be executed.
+  EXPECT_EQ(kLoadTestNumIterations * 3, NumTasksExecuted());
+
+  // Should return immediately because no tasks are blocking shutdown.
+  tracker_.Shutdown();
+}
+
+TEST_F(TaskSchedulerTaskTrackerTest,
+       LoadWillPostBeforeShutdownAndRunDuringShutdown) {
+  // Post tasks asynchronously.
+  std::vector<Task> tasks_continue_on_shutdown;
+  std::vector<Task> tasks_skip_on_shutdown;
+  std::vector<Task> tasks_block_shutdown;
+  for (size_t i = 0; i < kLoadTestNumIterations; ++i) {
+    tasks_continue_on_shutdown.push_back(
+        CreateTask(TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN));
+    tasks_skip_on_shutdown.push_back(
+        CreateTask(TaskShutdownBehavior::SKIP_ON_SHUTDOWN));
+    tasks_block_shutdown.push_back(
+        CreateTask(TaskShutdownBehavior::BLOCK_SHUTDOWN));
+  }
+
+  std::vector<std::unique_ptr<ThreadPostingAndRunningTask>> post_threads;
+  for (size_t i = 0; i < kLoadTestNumIterations; ++i) {
+    post_threads.push_back(std::make_unique<ThreadPostingAndRunningTask>(
+        &tracker_, &tasks_continue_on_shutdown[i],
+        ThreadPostingAndRunningTask::Action::WILL_POST, true));
+    post_threads.back()->Start();
+
+    post_threads.push_back(std::make_unique<ThreadPostingAndRunningTask>(
+        &tracker_, &tasks_skip_on_shutdown[i],
+        ThreadPostingAndRunningTask::Action::WILL_POST, true));
+    post_threads.back()->Start();
+
+    post_threads.push_back(std::make_unique<ThreadPostingAndRunningTask>(
+        &tracker_, &tasks_block_shutdown[i],
+        ThreadPostingAndRunningTask::Action::WILL_POST, true));
+    post_threads.back()->Start();
+  }
+
+  for (const auto& thread : post_threads)
+    thread->Join();
+
+  // Call Shutdown() asynchronously.
+  CallShutdownAsync();
+
+  // Run tasks asynchronously.
+  std::vector<std::unique_ptr<ThreadPostingAndRunningTask>> run_threads;
+  for (size_t i = 0; i < kLoadTestNumIterations; ++i) {
+    run_threads.push_back(std::make_unique<ThreadPostingAndRunningTask>(
+        &tracker_, std::move(tasks_continue_on_shutdown[i]),
+        ThreadPostingAndRunningTask::Action::RUN, false));
+    run_threads.back()->Start();
+
+    run_threads.push_back(std::make_unique<ThreadPostingAndRunningTask>(
+        &tracker_, std::move(tasks_skip_on_shutdown[i]),
+        ThreadPostingAndRunningTask::Action::RUN, false));
+    run_threads.back()->Start();
+
+    run_threads.push_back(std::make_unique<ThreadPostingAndRunningTask>(
+        &tracker_, std::move(tasks_block_shutdown[i]),
+        ThreadPostingAndRunningTask::Action::RUN, false));
+    run_threads.back()->Start();
+  }
+
+  for (const auto& thread : run_threads)
+    thread->Join();
+
+  WAIT_FOR_ASYNC_SHUTDOWN_COMPLETED();
+
+  // Expect BLOCK_SHUTDOWN tasks to have been executed.
+  EXPECT_EQ(kLoadTestNumIterations, NumTasksExecuted());
+}
+
+TEST_F(TaskSchedulerTaskTrackerTest, LoadWillPostAndRunDuringShutdown) {
+  // Inform |task_tracker_| that a BLOCK_SHUTDOWN task will be posted just to
+  // block shutdown.
+  Task block_shutdown_task(CreateTask(TaskShutdownBehavior::BLOCK_SHUTDOWN));
+  EXPECT_TRUE(tracker_.WillPostTask(&block_shutdown_task));
+
+  // Call Shutdown() asynchronously.
+  CallShutdownAsync();
+
+  // Post and run tasks asynchronously.
+  std::vector<std::unique_ptr<ThreadPostingAndRunningTask>> threads;
+
+  for (size_t i = 0; i < kLoadTestNumIterations; ++i) {
+    threads.push_back(std::make_unique<ThreadPostingAndRunningTask>(
+        &tracker_, CreateTask(TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN),
+        ThreadPostingAndRunningTask::Action::WILL_POST_AND_RUN, false));
+    threads.back()->Start();
+
+    threads.push_back(std::make_unique<ThreadPostingAndRunningTask>(
+        &tracker_, CreateTask(TaskShutdownBehavior::SKIP_ON_SHUTDOWN),
+        ThreadPostingAndRunningTask::Action::WILL_POST_AND_RUN, false));
+    threads.back()->Start();
+
+    threads.push_back(std::make_unique<ThreadPostingAndRunningTask>(
+        &tracker_, CreateTask(TaskShutdownBehavior::BLOCK_SHUTDOWN),
+        ThreadPostingAndRunningTask::Action::WILL_POST_AND_RUN, true));
+    threads.back()->Start();
+  }
+
+  for (const auto& thread : threads)
+    thread->Join();
+
+  // Expect BLOCK_SHUTDOWN tasks to have been executed.
+  EXPECT_EQ(kLoadTestNumIterations, NumTasksExecuted());
+
+  // Shutdown() shouldn't return before |block_shutdown_task| is executed.
+  VERIFY_ASYNC_SHUTDOWN_IN_PROGRESS();
+
+  // Unblock shutdown by running |block_shutdown_task|.
+  DispatchAndRunTaskWithTracker(std::move(block_shutdown_task));
+  EXPECT_EQ(kLoadTestNumIterations + 1, NumTasksExecuted());
+  WAIT_FOR_ASYNC_SHUTDOWN_COMPLETED();
+}
+
+// Verify that RunAndPopNextTask() returns the sequence from which it ran a task
+// when it can be rescheduled.
+TEST_F(TaskSchedulerTaskTrackerTest,
+       RunAndPopNextTaskReturnsSequenceToReschedule) {
+  Task task_1(FROM_HERE, DoNothing(), TaskTraits(), TimeDelta());
+  EXPECT_TRUE(tracker_.WillPostTask(&task_1));
+  Task task_2(FROM_HERE, DoNothing(), TaskTraits(), TimeDelta());
+  EXPECT_TRUE(tracker_.WillPostTask(&task_2));
+
+  scoped_refptr<Sequence> sequence =
+      test::CreateSequenceWithTask(std::move(task_1));
+  sequence->PushTask(std::move(task_2));
+  EXPECT_EQ(sequence, tracker_.WillScheduleSequence(sequence, nullptr));
+
+  EXPECT_EQ(sequence, tracker_.RunAndPopNextTask(sequence, nullptr));
+}
+
+// Verify that WillScheduleSequence() returns nullptr when it receives a
+// background sequence and the maximum number of background sequences that can
+// be scheduled concurrently is reached. Verify that an observer is notified
+// when a background sequence can be scheduled (i.e. when one of the previously
+// scheduled background sequences has run).
+TEST_F(TaskSchedulerTaskTrackerTest,
+       WillScheduleBackgroundSequenceWithMaxBackgroundSequences) {
+  constexpr int kMaxNumScheduledBackgroundSequences = 2;
+  TaskTracker tracker("Test", kMaxNumScheduledBackgroundSequences);
+
+  // Simulate posting |kMaxNumScheduledBackgroundSequences| background tasks
+  // and scheduling the associated sequences. This should succeed.
+  std::vector<scoped_refptr<Sequence>> scheduled_sequences;
+  testing::StrictMock<MockCanScheduleSequenceObserver> never_notified_observer;
+  for (int i = 0; i < kMaxNumScheduledBackgroundSequences; ++i) {
+    Task task(FROM_HERE, DoNothing(), TaskTraits(TaskPriority::BACKGROUND),
+              TimeDelta());
+    EXPECT_TRUE(tracker.WillPostTask(&task));
+    scoped_refptr<Sequence> sequence =
+        test::CreateSequenceWithTask(std::move(task));
+    EXPECT_EQ(sequence,
+              tracker.WillScheduleSequence(sequence, &never_notified_observer));
+    scheduled_sequences.push_back(std::move(sequence));
+  }
+
+  // Simulate posting extra background tasks and scheduling the associated
+  // sequences. This should fail because the maximum number of background
+  // sequences that can be scheduled concurrently is already reached.
+  std::vector<std::unique_ptr<bool>> extra_tasks_did_run;
+  std::vector<
+      std::unique_ptr<testing::StrictMock<MockCanScheduleSequenceObserver>>>
+      extra_observers;
+  std::vector<scoped_refptr<Sequence>> extra_sequences;
+  for (int i = 0; i < kMaxNumScheduledBackgroundSequences; ++i) {
+    extra_tasks_did_run.push_back(std::make_unique<bool>());
+    Task extra_task(
+        FROM_HERE,
+        BindOnce([](bool* extra_task_did_run) { *extra_task_did_run = true; },
+                 Unretained(extra_tasks_did_run.back().get())),
+        TaskTraits(TaskPriority::BACKGROUND), TimeDelta());
+    EXPECT_TRUE(tracker.WillPostTask(&extra_task));
+    extra_sequences.push_back(
+        test::CreateSequenceWithTask(std::move(extra_task)));
+    extra_observers.push_back(
+        std::make_unique<
+            testing::StrictMock<MockCanScheduleSequenceObserver>>());
+    EXPECT_EQ(nullptr,
+              tracker.WillScheduleSequence(extra_sequences.back(),
+                                           extra_observers.back().get()));
+  }
+
+  // Run the sequences scheduled at the beginning of the test. Expect an
+  // observer from |extra_observer| to be notified every time a task finishes to
+  // run.
+  for (int i = 0; i < kMaxNumScheduledBackgroundSequences; ++i) {
+    EXPECT_CALL(*extra_observers[i].get(),
+                MockOnCanScheduleSequence(extra_sequences[i].get()));
+    EXPECT_FALSE(tracker.RunAndPopNextTask(scheduled_sequences[i],
+                                           &never_notified_observer));
+    testing::Mock::VerifyAndClear(extra_observers[i].get());
+  }
+
+  // Run the extra sequences.
+  for (int i = 0; i < kMaxNumScheduledBackgroundSequences; ++i) {
+    EXPECT_FALSE(*extra_tasks_did_run[i]);
+    EXPECT_FALSE(tracker.RunAndPopNextTask(extra_sequences[i],
+                                           &never_notified_observer));
+    EXPECT_TRUE(*extra_tasks_did_run[i]);
+  }
+}
+
+namespace {
+
+void SetBool(bool* arg) {
+  ASSERT_TRUE(arg);
+  EXPECT_FALSE(*arg);
+  *arg = true;
+}
+
+}  // namespace
+
+// Verify that RunAndPopNextTask() doesn't reschedule the background sequence it
+// was assigned if there is a preempted background sequence with an earlier
+// sequence time (compared to the next task in the sequence assigned to
+// RunAndPopNextTask()).
+TEST_F(TaskSchedulerTaskTrackerTest,
+       RunNextBackgroundTaskWithEarlierPendingBackgroundTask) {
+  constexpr int kMaxNumScheduledBackgroundSequences = 1;
+  TaskTracker tracker("Test", kMaxNumScheduledBackgroundSequences);
+  testing::StrictMock<MockCanScheduleSequenceObserver> never_notified_observer;
+
+  // Simulate posting a background task and scheduling the associated sequence.
+  // This should succeed.
+  bool task_a_1_did_run = false;
+  Task task_a_1(FROM_HERE, BindOnce(&SetBool, Unretained(&task_a_1_did_run)),
+                TaskTraits(TaskPriority::BACKGROUND), TimeDelta());
+  EXPECT_TRUE(tracker.WillPostTask(&task_a_1));
+  scoped_refptr<Sequence> sequence_a =
+      test::CreateSequenceWithTask(std::move(task_a_1));
+  EXPECT_EQ(sequence_a,
+            tracker.WillScheduleSequence(sequence_a, &never_notified_observer));
+
+  // Simulate posting an extra background task and scheduling the associated
+  // sequence. This should fail because the maximum number of background
+  // sequences that can be scheduled concurrently is already reached.
+  bool task_b_1_did_run = false;
+  Task task_b_1(FROM_HERE, BindOnce(&SetBool, Unretained(&task_b_1_did_run)),
+                TaskTraits(TaskPriority::BACKGROUND), TimeDelta());
+  EXPECT_TRUE(tracker.WillPostTask(&task_b_1));
+  scoped_refptr<Sequence> sequence_b =
+      test::CreateSequenceWithTask(std::move(task_b_1));
+  testing::StrictMock<MockCanScheduleSequenceObserver> task_b_1_observer;
+  EXPECT_FALSE(tracker.WillScheduleSequence(sequence_b, &task_b_1_observer));
+
+  // Wait to be sure that the sequence time of |task_a_2| is after the sequenced
+  // time of |task_b_1|.
+  PlatformThread::Sleep(TestTimeouts::tiny_timeout());
+
+  // Post an extra background task in |sequence_a|.
+  bool task_a_2_did_run = false;
+  Task task_a_2(FROM_HERE, BindOnce(&SetBool, Unretained(&task_a_2_did_run)),
+                TaskTraits(TaskPriority::BACKGROUND), TimeDelta());
+  EXPECT_TRUE(tracker.WillPostTask(&task_a_2));
+  sequence_a->PushTask(std::move(task_a_2));
+
+  // Run the first task in |sequence_a|. RunAndPopNextTask() should return
+  // nullptr since |sequence_a| can't be rescheduled immediately.
+  // |task_b_1_observer| should be notified that |sequence_b| can be scheduled.
+  testing::StrictMock<MockCanScheduleSequenceObserver> task_a_2_observer;
+  EXPECT_CALL(task_b_1_observer, MockOnCanScheduleSequence(sequence_b.get()));
+  EXPECT_FALSE(tracker.RunAndPopNextTask(sequence_a, &task_a_2_observer));
+  testing::Mock::VerifyAndClear(&task_b_1_observer);
+  EXPECT_TRUE(task_a_1_did_run);
+
+  // Run the first task in |sequence_b|. RunAndPopNextTask() should return
+  // nullptr since |sequence_b| is empty after popping a task from it.
+  // |task_a_2_observer| should be notified that |sequence_a| can be
+  // scheduled.
+  EXPECT_CALL(task_a_2_observer, MockOnCanScheduleSequence(sequence_a.get()));
+  EXPECT_FALSE(tracker.RunAndPopNextTask(sequence_b, &never_notified_observer));
+  testing::Mock::VerifyAndClear(&task_a_2_observer);
+  EXPECT_TRUE(task_b_1_did_run);
+
+  // Run the first task in |sequence_a|. RunAndPopNextTask() should return
+  // nullptr since |sequence_b| is empty after popping a task from it. No
+  // observer should be notified.
+  EXPECT_FALSE(tracker.RunAndPopNextTask(sequence_a, &never_notified_observer));
+  EXPECT_TRUE(task_a_2_did_run);
+}
+
+// Verify that preempted background sequences are scheduled when shutdown
+// starts.
+TEST_F(TaskSchedulerTaskTrackerTest,
+       SchedulePreemptedBackgroundSequencesOnShutdown) {
+  constexpr int kMaxNumScheduledBackgroundSequences = 0;
+  TaskTracker tracker("Test", kMaxNumScheduledBackgroundSequences);
+  testing::StrictMock<MockCanScheduleSequenceObserver> observer;
+
+  // Simulate scheduling sequences. TaskTracker should prevent this.
+  std::vector<scoped_refptr<Sequence>> preempted_sequences;
+  for (int i = 0; i < 3; ++i) {
+    Task task(FROM_HERE, DoNothing(),
+              TaskTraits(TaskPriority::BACKGROUND,
+                         TaskShutdownBehavior::BLOCK_SHUTDOWN),
+              TimeDelta());
+    EXPECT_TRUE(tracker.WillPostTask(&task));
+    scoped_refptr<Sequence> sequence =
+        test::CreateSequenceWithTask(std::move(task));
+    EXPECT_FALSE(tracker.WillScheduleSequence(sequence, &observer));
+    preempted_sequences.push_back(std::move(sequence));
+
+    // Wait to be sure that tasks have different |sequenced_time|.
+    PlatformThread::Sleep(TestTimeouts::tiny_timeout());
+  }
+
+  // Perform shutdown. Expect |preempted_sequences| to be scheduled in posting
+  // order.
+  {
+    testing::InSequence in_sequence;
+    for (auto& preempted_sequence : preempted_sequences) {
+      EXPECT_CALL(observer, MockOnCanScheduleSequence(preempted_sequence.get()))
+          .WillOnce(testing::Invoke([&tracker](Sequence* sequence) {
+            // Run the task to unblock shutdown.
+            tracker.RunAndPopNextTask(sequence, nullptr);
+          }));
+    }
+    tracker.Shutdown();
+  }
+}
+
+namespace {
+
+class WaitAllowedTestThread : public SimpleThread {
+ public:
+  WaitAllowedTestThread() : SimpleThread("WaitAllowedTestThread") {}
+
+ private:
+  void Run() override {
+    auto task_tracker = std::make_unique<TaskTracker>("Test");
+
+    // Waiting is allowed by default. Expect TaskTracker to disallow it before
+    // running a task without the WithBaseSyncPrimitives() trait.
+    internal::AssertBaseSyncPrimitivesAllowed();
+    Task task_without_sync_primitives(
+        FROM_HERE, Bind([]() {
+          EXPECT_DCHECK_DEATH({ internal::AssertBaseSyncPrimitivesAllowed(); });
+        }),
+        TaskTraits(), TimeDelta());
+    EXPECT_TRUE(task_tracker->WillPostTask(&task_without_sync_primitives));
+    testing::StrictMock<MockCanScheduleSequenceObserver>
+        never_notified_observer;
+    auto sequence_without_sync_primitives = task_tracker->WillScheduleSequence(
+        test::CreateSequenceWithTask(std::move(task_without_sync_primitives)),
+        &never_notified_observer);
+    ASSERT_TRUE(sequence_without_sync_primitives);
+    task_tracker->RunAndPopNextTask(std::move(sequence_without_sync_primitives),
+                                    &never_notified_observer);
+
+    // Disallow waiting. Expect TaskTracker to allow it before running a task
+    // with the WithBaseSyncPrimitives() trait.
+    ThreadRestrictions::DisallowWaiting();
+    Task task_with_sync_primitives(
+        FROM_HERE, Bind([]() {
+          // Shouldn't fail.
+          internal::AssertBaseSyncPrimitivesAllowed();
+        }),
+        TaskTraits(WithBaseSyncPrimitives()), TimeDelta());
+    EXPECT_TRUE(task_tracker->WillPostTask(&task_with_sync_primitives));
+    auto sequence_with_sync_primitives = task_tracker->WillScheduleSequence(
+        test::CreateSequenceWithTask(std::move(task_with_sync_primitives)),
+        &never_notified_observer);
+    ASSERT_TRUE(sequence_with_sync_primitives);
+    task_tracker->RunAndPopNextTask(std::move(sequence_with_sync_primitives),
+                                    &never_notified_observer);
+
+    ScopedAllowBaseSyncPrimitivesForTesting
+        allow_wait_in_task_tracker_destructor;
+    task_tracker.reset();
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(WaitAllowedTestThread);
+};
+
+}  // namespace
+
+// Verify that AssertIOAllowed() succeeds only for a WithBaseSyncPrimitives()
+// task.
+TEST(TaskSchedulerTaskTrackerWaitAllowedTest, WaitAllowed) {
+  // Run the test on the separate thread since it is not possible to reset the
+  // "wait allowed" bit of a thread without being a friend of
+  // ThreadRestrictions.
+  testing::GTEST_FLAG(death_test_style) = "threadsafe";
+  WaitAllowedTestThread wait_allowed_test_thread;
+  wait_allowed_test_thread.Start();
+  wait_allowed_test_thread.Join();
+}
+
+// Verify that TaskScheduler.TaskLatency.* histograms are correctly recorded
+// when a task runs.
+TEST(TaskSchedulerTaskTrackerHistogramTest, TaskLatency) {
+  auto statistics_recorder = StatisticsRecorder::CreateTemporaryForTesting();
+
+  TaskTracker tracker("Test");
+  testing::StrictMock<MockCanScheduleSequenceObserver> never_notified_observer;
+
+  struct {
+    const TaskTraits traits;
+    const char* const expected_histogram;
+  } static constexpr kTests[] = {
+      {{TaskPriority::BACKGROUND},
+       "TaskScheduler.TaskLatencyMicroseconds.Test."
+       "BackgroundTaskPriority"},
+      {{MayBlock(), TaskPriority::BACKGROUND},
+       "TaskScheduler.TaskLatencyMicroseconds.Test."
+       "BackgroundTaskPriority_MayBlock"},
+      {{WithBaseSyncPrimitives(), TaskPriority::BACKGROUND},
+       "TaskScheduler.TaskLatencyMicroseconds.Test."
+       "BackgroundTaskPriority_MayBlock"},
+      {{TaskPriority::USER_VISIBLE},
+       "TaskScheduler.TaskLatencyMicroseconds.Test."
+       "UserVisibleTaskPriority"},
+      {{MayBlock(), TaskPriority::USER_VISIBLE},
+       "TaskScheduler.TaskLatencyMicroseconds.Test."
+       "UserVisibleTaskPriority_MayBlock"},
+      {{WithBaseSyncPrimitives(), TaskPriority::USER_VISIBLE},
+       "TaskScheduler.TaskLatencyMicroseconds.Test."
+       "UserVisibleTaskPriority_MayBlock"},
+      {{TaskPriority::USER_BLOCKING},
+       "TaskScheduler.TaskLatencyMicroseconds.Test."
+       "UserBlockingTaskPriority"},
+      {{MayBlock(), TaskPriority::USER_BLOCKING},
+       "TaskScheduler.TaskLatencyMicroseconds.Test."
+       "UserBlockingTaskPriority_MayBlock"},
+      {{WithBaseSyncPrimitives(), TaskPriority::USER_BLOCKING},
+       "TaskScheduler.TaskLatencyMicroseconds.Test."
+       "UserBlockingTaskPriority_MayBlock"}};
+
+  for (const auto& test : kTests) {
+    Task task(FROM_HERE, DoNothing(), test.traits, TimeDelta());
+    ASSERT_TRUE(tracker.WillPostTask(&task));
+
+    HistogramTester tester;
+
+    auto sequence = tracker.WillScheduleSequence(
+        test::CreateSequenceWithTask(std::move(task)),
+        &never_notified_observer);
+    ASSERT_TRUE(sequence);
+    tracker.RunAndPopNextTask(std::move(sequence), &never_notified_observer);
+    tester.ExpectTotalCount(test.expected_histogram, 1);
+  }
+}
+
+}  // namespace internal
+}  // namespace base
diff --git a/base/task_scheduler/task_unittest.cc b/base/task_scheduler/task_unittest.cc
new file mode 100644
index 0000000..31a59de
--- /dev/null
+++ b/base/task_scheduler/task_unittest.cc
@@ -0,0 +1,60 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/task_scheduler/task.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/location.h"
+#include "base/task_scheduler/task_traits.h"
+#include "base/time/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace internal {
+
+// Verify that the shutdown behavior of a BLOCK_SHUTDOWN delayed task is
+// adjusted to SKIP_ON_SHUTDOWN. The shutown behavior of other delayed tasks
+// should not change.
+TEST(TaskSchedulerTaskTest, ShutdownBehaviorChangeWithDelay) {
+  Task continue_on_shutdown(FROM_HERE, DoNothing(),
+                            {TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
+                            TimeDelta::FromSeconds(1));
+  EXPECT_EQ(TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN,
+            continue_on_shutdown.traits.shutdown_behavior());
+
+  Task skip_on_shutdown(FROM_HERE, DoNothing(),
+                        {TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
+                        TimeDelta::FromSeconds(1));
+  EXPECT_EQ(TaskShutdownBehavior::SKIP_ON_SHUTDOWN,
+            skip_on_shutdown.traits.shutdown_behavior());
+
+  Task block_shutdown(FROM_HERE, DoNothing(),
+                      {TaskShutdownBehavior::BLOCK_SHUTDOWN},
+                      TimeDelta::FromSeconds(1));
+  EXPECT_EQ(TaskShutdownBehavior::SKIP_ON_SHUTDOWN,
+            block_shutdown.traits.shutdown_behavior());
+}
+
+// Verify that the shutdown behavior of undelayed tasks is not adjusted.
+TEST(TaskSchedulerTaskTest, NoShutdownBehaviorChangeNoDelay) {
+  Task continue_on_shutdown(FROM_HERE, DoNothing(),
+                            {TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
+                            TimeDelta());
+  EXPECT_EQ(TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN,
+            continue_on_shutdown.traits.shutdown_behavior());
+
+  Task skip_on_shutdown(FROM_HERE, DoNothing(),
+                        {TaskShutdownBehavior::SKIP_ON_SHUTDOWN}, TimeDelta());
+  EXPECT_EQ(TaskShutdownBehavior::SKIP_ON_SHUTDOWN,
+            skip_on_shutdown.traits.shutdown_behavior());
+
+  Task block_shutdown(FROM_HERE, DoNothing(),
+                      {TaskShutdownBehavior::BLOCK_SHUTDOWN}, TimeDelta());
+  EXPECT_EQ(TaskShutdownBehavior::BLOCK_SHUTDOWN,
+            block_shutdown.traits.shutdown_behavior());
+}
+
+}  // namespace internal
+}  // namespace base
diff --git a/base/task_scheduler/test_task_factory.cc b/base/task_scheduler/test_task_factory.cc
new file mode 100644
index 0000000..0867547
--- /dev/null
+++ b/base/task_scheduler/test_task_factory.cc
@@ -0,0 +1,106 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/task_scheduler/test_task_factory.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace internal {
+namespace test {
+
+TestTaskFactory::TestTaskFactory(scoped_refptr<TaskRunner> task_runner,
+                                 ExecutionMode execution_mode)
+    : cv_(&lock_),
+      task_runner_(std::move(task_runner)),
+      execution_mode_(execution_mode) {
+  // Detach |thread_checker_| from the current thread. It will be attached to
+  // the first thread that calls ThreadCheckerImpl::CalledOnValidThread().
+  thread_checker_.DetachFromThread();
+}
+
+TestTaskFactory::~TestTaskFactory() {
+  WaitForAllTasksToRun();
+}
+
+bool TestTaskFactory::PostTask(PostNestedTask post_nested_task,
+                               OnceClosure after_task_closure) {
+  AutoLock auto_lock(lock_);
+  return task_runner_->PostTask(
+      FROM_HERE, BindOnce(&TestTaskFactory::RunTaskCallback, Unretained(this),
+                          num_posted_tasks_++, post_nested_task,
+                          std::move(after_task_closure)));
+}
+
+void TestTaskFactory::WaitForAllTasksToRun() const {
+  AutoLock auto_lock(lock_);
+  while (ran_tasks_.size() < num_posted_tasks_)
+    cv_.Wait();
+}
+
+void TestTaskFactory::RunTaskCallback(size_t task_index,
+                                      PostNestedTask post_nested_task,
+                                      OnceClosure after_task_closure) {
+  if (post_nested_task == PostNestedTask::YES)
+    PostTask(PostNestedTask::NO, Closure());
+
+  EXPECT_TRUE(task_runner_->RunsTasksInCurrentSequence());
+
+  // Verify TaskRunnerHandles are set as expected in the task's scope.
+  switch (execution_mode_) {
+    case ExecutionMode::PARALLEL:
+      EXPECT_FALSE(ThreadTaskRunnerHandle::IsSet());
+      EXPECT_FALSE(SequencedTaskRunnerHandle::IsSet());
+      break;
+    case ExecutionMode::SEQUENCED:
+      EXPECT_FALSE(ThreadTaskRunnerHandle::IsSet());
+      EXPECT_TRUE(SequencedTaskRunnerHandle::IsSet());
+      EXPECT_EQ(task_runner_, SequencedTaskRunnerHandle::Get());
+      break;
+    case ExecutionMode::SINGLE_THREADED:
+      // SequencedTaskRunnerHandle inherits from ThreadTaskRunnerHandle so
+      // both are expected to be "set" in the SINGLE_THREADED case.
+      EXPECT_TRUE(ThreadTaskRunnerHandle::IsSet());
+      EXPECT_TRUE(SequencedTaskRunnerHandle::IsSet());
+      EXPECT_EQ(task_runner_, ThreadTaskRunnerHandle::Get());
+      EXPECT_EQ(task_runner_, SequencedTaskRunnerHandle::Get());
+      break;
+  }
+
+  {
+    AutoLock auto_lock(lock_);
+
+    DCHECK_LE(task_index, num_posted_tasks_);
+
+    if ((execution_mode_ == ExecutionMode::SINGLE_THREADED ||
+         execution_mode_ == ExecutionMode::SEQUENCED) &&
+        task_index != ran_tasks_.size()) {
+      ADD_FAILURE() << "A task didn't run in the expected order.";
+    }
+
+    if (execution_mode_ == ExecutionMode::SINGLE_THREADED)
+      EXPECT_TRUE(thread_checker_.CalledOnValidThread());
+
+    if (ran_tasks_.find(task_index) != ran_tasks_.end())
+      ADD_FAILURE() << "A task ran more than once.";
+    ran_tasks_.insert(task_index);
+
+    cv_.Signal();
+  }
+
+  if (!after_task_closure.is_null())
+    std::move(after_task_closure).Run();
+}
+
+}  // namespace test
+}  // namespace internal
+}  // namespace base
diff --git a/base/task_scheduler/test_task_factory.h b/base/task_scheduler/test_task_factory.h
new file mode 100644
index 0000000..300b7bf
--- /dev/null
+++ b/base/task_scheduler/test_task_factory.h
@@ -0,0 +1,99 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TASK_SCHEDULER_TEST_TASK_FACTORY_H_
+#define BASE_TASK_SCHEDULER_TEST_TASK_FACTORY_H_
+
+#include <stddef.h>
+
+#include <unordered_set>
+
+#include "base/callback_forward.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/synchronization/condition_variable.h"
+#include "base/synchronization/lock.h"
+#include "base/task_runner.h"
+#include "base/task_scheduler/task_traits.h"
+#include "base/task_scheduler/test_utils.h"
+#include "base/threading/thread_checker_impl.h"
+
+namespace base {
+namespace internal {
+namespace test {
+
+// A TestTaskFactory posts tasks to a TaskRunner and verifies that they run as
+// expected. Generates a test failure when:
+// - The RunsTasksInCurrentSequence() method of the TaskRunner returns false on
+//   a thread on which a Task is run.
+// - The TaskRunnerHandles set in the context of the task don't match what's
+//   expected for the tested ExecutionMode.
+// - The ExecutionMode of the TaskRunner is SEQUENCED or SINGLE_THREADED and
+//   Tasks don't run in posting order.
+// - The ExecutionMode of the TaskRunner is SINGLE_THREADED and Tasks don't run
+//   on the same thread.
+// - A Task runs more than once.
+class TestTaskFactory {
+ public:
+  enum class PostNestedTask {
+    YES,
+    NO,
+  };
+
+  // Constructs a TestTaskFactory that posts tasks to |task_runner|.
+  // |execution_mode| is the ExecutionMode of |task_runner|.
+  TestTaskFactory(scoped_refptr<TaskRunner> task_runner,
+                  ExecutionMode execution_mode);
+
+  ~TestTaskFactory();
+
+  // Posts a task. The posted task will:
+  // - Post a new task if |post_nested_task| is YES. The nested task won't run
+  //   |after_task_closure|.
+  // - Verify conditions in which the task runs (see potential failures above).
+  // - Run |after_task_closure| if it is not null.
+  bool PostTask(PostNestedTask post_nested_task,
+                OnceClosure after_task_closure);
+
+  // Waits for all tasks posted by PostTask() to start running. It is not
+  // guaranteed that the tasks have completed their execution when this returns.
+  void WaitForAllTasksToRun() const;
+
+  const TaskRunner* task_runner() const { return task_runner_.get(); }
+
+ private:
+  void RunTaskCallback(size_t task_index,
+                       PostNestedTask post_nested_task,
+                       OnceClosure after_task_closure);
+
+  // Synchronizes access to all members.
+  mutable Lock lock_;
+
+  // Condition variable signaled when a task runs.
+  mutable ConditionVariable cv_;
+
+  // Task runner through which this factory posts tasks.
+  const scoped_refptr<TaskRunner> task_runner_;
+
+  // Execution mode of |task_runner_|.
+  const ExecutionMode execution_mode_;
+
+  // Number of tasks posted by PostTask().
+  size_t num_posted_tasks_ = 0;
+
+  // Indexes of tasks that ran.
+  std::unordered_set<size_t> ran_tasks_;
+
+  // Used to verify that all tasks run on the same thread when |execution_mode_|
+  // is SINGLE_THREADED.
+  ThreadCheckerImpl thread_checker_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestTaskFactory);
+};
+
+}  // namespace test
+}  // namespace internal
+}  // namespace base
+
+#endif  // BASE_TASK_SCHEDULER_TEST_TASK_FACTORY_H_
diff --git a/base/test/android/java_handler_thread_helpers.cc b/base/test/android/java_handler_thread_helpers.cc
deleted file mode 100644
index 925dc9d..0000000
--- a/base/test/android/java_handler_thread_helpers.cc
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/test/android/java_handler_thread_helpers.h"
-
-#include "base/android/java_handler_thread.h"
-#include "base/message_loop/message_loop_current.h"
-#include "base/synchronization/waitable_event.h"
-#include "jni/JavaHandlerThreadHelpers_jni.h"
-
-namespace base {
-namespace android {
-
-// static
-std::unique_ptr<JavaHandlerThread> JavaHandlerThreadHelpers::CreateJavaFirst() {
-  return std::make_unique<JavaHandlerThread>(
-      Java_JavaHandlerThreadHelpers_testAndGetJavaHandlerThread(
-          base::android::AttachCurrentThread()));
-}
-
-// static
-void JavaHandlerThreadHelpers::ThrowExceptionAndAbort(WaitableEvent* event) {
-  JNIEnv* env = AttachCurrentThread();
-  Java_JavaHandlerThreadHelpers_throwException(env);
-  DCHECK(HasException(env));
-  base::MessageLoopCurrentForUI::Get()->Abort();
-  event->Signal();
-}
-
-// static
-bool JavaHandlerThreadHelpers::IsExceptionTestException(
-    ScopedJavaLocalRef<jthrowable> exception) {
-  JNIEnv* env = AttachCurrentThread();
-  return Java_JavaHandlerThreadHelpers_isExceptionTestException(env, exception);
-}
-
-}  // namespace android
-}  // namespace base
diff --git a/base/test/android/java_handler_thread_helpers.h b/base/test/android/java_handler_thread_helpers.h
deleted file mode 100644
index 5f05cbc..0000000
--- a/base/test/android/java_handler_thread_helpers.h
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef BASE_ANDROID_JAVA_HANDLER_THREAD_FOR_TESTING_H_
-#define BASE_ANDROID_JAVA_HANDLER_THREAD_FOR_TESTING_H_
-
-#include <jni.h>
-
-#include <memory>
-
-#include "base/android/scoped_java_ref.h"
-
-namespace base {
-
-class WaitableEvent;
-
-namespace android {
-
-class JavaHandlerThread;
-
-// Test-only helpers for working with JavaHandlerThread.
-class JavaHandlerThreadHelpers {
- public:
-  // Create the Java peer first and test that it works before connecting to the
-  // native object.
-  static std::unique_ptr<JavaHandlerThread> CreateJavaFirst();
-
-  static void ThrowExceptionAndAbort(WaitableEvent* event);
-
-  static bool IsExceptionTestException(
-      ScopedJavaLocalRef<jthrowable> exception);
-
- private:
-  JavaHandlerThreadHelpers() = default;
-  ~JavaHandlerThreadHelpers() = default;
-};
-
-}  // namespace android
-}  // namespace base
-
-#endif  // BASE_ANDROID_JAVA_HANDLER_THREAD_FOR_TESTING_H_
diff --git a/base/test/android/javatests/src/org/chromium/base/test/params/ParameterProvider.java b/base/test/android/javatests/src/org/chromium/base/test/params/ParameterProvider.java
deleted file mode 100644
index 9bf27bd..0000000
--- a/base/test/android/javatests/src/org/chromium/base/test/params/ParameterProvider.java
+++ /dev/null
@@ -1,11 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.base.test.params;
-
-/**
- * Generator to use generate arguments for parameterized test methods.
- * @see ParameterAnnotations.UseMethodParameter
- */
-public interface ParameterProvider { Iterable<ParameterSet> getParameters(); }
diff --git a/base/test/android/javatests/src/org/chromium/base/test/util/AnnotationProcessingUtils.java b/base/test/android/javatests/src/org/chromium/base/test/util/AnnotationProcessingUtils.java
deleted file mode 100644
index d335412..0000000
--- a/base/test/android/javatests/src/org/chromium/base/test/util/AnnotationProcessingUtils.java
+++ /dev/null
@@ -1,259 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.base.test.util;
-
-import android.support.annotation.Nullable;
-
-import org.junit.runner.Description;
-
-import org.chromium.base.VisibleForTesting;
-
-import java.lang.annotation.Annotation;
-import java.lang.reflect.AnnotatedElement;
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Queue;
-import java.util.Set;
-
-/**
- * Utility class to help with processing annotations, going around the code to collect them, etc.
- */
-public abstract class AnnotationProcessingUtils {
-    /**
-     * Returns the closest instance of the requested annotation or null if there is none.
-     * See {@link AnnotationExtractor} for context of "closest".
-     */
-    @SuppressWarnings("unchecked")
-    public static <A extends Annotation> A getAnnotation(Description description, Class<A> clazz) {
-        AnnotationExtractor extractor = new AnnotationExtractor(clazz);
-        return (A) extractor.getClosest(extractor.getMatchingAnnotations(description));
-    }
-
-    /**
-     * Returns the closest instance of the requested annotation or null if there is none.
-     * See {@link AnnotationExtractor} for context of "closest".
-     */
-    @SuppressWarnings("unchecked")
-    public static <A extends Annotation> A getAnnotation(AnnotatedElement element, Class<A> clazz) {
-        AnnotationExtractor extractor = new AnnotationExtractor(clazz);
-        return (A) extractor.getClosest(extractor.getMatchingAnnotations(element));
-    }
-
-    /** See {@link AnnotationExtractor} for details about the output sorting order. */
-    @SuppressWarnings("unchecked")
-    public static <A extends Annotation> List<A> getAnnotations(
-            Description description, Class<A> annotationType) {
-        return (List<A>) new AnnotationExtractor(annotationType)
-                .getMatchingAnnotations(description);
-    }
-
-    /** See {@link AnnotationExtractor} for details about the output sorting order. */
-    @SuppressWarnings("unchecked")
-    public static <A extends Annotation> List<A> getAnnotations(
-            AnnotatedElement annotatedElement, Class<A> annotationType) {
-        return (List<A>) new AnnotationExtractor(annotationType)
-                .getMatchingAnnotations(annotatedElement);
-    }
-
-    private static boolean isChromiumAnnotation(Annotation annotation) {
-        Package pkg = annotation.annotationType().getPackage();
-        return pkg != null && pkg.getName().startsWith("org.chromium");
-    }
-
-    /**
-     * Processes various types of annotated elements ({@link Class}es, {@link Annotation}s,
-     * {@link Description}s, etc.) and extracts the targeted annotations from it. The output will be
-     * sorted in BFS-like order.
-     *
-     * For example, for a method we would get in reverse order:
-     * - the method annotations
-     * - the meta-annotations present on the method annotations,
-     * - the class annotations
-     * - the meta-annotations present on the class annotations,
-     * - the annotations present on the super class,
-     * - the meta-annotations present on the super class annotations,
-     * - etc.
-     *
-     * When multiple annotations are targeted, if more than one is picked up at a given level (for
-     * example directly on the method), they will be returned in the reverse order that they were
-     * provided to the constructor.
-     *
-     * Note: We return the annotations in reverse order because we assume that if some processing
-     * is going to be made on related annotations, the later annotations would likely override
-     * modifications made by the former.
-     *
-     * Note: While resolving meta annotations, we don't expand the explorations to annotations types
-     * that have already been visited. Please file a bug and assign to dgn@ if you think it caused
-     * an issue.
-     */
-    public static class AnnotationExtractor {
-        private final List<Class<? extends Annotation>> mAnnotationTypes;
-        private final Comparator<Class<? extends Annotation>> mAnnotationTypeComparator;
-        private final Comparator<Annotation> mAnnotationComparator;
-
-        @SafeVarargs
-        public AnnotationExtractor(Class<? extends Annotation>... additionalTypes) {
-            this(Arrays.asList(additionalTypes));
-        }
-
-        public AnnotationExtractor(List<Class<? extends Annotation>> additionalTypes) {
-            assert !additionalTypes.isEmpty();
-            mAnnotationTypes = Collections.unmodifiableList(additionalTypes);
-            mAnnotationTypeComparator =
-                    (t1, t2) -> mAnnotationTypes.indexOf(t1) - mAnnotationTypes.indexOf(t2);
-            mAnnotationComparator = (t1, t2)
-                    -> mAnnotationTypeComparator.compare(t1.annotationType(), t2.annotationType());
-        }
-
-        public List<Annotation> getMatchingAnnotations(Description description) {
-            return getMatchingAnnotations(new AnnotatedNode.DescriptionNode(description));
-        }
-
-        public List<Annotation> getMatchingAnnotations(AnnotatedElement annotatedElement) {
-            AnnotatedNode annotatedNode;
-            if (annotatedElement instanceof Method) {
-                annotatedNode = new AnnotatedNode.MethodNode((Method) annotatedElement);
-            } else if (annotatedElement instanceof Class) {
-                annotatedNode = new AnnotatedNode.ClassNode((Class) annotatedElement);
-            } else {
-                throw new IllegalArgumentException("Unsupported type for " + annotatedElement);
-            }
-
-            return getMatchingAnnotations(annotatedNode);
-        }
-
-        /**
-         * For a given list obtained from the extractor, returns the {@link Annotation} that would
-         * be closest from the extraction point, or {@code null} if the list is empty.
-         */
-        @Nullable
-        public Annotation getClosest(List<Annotation> annotationList) {
-            return annotationList.isEmpty() ? null : annotationList.get(annotationList.size() - 1);
-        }
-
-        @VisibleForTesting
-        Comparator<Class<? extends Annotation>> getTypeComparator() {
-            return mAnnotationTypeComparator;
-        }
-
-        private List<Annotation> getMatchingAnnotations(AnnotatedNode annotatedNode) {
-            List<Annotation> collectedAnnotations = new ArrayList<>();
-            Queue<Annotation> workingSet = new LinkedList<>();
-            Set<Class<? extends Annotation>> visited = new HashSet<>();
-
-            AnnotatedNode currentAnnotationLayer = annotatedNode;
-            while (currentAnnotationLayer != null) {
-                queueAnnotations(currentAnnotationLayer.getAnnotations(), workingSet);
-
-                while (!workingSet.isEmpty()) {
-                    sweepAnnotations(collectedAnnotations, workingSet, visited);
-                }
-
-                currentAnnotationLayer = currentAnnotationLayer.getParent();
-            }
-
-            return collectedAnnotations;
-        }
-
-        private void queueAnnotations(List<Annotation> annotations, Queue<Annotation> workingSet) {
-            Collections.sort(annotations, mAnnotationComparator);
-            workingSet.addAll(annotations);
-        }
-
-        private void sweepAnnotations(List<Annotation> collectedAnnotations,
-                Queue<Annotation> workingSet, Set<Class<? extends Annotation>> visited) {
-            // 1. Grab node at the front of the working set.
-            Annotation annotation = workingSet.remove();
-
-            // 2. If it's an annotation of interest, put it aside for the output.
-            if (mAnnotationTypes.contains(annotation.annotationType())) {
-                collectedAnnotations.add(0, annotation);
-            }
-
-            // 3. Check if we can get skip some redundant iterations and avoid cycles.
-            if (!visited.add(annotation.annotationType())) return;
-            if (!isChromiumAnnotation(annotation)) return;
-
-            // 4. Expand the working set
-            queueAnnotations(Arrays.asList(annotation.annotationType().getDeclaredAnnotations()),
-                    workingSet);
-        }
-    }
-
-    /**
-     * Abstraction to hide differences between Class, Method and Description with regards to their
-     * annotations and what should be analyzed next.
-     */
-    private static abstract class AnnotatedNode {
-        @Nullable
-        abstract AnnotatedNode getParent();
-
-        abstract List<Annotation> getAnnotations();
-
-        static class DescriptionNode extends AnnotatedNode {
-            final Description mDescription;
-
-            DescriptionNode(Description description) {
-                mDescription = description;
-            }
-
-            @Nullable
-            @Override
-            AnnotatedNode getParent() {
-                return new ClassNode(mDescription.getTestClass());
-            }
-
-            @Override
-            List<Annotation> getAnnotations() {
-                return new ArrayList<>(mDescription.getAnnotations());
-            }
-        }
-
-        static class ClassNode extends AnnotatedNode {
-            final Class<?> mClass;
-
-            ClassNode(Class<?> clazz) {
-                mClass = clazz;
-            }
-
-            @Nullable
-            @Override
-            AnnotatedNode getParent() {
-                Class<?> superClass = mClass.getSuperclass();
-                return superClass == null ? null : new ClassNode(superClass);
-            }
-
-            @Override
-            List<Annotation> getAnnotations() {
-                return Arrays.asList(mClass.getDeclaredAnnotations());
-            }
-        }
-
-        static class MethodNode extends AnnotatedNode {
-            final Method mMethod;
-
-            MethodNode(Method method) {
-                mMethod = method;
-            }
-
-            @Nullable
-            @Override
-            AnnotatedNode getParent() {
-                return new ClassNode(mMethod.getDeclaringClass());
-            }
-
-            @Override
-            List<Annotation> getAnnotations() {
-                return Arrays.asList(mMethod.getDeclaredAnnotations());
-            }
-        }
-    }
-}
diff --git a/base/test/android/javatests/src/org/chromium/base/test/util/AnnotationRule.java b/base/test/android/javatests/src/org/chromium/base/test/util/AnnotationRule.java
deleted file mode 100644
index a361ac3..0000000
--- a/base/test/android/javatests/src/org/chromium/base/test/util/AnnotationRule.java
+++ /dev/null
@@ -1,139 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.base.test.util;
-
-import android.support.annotation.CallSuper;
-import android.support.annotation.Nullable;
-
-import org.junit.rules.ExternalResource;
-import org.junit.runner.Description;
-import org.junit.runners.model.Statement;
-
-import java.lang.annotation.Annotation;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.ListIterator;
-
-/**
- * Test rule that collects specific annotations to help with test set up and tear down. It is set up
- * with a list of annotations to look for and exposes the ones picked up on the test through
- * {@link #getAnnotations()} and related methods.
- *
- * Note: The rule always apply, whether it picked up annotations or not.
- *
- * Usage:
- *
- * <pre>
- * public class Test {
- *    &#64;Rule
- *    public AnnotationRule rule = new AnnotationRule(Foo.class) {
- *          &#64;Override
- *          protected void before() { ... }
- *
- *          &#64;Override
- *          protected void after() { ... }
- *    };
- *
- *    &#64;Test
- *    &#64;Foo
- *    public void myTest() { ... }
- * }
- * </pre>
- *
- * It can also be used to trigger for multiple annotations:
- *
- * <pre>
- * &#64;DisableFoo
- * public class Test {
- *    &#64;Rule
- *    public AnnotationRule rule = new AnnotationRule(EnableFoo.class, DisableFoo.class) {
- *          &#64;Override
- *          protected void before() {
- *            // Loops through all the picked up annotations. For myTest(), it would process
- *            // DisableFoo first, then EnableFoo.
- *            for (Annotation annotation : getAnnotations()) {
- *                if (annotation instanceof EnableFoo) { ... }
- *                else if (annotation instanceof DisableFoo) { ... }
- *            }
- *          }
- *
- *          &#64;Override
- *          protected void after() {
- *            // For myTest(), would return EnableFoo as it's directly set on the method.
- *            Annotation a = getClosestAnnotation();
- *            ...
- *          }
- *    };
- *
- *    &#64;Test
- *    &#64;EnableFoo
- *    public void myTest() { ... }
- * }
- * </pre>
- *
- * @see AnnotationProcessingUtils.AnnotationExtractor
- */
-public abstract class AnnotationRule extends ExternalResource {
-    private final AnnotationProcessingUtils.AnnotationExtractor mAnnotationExtractor;
-    private List<Annotation> mCollectedAnnotations;
-    private Description mTestDescription;
-
-    @SafeVarargs
-    public AnnotationRule(Class<? extends Annotation> firstAnnotationType,
-            Class<? extends Annotation>... additionalTypes) {
-        List<Class<? extends Annotation>> mAnnotationTypes = new ArrayList<>();
-        mAnnotationTypes.add(firstAnnotationType);
-        mAnnotationTypes.addAll(Arrays.asList(additionalTypes));
-        mAnnotationExtractor = new AnnotationProcessingUtils.AnnotationExtractor(mAnnotationTypes);
-    }
-
-    @CallSuper
-    @Override
-    public Statement apply(Statement base, Description description) {
-        mTestDescription = description;
-
-        mCollectedAnnotations = mAnnotationExtractor.getMatchingAnnotations(description);
-
-        // Return the wrapped statement to execute before() and after().
-        return super.apply(base, description);
-    }
-
-    /** @return {@link Description} of the current test. */
-    protected Description getTestDescription() {
-        return mTestDescription;
-    }
-
-    /**
-     * @return The collected annotations that match the declared type(s).
-     * @throws NullPointerException if this is called before annotations have been collected,
-     * which happens when the rule is applied to the {@link Statement}.
-     */
-    protected List<Annotation> getAnnotations() {
-        return Collections.unmodifiableList(mCollectedAnnotations);
-    }
-
-    /**
-     * @return The closest annotation matching the provided type, or {@code null} if there is none.
-     */
-    @SuppressWarnings("unchecked")
-    protected @Nullable <A extends Annotation> A getAnnotation(Class<A> annnotationType) {
-        ListIterator<Annotation> iteratorFromEnd =
-                mCollectedAnnotations.listIterator(mCollectedAnnotations.size());
-        while (iteratorFromEnd.hasPrevious()) {
-            Annotation annotation = iteratorFromEnd.previous();
-            if (annnotationType.isAssignableFrom(annotation.annotationType())) {
-                return (A) annotation;
-            }
-        }
-        return null;
-    }
-
-    protected @Nullable Annotation getClosestAnnotation() {
-        if (mCollectedAnnotations.isEmpty()) return null;
-        return mCollectedAnnotations.get(mCollectedAnnotations.size() - 1);
-    }
-}
diff --git a/base/test/android/junit/src/org/chromium/base/test/util/AnnotationProcessingUtilsTest.java b/base/test/android/junit/src/org/chromium/base/test/util/AnnotationProcessingUtilsTest.java
deleted file mode 100644
index 9acd141..0000000
--- a/base/test/android/junit/src/org/chromium/base/test/util/AnnotationProcessingUtilsTest.java
+++ /dev/null
@@ -1,377 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.base.test.util;
-
-import static org.hamcrest.Matchers.contains;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.fail;
-import static org.junit.runner.Description.createTestDescription;
-
-import org.junit.Ignore;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TestRule;
-import org.junit.runner.Description;
-import org.junit.runner.RunWith;
-import org.junit.runners.BlockJUnit4ClassRunner;
-import org.junit.runners.model.FrameworkMethod;
-import org.junit.runners.model.InitializationError;
-import org.junit.runners.model.Statement;
-
-import org.chromium.base.test.util.AnnotationProcessingUtils.AnnotationExtractor;
-
-import java.lang.annotation.Annotation;
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-import java.util.Arrays;
-import java.util.Comparator;
-import java.util.List;
-
-/** Test for {@link AnnotationProcessingUtils}. */
-@RunWith(BlockJUnit4ClassRunner.class)
-public class AnnotationProcessingUtilsTest {
-    @Test
-    public void testGetTargetAnnotation_NotOnClassNorMethod() {
-        TargetAnnotation retrievedAnnotation;
-
-        retrievedAnnotation = AnnotationProcessingUtils.getAnnotation(
-                createTestDescription(
-                        ClassWithoutTargetAnnotation.class, "methodWithoutAnnotation"),
-                TargetAnnotation.class);
-        assertNull(retrievedAnnotation);
-    }
-
-    @Test
-    public void testGetTargetAnnotation_NotOnClassButOnMethod() {
-        TargetAnnotation retrievedAnnotation;
-
-        retrievedAnnotation = AnnotationProcessingUtils.getAnnotation(
-                getTest(ClassWithoutTargetAnnotation.class, "methodWithTargetAnnotation"),
-                TargetAnnotation.class);
-        assertNotNull(retrievedAnnotation);
-    }
-
-    @Test
-    public void testGetTargetAnnotation_NotOnClassDifferentOneOnMethod() {
-        TargetAnnotation retrievedAnnotation;
-
-        retrievedAnnotation = AnnotationProcessingUtils.getAnnotation(
-                getTest(ClassWithoutTargetAnnotation.class, "methodWithAnnotatedAnnotation"),
-                TargetAnnotation.class);
-        assertNull(retrievedAnnotation);
-    }
-
-    @Test
-    public void testGetTargetAnnotation_OnClassButNotOnMethod() {
-        TargetAnnotation retrievedAnnotation;
-
-        retrievedAnnotation = AnnotationProcessingUtils.getAnnotation(
-                getTest(ClassWithAnnotation.class, "methodWithoutAnnotation"),
-                TargetAnnotation.class);
-        assertNotNull(retrievedAnnotation);
-        assertEquals(Location.Class, retrievedAnnotation.value());
-    }
-
-    @Test
-    public void testGetTargetAnnotation_OnClassAndMethod() {
-        TargetAnnotation retrievedAnnotation;
-
-        retrievedAnnotation = AnnotationProcessingUtils.getAnnotation(
-                getTest(ClassWithAnnotation.class, "methodWithTargetAnnotation"),
-                TargetAnnotation.class);
-        assertNotNull(retrievedAnnotation);
-        assertEquals(Location.Method, retrievedAnnotation.value());
-    }
-
-    @Test
-    @Ignore("Rules not supported yet.")
-    public void testGetTargetAnnotation_OnRuleButNotOnMethod() {
-        TargetAnnotation retrievedAnnotation;
-
-        retrievedAnnotation = AnnotationProcessingUtils.getAnnotation(
-                getTest(ClassWithRule.class, "methodWithoutAnnotation"), TargetAnnotation.class);
-        assertNotNull(retrievedAnnotation);
-        assertEquals(Location.Rule, retrievedAnnotation.value());
-    }
-
-    @Test
-    @Ignore("Rules not supported yet.")
-    public void testGetTargetAnnotation_OnRuleAndMethod() {
-        TargetAnnotation retrievedAnnotation;
-
-        retrievedAnnotation = AnnotationProcessingUtils.getAnnotation(
-                getTest(ClassWithRule.class, "methodWithTargetAnnotation"), TargetAnnotation.class);
-        assertNotNull(retrievedAnnotation);
-        assertEquals(Location.Method, retrievedAnnotation.value());
-    }
-
-    @Test
-    public void testGetMetaAnnotation_Indirectly() {
-        MetaAnnotation retrievedAnnotation;
-
-        retrievedAnnotation = AnnotationProcessingUtils.getAnnotation(
-                getTest(ClassWithoutTargetAnnotation.class, "methodWithAnnotatedAnnotation"),
-                MetaAnnotation.class);
-        assertNotNull(retrievedAnnotation);
-    }
-
-    @Test
-    public void testGetAllTargetAnnotations() {
-        List<TargetAnnotation> retrievedAnnotations;
-
-        retrievedAnnotations = AnnotationProcessingUtils.getAnnotations(
-                getTest(ClassWithAnnotation.class, "methodWithTargetAnnotation"),
-                TargetAnnotation.class);
-        assertEquals(2, retrievedAnnotations.size());
-        assertEquals(Location.Class, retrievedAnnotations.get(0).value());
-        assertEquals(Location.Method, retrievedAnnotations.get(1).value());
-    }
-
-    @Test
-    public void testGetAllTargetAnnotations_OnParentClass() {
-        List<TargetAnnotation> retrievedAnnotations;
-
-        retrievedAnnotations = AnnotationProcessingUtils.getAnnotations(
-                getTest(DerivedClassWithoutAnnotation.class, "newMethodWithoutAnnotation"),
-                TargetAnnotation.class);
-        assertEquals(1, retrievedAnnotations.size());
-        assertEquals(Location.Class, retrievedAnnotations.get(0).value());
-    }
-
-    @Test
-    public void testGetAllTargetAnnotations_OnDerivedMethodAndParentClass() {
-        List<TargetAnnotation> retrievedAnnotations;
-
-        retrievedAnnotations = AnnotationProcessingUtils.getAnnotations(
-                getTest(DerivedClassWithoutAnnotation.class, "newMethodWithTargetAnnotation"),
-                TargetAnnotation.class);
-        assertEquals(2, retrievedAnnotations.size());
-        assertEquals(Location.Class, retrievedAnnotations.get(0).value());
-        assertEquals(Location.DerivedMethod, retrievedAnnotations.get(1).value());
-    }
-
-    @Test
-    public void testGetAllTargetAnnotations_OnDerivedMethodAndParentClassAndMethod() {
-        List<TargetAnnotation> retrievedAnnotations;
-
-        retrievedAnnotations = AnnotationProcessingUtils.getAnnotations(
-                getTest(DerivedClassWithoutAnnotation.class, "methodWithTargetAnnotation"),
-                TargetAnnotation.class);
-        // We should not look at the base implementation of the method. Mostly it should not happen
-        // in the context of tests.
-        assertEquals(2, retrievedAnnotations.size());
-        assertEquals(Location.Class, retrievedAnnotations.get(0).value());
-        assertEquals(Location.DerivedMethod, retrievedAnnotations.get(1).value());
-    }
-
-    @Test
-    public void testGetAllTargetAnnotations_OnDerivedParentAndParentClass() {
-        List<TargetAnnotation> retrievedAnnotations;
-
-        retrievedAnnotations = AnnotationProcessingUtils.getAnnotations(
-                getTest(DerivedClassWithAnnotation.class, "methodWithoutAnnotation"),
-                TargetAnnotation.class);
-        assertEquals(2, retrievedAnnotations.size());
-        assertEquals(Location.Class, retrievedAnnotations.get(0).value());
-        assertEquals(Location.DerivedClass, retrievedAnnotations.get(1).value());
-    }
-
-    @Test
-    public void testGetAllAnnotations() {
-        List<Annotation> annotations;
-
-        AnnotationExtractor annotationExtractor = new AnnotationExtractor(
-                TargetAnnotation.class, MetaAnnotation.class, AnnotatedAnnotation.class);
-        annotations = annotationExtractor.getMatchingAnnotations(
-                getTest(DerivedClassWithAnnotation.class, "methodWithTwoAnnotations"));
-        assertEquals(5, annotations.size());
-
-        // Retrieved annotation order:
-        // On Parent Class
-        assertEquals(TargetAnnotation.class, annotations.get(0).annotationType());
-        assertEquals(Location.Class, ((TargetAnnotation) annotations.get(0)).value());
-
-        // On Class
-        assertEquals(TargetAnnotation.class, annotations.get(1).annotationType());
-        assertEquals(Location.DerivedClass, ((TargetAnnotation) annotations.get(1)).value());
-
-        // Meta-annotations from method
-        assertEquals(MetaAnnotation.class, annotations.get(2).annotationType());
-
-        // On Method
-        assertEquals(AnnotatedAnnotation.class, annotations.get(3).annotationType());
-        assertEquals(TargetAnnotation.class, annotations.get(4).annotationType());
-        assertEquals(Location.DerivedMethod, ((TargetAnnotation) annotations.get(4)).value());
-    }
-
-    @SuppressWarnings("unchecked")
-    @Test
-    public void testAnnotationExtractorSortOrder_UnknownAnnotations() {
-        AnnotationExtractor annotationExtractor = new AnnotationExtractor(Target.class);
-        Comparator<Class<? extends Annotation>> comparator =
-                annotationExtractor.getTypeComparator();
-        List<Class<? extends Annotation>> testList =
-                Arrays.asList(Rule.class, Test.class, Override.class, Target.class, Rule.class);
-        testList.sort(comparator);
-        assertThat("Unknown annotations should not be reordered and come before the known ones.",
-                testList,
-                contains(Rule.class, Test.class, Override.class, Rule.class, Target.class));
-    }
-
-    @SuppressWarnings("unchecked")
-    @Test
-    public void testAnnotationExtractorSortOrder_KnownAnnotations() {
-        AnnotationExtractor annotationExtractor =
-                new AnnotationExtractor(Test.class, Target.class, Rule.class);
-        Comparator<Class<? extends Annotation>> comparator =
-                annotationExtractor.getTypeComparator();
-        List<Class<? extends Annotation>> testList =
-                Arrays.asList(Rule.class, Test.class, Override.class, Target.class, Rule.class);
-        testList.sort(comparator);
-        assertThat(
-                "Known annotations should be sorted in the same order as provided to the extractor",
-                testList,
-                contains(Override.class, Test.class, Target.class, Rule.class, Rule.class));
-    }
-
-    private static Description getTest(Class<?> klass, String testName) {
-        Description description = null;
-        try {
-            description = new DummyTestRunner(klass).describe(testName);
-        } catch (InitializationError initializationError) {
-            initializationError.printStackTrace();
-            fail("DummyTestRunner initialization failed:" + initializationError.getMessage());
-        }
-        if (description == null) {
-            fail("Not test named '" + testName + "' in class" + klass.getSimpleName());
-        }
-        return description;
-    }
-
-    // region Test Data: Annotations and dummy test classes
-    private enum Location { Unspecified, Class, Method, Rule, DerivedClass, DerivedMethod }
-
-    @Retention(RetentionPolicy.RUNTIME)
-    @Target({ElementType.TYPE, ElementType.METHOD})
-    private @interface TargetAnnotation {
-        Location value() default Location.Unspecified;
-    }
-
-    @Retention(RetentionPolicy.RUNTIME)
-    @Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE, ElementType.METHOD})
-    private @interface MetaAnnotation {}
-
-    @Retention(RetentionPolicy.RUNTIME)
-    @Target({ElementType.TYPE, ElementType.METHOD})
-    @MetaAnnotation
-    private @interface AnnotatedAnnotation {}
-
-    private @interface SimpleAnnotation {}
-
-    @SimpleAnnotation
-    private static class ClassWithoutTargetAnnotation {
-        @Test
-        public void methodWithoutAnnotation() {}
-
-        @Test
-        @TargetAnnotation
-        public void methodWithTargetAnnotation() {}
-
-        @Test
-        @AnnotatedAnnotation
-        public void methodWithAnnotatedAnnotation() {}
-    }
-
-    @TargetAnnotation(Location.Class)
-    private static class ClassWithAnnotation {
-        @Test
-        public void methodWithoutAnnotation() {}
-
-        @Test
-        @TargetAnnotation(Location.Method)
-        public void methodWithTargetAnnotation() {}
-
-        @Test
-        @MetaAnnotation
-        public void methodWithMetaAnnotation() {}
-
-        @Test
-        @AnnotatedAnnotation
-        public void methodWithAnnotatedAnnotation() {}
-    }
-
-    private static class DerivedClassWithoutAnnotation extends ClassWithAnnotation {
-        @Test
-        public void newMethodWithoutAnnotation() {}
-
-        @Test
-        @TargetAnnotation(Location.DerivedMethod)
-        public void newMethodWithTargetAnnotation() {}
-
-        @Test
-        @Override
-        @TargetAnnotation(Location.DerivedMethod)
-        public void methodWithTargetAnnotation() {}
-    }
-
-    @TargetAnnotation(Location.DerivedClass)
-    private static class DerivedClassWithAnnotation extends ClassWithAnnotation {
-        @Test
-        public void newMethodWithoutAnnotation() {}
-
-        @Test
-        @AnnotatedAnnotation
-        @TargetAnnotation(Location.DerivedMethod)
-        public void methodWithTwoAnnotations() {}
-    }
-
-    private static class ClassWithRule {
-        @Rule
-        Rule1 mRule = new Rule1();
-
-        @Test
-        public void methodWithoutAnnotation() {}
-
-        @Test
-        @TargetAnnotation
-        public void methodWithTargetAnnotation() {}
-    }
-
-    @TargetAnnotation(Location.Rule)
-    @MetaAnnotation
-    private static class Rule1 implements TestRule {
-        @Override
-        public Statement apply(Statement statement, Description description) {
-            return null;
-        }
-    }
-
-    private static class DummyTestRunner extends BlockJUnit4ClassRunner {
-        public DummyTestRunner(Class<?> klass) throws InitializationError {
-            super(klass);
-        }
-
-        @Override
-        protected void collectInitializationErrors(List<Throwable> errors) {
-            // Do nothing. BlockJUnit4ClassRunner requires the class to be public, but we don't
-            // want/need it.
-        }
-
-        public Description describe(String testName) {
-            List<FrameworkMethod> tests = getTestClass().getAnnotatedMethods(Test.class);
-            for (FrameworkMethod testMethod : tests) {
-                if (testMethod.getName().equals(testName)) return describeChild(testMethod);
-            }
-            return null;
-        }
-    }
-
-    // endregion
-    }
diff --git a/base/test/android/url_utils.cc b/base/test/android/url_utils.cc
deleted file mode 100644
index 7d2a8ed..0000000
--- a/base/test/android/url_utils.cc
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/test/android/url_utils.h"
-
-#include "base/android/jni_string.h"
-#include "base/android/scoped_java_ref.h"
-#include "jni/UrlUtils_jni.h"
-
-namespace base {
-namespace android {
-
-FilePath GetIsolatedTestRoot() {
-  JNIEnv* env = base::android::AttachCurrentThread();
-  ScopedJavaLocalRef<jstring> jtest_data_dir =
-      Java_UrlUtils_getIsolatedTestRoot(env);
-  base::FilePath test_data_dir(
-      base::android::ConvertJavaStringToUTF8(env, jtest_data_dir));
-  return test_data_dir;
-}
-
-}  // namespace android
-}  // namespace base
diff --git a/base/test/android/url_utils.h b/base/test/android/url_utils.h
deleted file mode 100644
index 3769bd2..0000000
--- a/base/test/android/url_utils.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef BASE_TEST_ANDROID_URL_UTILS_H_
-#define BASE_TEST_ANDROID_URL_UTILS_H_
-
-#include <jni.h>
-
-#include "base/base_export.h"
-#include "base/files/file_path.h"
-
-namespace base {
-namespace android {
-
-// Returns the root of the test data directory. This function will call into
-// Java class UrlUtils through JNI bridge.
-BASE_EXPORT FilePath GetIsolatedTestRoot();
-
-}  // namespace android
-}  // namespace base
-
-#endif  // BASE_TEST_ANDROID_URL_UTILS_H_
diff --git a/base/test/fuzzed_data_provider.cc b/base/test/fuzzed_data_provider.cc
new file mode 100644
index 0000000..b2d443a
--- /dev/null
+++ b/base/test/fuzzed_data_provider.cc
@@ -0,0 +1,98 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/fuzzed_data_provider.h"
+
+#include <algorithm>
+#include <limits>
+
+#include "base/logging.h"
+
+namespace base {
+
+FuzzedDataProvider::FuzzedDataProvider(const uint8_t* data, size_t size)
+    : remaining_data_(reinterpret_cast<const char*>(data), size) {}
+
+FuzzedDataProvider::~FuzzedDataProvider() = default;
+
+std::string FuzzedDataProvider::ConsumeBytes(size_t num_bytes) {
+  num_bytes = std::min(num_bytes, remaining_data_.length());
+  StringPiece result(remaining_data_.data(), num_bytes);
+  remaining_data_ = remaining_data_.substr(num_bytes);
+  return result.as_string();
+}
+
+std::string FuzzedDataProvider::ConsumeRemainingBytes() {
+  return ConsumeBytes(remaining_data_.length());
+}
+
+uint32_t FuzzedDataProvider::ConsumeUint32InRange(uint32_t min, uint32_t max) {
+  CHECK_LE(min, max);
+
+  uint32_t range = max - min;
+  uint32_t offset = 0;
+  uint32_t result = 0;
+
+  while (offset < 32 && (range >> offset) > 0 && !remaining_data_.empty()) {
+    // Pull bytes off the end of the seed data. Experimentally, this seems to
+    // allow the fuzzer to more easily explore the input space. This makes
+    // sense, since it works by modifying inputs that caused new code to run,
+    // and this data is often used to encode length of data read by
+    // ConsumeBytes. Separating out read lengths makes it easier modify the
+    // contents of the data that is actually read.
+    uint8_t next_byte = remaining_data_.back();
+    remaining_data_.remove_suffix(1);
+    result = (result << 8) | next_byte;
+    offset += 8;
+  }
+
+  // Avoid division by 0, in the case |range + 1| results in overflow.
+  if (range == std::numeric_limits<uint32_t>::max())
+    return result;
+
+  return min + result % (range + 1);
+}
+
+std::string FuzzedDataProvider::ConsumeRandomLengthString(size_t max_length) {
+  // Reads bytes from start of |remaining_data_|. Maps "\\" to "\", and maps "\"
+  // followed by anything else to the end of the string. As a result of this
+  // logic, a fuzzer can insert characters into the string, and the string will
+  // be lengthened to include those new characters, resulting in a more stable
+  // fuzzer than picking the length of a string independently from picking its
+  // contents.
+  std::string out;
+  for (size_t i = 0; i < max_length && !remaining_data_.empty(); ++i) {
+    char next = remaining_data_[0];
+    remaining_data_.remove_prefix(1);
+    if (next == '\\' && !remaining_data_.empty()) {
+      next = remaining_data_[0];
+      remaining_data_.remove_prefix(1);
+      if (next != '\\')
+        return out;
+    }
+    out += next;
+  }
+  return out;
+}
+
+int FuzzedDataProvider::ConsumeInt32InRange(int min, int max) {
+  CHECK_LE(min, max);
+
+  uint32_t range = max - min;
+  return min + ConsumeUint32InRange(0, range);
+}
+
+bool FuzzedDataProvider::ConsumeBool() {
+  return (ConsumeUint8() & 0x01) == 0x01;
+}
+
+uint8_t FuzzedDataProvider::ConsumeUint8() {
+  return ConsumeUint32InRange(0, 0xFF);
+}
+
+uint16_t FuzzedDataProvider::ConsumeUint16() {
+  return ConsumeUint32InRange(0, 0xFFFF);
+}
+
+}  // namespace base
diff --git a/base/test/fuzzed_data_provider.h b/base/test/fuzzed_data_provider.h
new file mode 100644
index 0000000..425c820
--- /dev/null
+++ b/base/test/fuzzed_data_provider.h
@@ -0,0 +1,80 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_FUZZED_DATA_PROVIDER_H_
+#define BASE_TEST_FUZZED_DATA_PROVIDER_H_
+
+#include <stdint.h>
+
+#include <string>
+
+#include "base/base_export.h"
+#include "base/macros.h"
+#include "base/strings/string_piece.h"
+
+namespace base {
+
+// Utility class to break up fuzzer input for multiple consumers. Whenever run
+// on the same input, provides the same output, as long as its methods are
+// called in the same order, with the same arguments.
+class FuzzedDataProvider {
+ public:
+  // |data| is an array of length |size| that the FuzzedDataProvider wraps to
+  // provide more granular access. |data| must outlive the FuzzedDataProvider.
+  FuzzedDataProvider(const uint8_t* data, size_t size);
+  ~FuzzedDataProvider();
+
+  // Returns a std::string containing |num_bytes| of input data. If fewer than
+  // |num_bytes| of data remain, returns a shorter std::string containing all
+  // of the data that's left.
+  std::string ConsumeBytes(size_t num_bytes);
+
+  // Returns a std::string containing all remaining bytes of the input data.
+  std::string ConsumeRemainingBytes();
+
+  // Returns a std::string of length from 0 to |max_length|. When it runs out of
+  // input data, returns what remains of the input. Designed to be more stable
+  // with respect to a fuzzer inserting characters than just picking a random
+  // length and then consuming that many bytes with ConsumeBytes().
+  std::string ConsumeRandomLengthString(size_t max_length);
+
+  // Returns a number in the range [min, max] by consuming bytes from the input
+  // data. The value might not be uniformly distributed in the given range. If
+  // there's no input data left, always returns |min|. |min| must be less than
+  // or equal to |max|.
+  uint32_t ConsumeUint32InRange(uint32_t min, uint32_t max);
+  int ConsumeInt32InRange(int min, int max);
+
+  // Returns a bool, or false when no data remains.
+  bool ConsumeBool();
+
+  // Returns a uint8_t from the input or 0 if nothing remains. This is
+  // equivalent to ConsumeUint32InRange(0, 0xFF).
+  uint8_t ConsumeUint8();
+
+  // Returns a uint16_t from the input. If fewer than 2 bytes of data remain
+  // will fill the most significant bytes with 0. This is equivalent to
+  // ConsumeUint32InRange(0, 0xFFFF).
+  uint16_t ConsumeUint16();
+
+  // Returns a value from |array|, consuming as many bytes as needed to do so.
+  // |array| must be a fixed-size array. Equivalent to
+  // array[ConsumeUint32InRange(sizeof(array)-1)];
+  template <typename Type, size_t size>
+  Type PickValueInArray(Type (&array)[size]) {
+    return array[ConsumeUint32InRange(0, size - 1)];
+  }
+
+  // Reports the remaining bytes available for fuzzed input.
+  size_t remaining_bytes() { return remaining_data_.length(); }
+
+ private:
+  StringPiece remaining_data_;
+
+  DISALLOW_COPY_AND_ASSIGN(FuzzedDataProvider);
+};
+
+}  // namespace base
+
+#endif  // BASE_TEST_FUZZED_DATA_PROVIDER_H_
diff --git a/base/test/gtest_xml_unittest_result_printer.cc b/base/test/gtest_xml_unittest_result_printer.cc
new file mode 100644
index 0000000..558a986
--- /dev/null
+++ b/base/test/gtest_xml_unittest_result_printer.cc
@@ -0,0 +1,162 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/gtest_xml_unittest_result_printer.h"
+
+#include "base/base64.h"
+#include "base/command_line.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/test/test_switches.h"
+#include "base/time/time.h"
+
+namespace base {
+
+namespace {
+const int kDefaultTestPartResultsLimit = 10;
+
+const char kTestPartLesultsLimitExceeded[] =
+    "Test part results limit exceeded. Use --test-launcher-test-part-limit to "
+    "increase or disable limit.";
+}  // namespace
+
+XmlUnitTestResultPrinter::XmlUnitTestResultPrinter()
+    : output_file_(nullptr), open_failed_(false) {}
+
+XmlUnitTestResultPrinter::~XmlUnitTestResultPrinter() {
+  if (output_file_ && !open_failed_) {
+    fprintf(output_file_, "</testsuites>\n");
+    fflush(output_file_);
+    CloseFile(output_file_);
+  }
+}
+
+bool XmlUnitTestResultPrinter::Initialize(const FilePath& output_file_path) {
+  DCHECK(!output_file_);
+  output_file_ = OpenFile(output_file_path, "w");
+  if (!output_file_) {
+    // If the file open fails, we set the output location to stderr. This is
+    // because in current usage our caller CHECKs the result of this function.
+    // But that in turn causes a LogMessage that comes back to this object,
+    // which in turn causes a (double) crash. By pointing at stderr, there might
+    // be some indication what's going wrong. See https://crbug.com/736783.
+    output_file_ = stderr;
+    open_failed_ = true;
+    return false;
+  }
+
+  fprintf(output_file_,
+          "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<testsuites>\n");
+  fflush(output_file_);
+
+  return true;
+}
+
+void XmlUnitTestResultPrinter::OnAssert(const char* file,
+                                        int line,
+                                        const std::string& summary,
+                                        const std::string& message) {
+  WriteTestPartResult(file, line, testing::TestPartResult::kFatalFailure,
+                      summary, message);
+}
+
+void XmlUnitTestResultPrinter::OnTestCaseStart(
+    const testing::TestCase& test_case) {
+  fprintf(output_file_, "  <testsuite>\n");
+  fflush(output_file_);
+}
+
+void XmlUnitTestResultPrinter::OnTestStart(
+    const testing::TestInfo& test_info) {
+  // This is our custom extension - it helps to recognize which test was
+  // running when the test binary crashed. Note that we cannot even open the
+  // <testcase> tag here - it requires e.g. run time of the test to be known.
+  fprintf(output_file_,
+          "    <x-teststart name=\"%s\" classname=\"%s\" />\n",
+          test_info.name(),
+          test_info.test_case_name());
+  fflush(output_file_);
+}
+
+void XmlUnitTestResultPrinter::OnTestEnd(const testing::TestInfo& test_info) {
+  fprintf(output_file_,
+          "    <testcase name=\"%s\" status=\"run\" time=\"%.3f\""
+          " classname=\"%s\">\n",
+          test_info.name(),
+          static_cast<double>(test_info.result()->elapsed_time()) /
+              Time::kMillisecondsPerSecond,
+          test_info.test_case_name());
+  if (test_info.result()->Failed()) {
+    fprintf(output_file_,
+            "      <failure message=\"\" type=\"\"></failure>\n");
+  }
+
+  int limit = test_info.result()->total_part_count();
+  if (CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kTestLauncherTestPartResultsLimit)) {
+    std::string limit_str =
+        CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+            switches::kTestLauncherTestPartResultsLimit);
+    int test_part_results_limit = std::strtol(limit_str.c_str(), nullptr, 10);
+    if (test_part_results_limit >= 0)
+      limit = std::min(limit, test_part_results_limit);
+  } else {
+    limit = std::min(limit, kDefaultTestPartResultsLimit);
+  }
+
+  for (int i = 0; i < limit; ++i) {
+    const auto& test_part_result = test_info.result()->GetTestPartResult(i);
+    WriteTestPartResult(test_part_result.file_name(),
+                        test_part_result.line_number(), test_part_result.type(),
+                        test_part_result.summary(), test_part_result.message());
+  }
+
+  if (test_info.result()->total_part_count() > limit) {
+    WriteTestPartResult(
+        "<unknown>", 0, testing::TestPartResult::kNonFatalFailure,
+        kTestPartLesultsLimitExceeded, kTestPartLesultsLimitExceeded);
+  }
+
+  fprintf(output_file_, "    </testcase>\n");
+  fflush(output_file_);
+}
+
+void XmlUnitTestResultPrinter::OnTestCaseEnd(
+    const testing::TestCase& test_case) {
+  fprintf(output_file_, "  </testsuite>\n");
+  fflush(output_file_);
+}
+
+void XmlUnitTestResultPrinter::WriteTestPartResult(
+    const char* file,
+    int line,
+    testing::TestPartResult::Type result_type,
+    const std::string& summary,
+    const std::string& message) {
+  const char* type = "unknown";
+  switch (result_type) {
+    case testing::TestPartResult::kSuccess:
+      type = "success";
+      break;
+    case testing::TestPartResult::kNonFatalFailure:
+      type = "failure";
+      break;
+    case testing::TestPartResult::kFatalFailure:
+      type = "fatal_failure";
+      break;
+  }
+  std::string summary_encoded;
+  Base64Encode(summary, &summary_encoded);
+  std::string message_encoded;
+  Base64Encode(message, &message_encoded);
+  fprintf(output_file_,
+          "      <x-test-result-part type=\"%s\" file=\"%s\" line=\"%d\">\n"
+          "        <summary>%s</summary>\n"
+          "        <message>%s</message>\n"
+          "      </x-test-result-part>\n",
+          type, file, line, summary_encoded.c_str(), message_encoded.c_str());
+  fflush(output_file_);
+}
+
+}  // namespace base
diff --git a/base/test/gtest_xml_unittest_result_printer.h b/base/test/gtest_xml_unittest_result_printer.h
new file mode 100644
index 0000000..9340382
--- /dev/null
+++ b/base/test/gtest_xml_unittest_result_printer.h
@@ -0,0 +1,55 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_GTEST_XML_UNITTEST_RESULT_PRINTER_H_
+#define BASE_TEST_GTEST_XML_UNITTEST_RESULT_PRINTER_H_
+
+#include <stdio.h>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+class FilePath;
+
+// Generates an XML output file. Format is very close to GTest, but has
+// extensions needed by the test launcher.
+class XmlUnitTestResultPrinter : public testing::EmptyTestEventListener {
+ public:
+  XmlUnitTestResultPrinter();
+  ~XmlUnitTestResultPrinter() override;
+
+  // Must be called before adding as a listener. Returns true on success.
+  bool Initialize(const FilePath& output_file_path) WARN_UNUSED_RESULT;
+
+  // CHECK/DCHECK failed. Print file/line and message to the xml.
+  void OnAssert(const char* file,
+                int line,
+                const std::string& summary,
+                const std::string& message);
+
+ private:
+  // testing::EmptyTestEventListener:
+  void OnTestCaseStart(const testing::TestCase& test_case) override;
+  void OnTestStart(const testing::TestInfo& test_info) override;
+  void OnTestEnd(const testing::TestInfo& test_info) override;
+  void OnTestCaseEnd(const testing::TestCase& test_case) override;
+
+  void WriteTestPartResult(const char* file,
+                           int line,
+                           testing::TestPartResult::Type type,
+                           const std::string& summary,
+                           const std::string& message);
+
+  FILE* output_file_;
+  bool open_failed_;
+
+  DISALLOW_COPY_AND_ASSIGN(XmlUnitTestResultPrinter);
+};
+
+}  // namespace base
+
+#endif  // BASE_TEST_GTEST_XML_UNITTEST_RESULT_PRINTER_H_
diff --git a/base/test/gtest_xml_util.cc b/base/test/gtest_xml_util.cc
new file mode 100644
index 0000000..37104e8
--- /dev/null
+++ b/base/test/gtest_xml_util.cc
@@ -0,0 +1,234 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/gtest_xml_util.h"
+
+#include <stdint.h>
+
+#include "base/base64.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
+#include "base/test/gtest_util.h"
+#include "base/test/launcher/test_launcher.h"
+#include "third_party/libxml/chromium/libxml_utils.h"
+
+namespace base {
+
+namespace {
+
+// This is used for the xml parser to report errors. This assumes the context
+// is a pointer to a std::string where the error message should be appended.
+static void XmlErrorFunc(void *context, const char *message, ...) {
+  va_list args;
+  va_start(args, message);
+  std::string* error = static_cast<std::string*>(context);
+  StringAppendV(error, message, args);
+  va_end(args);
+}
+
+}  // namespace
+
+bool ProcessGTestOutput(const base::FilePath& output_file,
+                        std::vector<TestResult>* results,
+                        bool* crashed) {
+  DCHECK(results);
+
+  std::string xml_contents;
+  if (!ReadFileToString(output_file, &xml_contents))
+    return false;
+
+  // Silence XML errors - otherwise they go to stderr.
+  std::string xml_errors;
+  ScopedXmlErrorFunc error_func(&xml_errors, &XmlErrorFunc);
+
+  XmlReader xml_reader;
+  if (!xml_reader.Load(xml_contents))
+    return false;
+
+  enum {
+    STATE_INIT,
+    STATE_TESTSUITE,
+    STATE_TESTCASE,
+    STATE_TEST_RESULT,
+    STATE_FAILURE,
+    STATE_END,
+  } state = STATE_INIT;
+
+  while (xml_reader.Read()) {
+    xml_reader.SkipToElement();
+    std::string node_name(xml_reader.NodeName());
+
+    switch (state) {
+      case STATE_INIT:
+        if (node_name == "testsuites" && !xml_reader.IsClosingElement())
+          state = STATE_TESTSUITE;
+        else
+          return false;
+        break;
+      case STATE_TESTSUITE:
+        if (node_name == "testsuites" && xml_reader.IsClosingElement())
+          state = STATE_END;
+        else if (node_name == "testsuite" && !xml_reader.IsClosingElement())
+          state = STATE_TESTCASE;
+        else
+          return false;
+        break;
+      case STATE_TESTCASE:
+        if (node_name == "testsuite" && xml_reader.IsClosingElement()) {
+          state = STATE_TESTSUITE;
+        } else if (node_name == "x-teststart" &&
+                   !xml_reader.IsClosingElement()) {
+          // This is our custom extension that helps recognize which test was
+          // running when the test binary crashed.
+          TestResult result;
+
+          std::string test_case_name;
+          if (!xml_reader.NodeAttribute("classname", &test_case_name))
+            return false;
+          std::string test_name;
+          if (!xml_reader.NodeAttribute("name", &test_name))
+            return false;
+          result.full_name = FormatFullTestName(test_case_name, test_name);
+
+          result.elapsed_time = TimeDelta();
+
+          // Assume the test crashed - we can correct that later.
+          result.status = TestResult::TEST_CRASH;
+
+          results->push_back(result);
+        } else if (node_name == "testcase" && !xml_reader.IsClosingElement()) {
+          std::string test_status;
+          if (!xml_reader.NodeAttribute("status", &test_status))
+            return false;
+
+          if (test_status != "run" && test_status != "notrun")
+            return false;
+          if (test_status != "run")
+            break;
+
+          TestResult result;
+
+          std::string test_case_name;
+          if (!xml_reader.NodeAttribute("classname", &test_case_name))
+            return false;
+          std::string test_name;
+          if (!xml_reader.NodeAttribute("name", &test_name))
+            return false;
+          result.full_name = test_case_name + "." + test_name;
+
+          std::string test_time_str;
+          if (!xml_reader.NodeAttribute("time", &test_time_str))
+            return false;
+          result.elapsed_time = TimeDelta::FromMicroseconds(
+              static_cast<int64_t>(strtod(test_time_str.c_str(), nullptr) *
+                                   Time::kMicrosecondsPerSecond));
+
+          result.status = TestResult::TEST_SUCCESS;
+
+          if (!results->empty() &&
+              results->back().full_name == result.full_name &&
+              results->back().status == TestResult::TEST_CRASH) {
+            // Erase the fail-safe "crashed" result - now we know the test did
+            // not crash.
+            results->pop_back();
+          }
+
+          results->push_back(result);
+        } else if (node_name == "failure" && !xml_reader.IsClosingElement()) {
+          std::string failure_message;
+          if (!xml_reader.NodeAttribute("message", &failure_message))
+            return false;
+
+          DCHECK(!results->empty());
+          results->back().status = TestResult::TEST_FAILURE;
+
+          state = STATE_FAILURE;
+        } else if (node_name == "testcase" && xml_reader.IsClosingElement()) {
+          // Deliberately empty.
+        } else if (node_name == "x-test-result-part" &&
+                   !xml_reader.IsClosingElement()) {
+          std::string result_type;
+          if (!xml_reader.NodeAttribute("type", &result_type))
+            return false;
+
+          std::string file_name;
+          if (!xml_reader.NodeAttribute("file", &file_name))
+            return false;
+
+          std::string line_number_str;
+          if (!xml_reader.NodeAttribute("line", &line_number_str))
+            return false;
+
+          int line_number;
+          if (!StringToInt(line_number_str, &line_number))
+            return false;
+
+          TestResultPart::Type type;
+          if (!TestResultPart::TypeFromString(result_type, &type))
+            return false;
+
+          TestResultPart test_result_part;
+          test_result_part.type = type;
+          test_result_part.file_name = file_name,
+          test_result_part.line_number = line_number;
+          DCHECK(!results->empty());
+          results->back().test_result_parts.push_back(test_result_part);
+
+          state = STATE_TEST_RESULT;
+        } else {
+          return false;
+        }
+        break;
+      case STATE_TEST_RESULT:
+        if (node_name == "summary" && !xml_reader.IsClosingElement()) {
+          std::string summary;
+          if (!xml_reader.ReadElementContent(&summary))
+            return false;
+
+          if (!Base64Decode(summary, &summary))
+            return false;
+
+          DCHECK(!results->empty());
+          DCHECK(!results->back().test_result_parts.empty());
+          results->back().test_result_parts.back().summary = summary;
+        } else if (node_name == "summary" && xml_reader.IsClosingElement()) {
+        } else if (node_name == "message" && !xml_reader.IsClosingElement()) {
+          std::string message;
+          if (!xml_reader.ReadElementContent(&message))
+            return false;
+
+          if (!Base64Decode(message, &message))
+            return false;
+
+          DCHECK(!results->empty());
+          DCHECK(!results->back().test_result_parts.empty());
+          results->back().test_result_parts.back().message = message;
+        } else if (node_name == "message" && xml_reader.IsClosingElement()) {
+        } else if (node_name == "x-test-result-part" &&
+                   xml_reader.IsClosingElement()) {
+          state = STATE_TESTCASE;
+        } else {
+          return false;
+        }
+        break;
+      case STATE_FAILURE:
+        if (node_name == "failure" && xml_reader.IsClosingElement())
+          state = STATE_TESTCASE;
+        else
+          return false;
+        break;
+      case STATE_END:
+        // If we are here and there are still XML elements, the file has wrong
+        // format.
+        return false;
+    }
+  }
+
+  *crashed = (state != STATE_END);
+  return true;
+}
+
+}  // namespace base
diff --git a/base/test/gtest_xml_util.h b/base/test/gtest_xml_util.h
new file mode 100644
index 0000000..b023f80
--- /dev/null
+++ b/base/test/gtest_xml_util.h
@@ -0,0 +1,27 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_GTEST_XML_UTIL_H_
+#define BASE_TEST_GTEST_XML_UTIL_H_
+
+#include <vector>
+
+#include "base/compiler_specific.h"
+
+namespace base {
+
+class FilePath;
+struct TestResult;
+
+// Produces a vector of test results based on GTest output file.
+// Returns true iff the output file exists and has been successfully parsed.
+// On successful return |crashed| is set to true if the test results
+// are valid but incomplete.
+bool ProcessGTestOutput(const base::FilePath& output_file,
+                        std::vector<TestResult>* results,
+                        bool* crashed) WARN_UNUSED_RESULT;
+
+}  // namespace base
+
+#endif  // BASE_TEST_GTEST_XML_UTIL_H_
diff --git a/base/test/icu_test_util.cc b/base/test/icu_test_util.cc
new file mode 100644
index 0000000..a6f3e55
--- /dev/null
+++ b/base/test/icu_test_util.cc
@@ -0,0 +1,39 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/icu_test_util.h"
+
+#include "base/base_switches.h"
+#include "base/command_line.h"
+#include "base/i18n/icu_util.h"
+#include "base/i18n/rtl.h"
+#include "third_party/icu/source/common/unicode/uloc.h"
+
+namespace base {
+namespace test {
+
+ScopedRestoreICUDefaultLocale::ScopedRestoreICUDefaultLocale()
+    : ScopedRestoreICUDefaultLocale(std::string()) {}
+
+ScopedRestoreICUDefaultLocale::ScopedRestoreICUDefaultLocale(
+    const std::string& locale)
+    : default_locale_(uloc_getDefault()) {
+  if (!locale.empty())
+    i18n::SetICUDefaultLocale(locale.data());
+}
+
+ScopedRestoreICUDefaultLocale::~ScopedRestoreICUDefaultLocale() {
+  i18n::SetICUDefaultLocale(default_locale_.data());
+}
+
+void InitializeICUForTesting() {
+  if (!CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kTestDoNotInitializeIcu)) {
+    i18n::AllowMultipleInitializeCallsForTesting();
+    i18n::InitializeICU();
+  }
+}
+
+}  // namespace test
+}  // namespace base
diff --git a/base/test/icu_test_util.h b/base/test/icu_test_util.h
new file mode 100644
index 0000000..1a6e47d
--- /dev/null
+++ b/base/test/icu_test_util.h
@@ -0,0 +1,35 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_ICU_TEST_UTIL_H_
+#define BASE_TEST_ICU_TEST_UTIL_H_
+
+#include <string>
+
+#include "base/macros.h"
+
+namespace base {
+namespace test {
+
+// In unit tests, prefer ScopedRestoreICUDefaultLocale over
+// calling base::i18n::SetICUDefaultLocale() directly. This scoper makes it
+// harder to accidentally forget to reset the locale.
+class ScopedRestoreICUDefaultLocale {
+ public:
+  ScopedRestoreICUDefaultLocale();
+  explicit ScopedRestoreICUDefaultLocale(const std::string& locale);
+  ~ScopedRestoreICUDefaultLocale();
+
+ private:
+  const std::string default_locale_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedRestoreICUDefaultLocale);
+};
+
+void InitializeICUForTesting();
+
+}  // namespace test
+}  // namespace base
+
+#endif  // BASE_TEST_ICU_TEST_UTIL_H_
diff --git a/base/test/launcher/test_launcher.cc b/base/test/launcher/test_launcher.cc
new file mode 100644
index 0000000..ddd9a4d
--- /dev/null
+++ b/base/test/launcher/test_launcher.cc
@@ -0,0 +1,1345 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/launcher/test_launcher.h"
+
+#include <stdio.h>
+
+#include <algorithm>
+#include <map>
+#include <random>
+#include <utility>
+
+#include "base/at_exit.h"
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/environment.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_file.h"
+#include "base/format_macros.h"
+#include "base/hash.h"
+#include "base/lazy_instance.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/process/kill.h"
+#include "base/process/launch.h"
+#include "base/run_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "base/strings/pattern.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringize_macros.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/sys_info.h"
+#include "base/task_scheduler/post_task.h"
+#include "base/task_scheduler/task_scheduler.h"
+#include "base/test/gtest_util.h"
+#include "base/test/launcher/test_launcher_tracer.h"
+#include "base/test/launcher/test_results_tracker.h"
+#include "base/test/test_switches.h"
+#include "base/test/test_timeouts.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/time/time.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if defined(OS_POSIX)
+#include <fcntl.h>
+
+#include "base/files/file_descriptor_watcher_posix.h"
+#endif
+
+#if defined(OS_MACOSX)
+#include "base/mac/scoped_nsautorelease_pool.h"
+#endif
+
+#if defined(OS_WIN)
+#include "base/win/windows_version.h"
+#endif
+
+#if defined(OS_FUCHSIA)
+#include <lib/zx/job.h>
+#include "base/fuchsia/default_job.h"
+#include "base/fuchsia/fuchsia_logging.h"
+#endif
+
+namespace base {
+
+// See https://groups.google.com/a/chromium.org/d/msg/chromium-dev/nkdTP7sstSc/uT3FaE_sgkAJ .
+using ::operator<<;
+
+// The environment variable name for the total number of test shards.
+const char kTestTotalShards[] = "GTEST_TOTAL_SHARDS";
+// The environment variable name for the test shard index.
+const char kTestShardIndex[] = "GTEST_SHARD_INDEX";
+
+namespace {
+
+// Global tag for test runs where the results are incomplete or unreliable
+// for any reason, e.g. early exit because of too many broken tests.
+const char kUnreliableResultsTag[] = "UNRELIABLE_RESULTS";
+
+// Maximum time of no output after which we print list of processes still
+// running. This deliberately doesn't use TestTimeouts (which is otherwise
+// a recommended solution), because they can be increased. This would defeat
+// the purpose of this timeout, which is 1) to avoid buildbot "no output for
+// X seconds" timeout killing the process 2) help communicate status of
+// the test launcher to people looking at the output (no output for a long
+// time is mysterious and gives no info about what is happening) 3) help
+// debugging in case the process hangs anyway.
+constexpr TimeDelta kOutputTimeout = TimeDelta::FromSeconds(15);
+
+// Limit of output snippet lines when printing to stdout.
+// Avoids flooding the logs with amount of output that gums up
+// the infrastructure.
+const size_t kOutputSnippetLinesLimit = 5000;
+
+// Limit of output snippet size. Exceeding this limit
+// results in truncating the output and failing the test.
+const size_t kOutputSnippetBytesLimit = 300 * 1024;
+
+// Limit of seed values for gtest shuffling. Arbitrary, but based on
+// gtest's similarly arbitrary choice.
+const uint32_t kRandomSeedUpperBound = 100000;
+
+// Set of live launch test processes with corresponding lock (it is allowed
+// for callers to launch processes on different threads).
+Lock* GetLiveProcessesLock() {
+  static auto* lock = new Lock;
+  return lock;
+}
+
+std::map<ProcessHandle, CommandLine>* GetLiveProcesses() {
+  static auto* map = new std::map<ProcessHandle, CommandLine>;
+  return map;
+}
+
+// Performance trace generator.
+TestLauncherTracer* GetTestLauncherTracer() {
+  static auto* tracer = new TestLauncherTracer;
+  return tracer;
+}
+
+// Creates and starts a TaskScheduler with |num_parallel_jobs| dedicated to
+// foreground blocking tasks (corresponds to the traits used to launch and wait
+// for child processes).
+void CreateAndStartTaskScheduler(int num_parallel_jobs) {
+  // These values are taken from TaskScheduler::StartWithDefaultParams(), which
+  // is not used directly to allow a custom number of threads in the foreground
+  // blocking pool.
+  constexpr int kMaxBackgroundThreads = 1;
+  constexpr int kMaxBackgroundBlockingThreads = 2;
+  const int max_foreground_threads =
+      std::max(1, base::SysInfo::NumberOfProcessors());
+  constexpr base::TimeDelta kSuggestedReclaimTime =
+      base::TimeDelta::FromSeconds(30);
+  base::TaskScheduler::Create("TestLauncher");
+  base::TaskScheduler::GetInstance()->Start(
+      {{kMaxBackgroundThreads, kSuggestedReclaimTime},
+       {kMaxBackgroundBlockingThreads, kSuggestedReclaimTime},
+       {max_foreground_threads, kSuggestedReclaimTime},
+       {num_parallel_jobs, kSuggestedReclaimTime}});
+}
+
+#if defined(OS_POSIX)
+// Self-pipe that makes it possible to do complex shutdown handling
+// outside of the signal handler.
+int g_shutdown_pipe[2] = { -1, -1 };
+
+void ShutdownPipeSignalHandler(int signal) {
+  HANDLE_EINTR(write(g_shutdown_pipe[1], "q", 1));
+}
+
+void KillSpawnedTestProcesses() {
+  // Keep the lock until exiting the process to prevent further processes
+  // from being spawned.
+  AutoLock lock(*GetLiveProcessesLock());
+
+  fprintf(stdout, "Sending SIGTERM to %" PRIuS " child processes... ",
+          GetLiveProcesses()->size());
+  fflush(stdout);
+
+  for (const auto& pair : *GetLiveProcesses()) {
+    // Send the signal to entire process group.
+    kill((-1) * (pair.first), SIGTERM);
+  }
+
+  fprintf(stdout,
+          "done.\nGiving processes a chance to terminate cleanly... ");
+  fflush(stdout);
+
+  PlatformThread::Sleep(TimeDelta::FromMilliseconds(500));
+
+  fprintf(stdout, "done.\n");
+  fflush(stdout);
+
+  fprintf(stdout, "Sending SIGKILL to %" PRIuS " child processes... ",
+          GetLiveProcesses()->size());
+  fflush(stdout);
+
+  for (const auto& pair : *GetLiveProcesses()) {
+    // Send the signal to entire process group.
+    kill((-1) * (pair.first), SIGKILL);
+  }
+
+  fprintf(stdout, "done.\n");
+  fflush(stdout);
+}
+#endif  // defined(OS_POSIX)
+
+// Parses the environment variable var as an Int32.  If it is unset, returns
+// true.  If it is set, unsets it then converts it to Int32 before
+// returning it in |result|.  Returns true on success.
+bool TakeInt32FromEnvironment(const char* const var, int32_t* result) {
+  std::unique_ptr<Environment> env(Environment::Create());
+  std::string str_val;
+
+  if (!env->GetVar(var, &str_val))
+    return true;
+
+  if (!env->UnSetVar(var)) {
+    LOG(ERROR) << "Invalid environment: we could not unset " << var << ".\n";
+    return false;
+  }
+
+  if (!StringToInt(str_val, result)) {
+    LOG(ERROR) << "Invalid environment: " << var << " is not an integer.\n";
+    return false;
+  }
+
+  return true;
+}
+
+// Unsets the environment variable |name| and returns true on success.
+// Also returns true if the variable just doesn't exist.
+bool UnsetEnvironmentVariableIfExists(const std::string& name) {
+  std::unique_ptr<Environment> env(Environment::Create());
+  std::string str_val;
+  if (!env->GetVar(name, &str_val))
+    return true;
+  return env->UnSetVar(name);
+}
+
+// Returns true if bot mode has been requested, i.e. defaults optimized
+// for continuous integration bots. This way developers don't have to remember
+// special command-line flags.
+bool BotModeEnabled() {
+  std::unique_ptr<Environment> env(Environment::Create());
+  return CommandLine::ForCurrentProcess()->HasSwitch(
+      switches::kTestLauncherBotMode) ||
+      env->HasVar("CHROMIUM_TEST_LAUNCHER_BOT_MODE");
+}
+
+// Returns command line command line after gtest-specific processing
+// and applying |wrapper|.
+CommandLine PrepareCommandLineForGTest(const CommandLine& command_line,
+                                       const std::string& wrapper) {
+  CommandLine new_command_line(command_line.GetProgram());
+  CommandLine::SwitchMap switches = command_line.GetSwitches();
+
+  // Handled by the launcher process.
+  switches.erase(kGTestRepeatFlag);
+  switches.erase(kGTestShuffleFlag);
+  switches.erase(kGTestRandomSeedFlag);
+
+  // Don't try to write the final XML report in child processes.
+  switches.erase(kGTestOutputFlag);
+
+  for (CommandLine::SwitchMap::const_iterator iter = switches.begin();
+       iter != switches.end(); ++iter) {
+    new_command_line.AppendSwitchNative((*iter).first, (*iter).second);
+  }
+
+  // Prepend wrapper after last CommandLine quasi-copy operation. CommandLine
+  // does not really support removing switches well, and trying to do that
+  // on a CommandLine with a wrapper is known to break.
+  // TODO(phajdan.jr): Give it a try to support CommandLine removing switches.
+#if defined(OS_WIN)
+  new_command_line.PrependWrapper(ASCIIToUTF16(wrapper));
+#else
+  new_command_line.PrependWrapper(wrapper);
+#endif
+
+  return new_command_line;
+}
+
+// Launches a child process using |command_line|. If the child process is still
+// running after |timeout|, it is terminated and |*was_timeout| is set to true.
+// Returns exit code of the process.
+int LaunchChildTestProcessWithOptions(const CommandLine& command_line,
+                                      const LaunchOptions& options,
+                                      int flags,
+                                      TimeDelta timeout,
+                                      ProcessLifetimeObserver* observer,
+                                      bool* was_timeout) {
+  TimeTicks start_time(TimeTicks::Now());
+
+#if defined(OS_POSIX)
+  // Make sure an option we rely on is present - see LaunchChildGTestProcess.
+  DCHECK(options.new_process_group);
+#endif
+
+  LaunchOptions new_options(options);
+
+#if defined(OS_WIN)
+  DCHECK(!new_options.job_handle);
+
+  win::ScopedHandle job_handle;
+  if (flags & TestLauncher::USE_JOB_OBJECTS) {
+    job_handle.Set(CreateJobObject(NULL, NULL));
+    if (!job_handle.IsValid()) {
+      LOG(ERROR) << "Could not create JobObject.";
+      return -1;
+    }
+
+    DWORD job_flags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
+
+    // Allow break-away from job since sandbox and few other places rely on it
+    // on Windows versions prior to Windows 8 (which supports nested jobs).
+    if (win::GetVersion() < win::VERSION_WIN8 &&
+        flags & TestLauncher::ALLOW_BREAKAWAY_FROM_JOB) {
+      job_flags |= JOB_OBJECT_LIMIT_BREAKAWAY_OK;
+    }
+
+    if (!SetJobObjectLimitFlags(job_handle.Get(), job_flags)) {
+      LOG(ERROR) << "Could not SetJobObjectLimitFlags.";
+      return -1;
+    }
+
+    new_options.job_handle = job_handle.Get();
+  }
+#elif defined(OS_FUCHSIA)
+  DCHECK(!new_options.job_handle);
+
+  zx::job job_handle;
+  zx_status_t result = zx::job::create(*GetDefaultJob(), 0, &job_handle);
+  ZX_CHECK(ZX_OK == result, result) << "zx_job_create";
+  new_options.job_handle = job_handle.get();
+#endif  // defined(OS_FUCHSIA)
+
+#if defined(OS_LINUX)
+  // To prevent accidental privilege sharing to an untrusted child, processes
+  // are started with PR_SET_NO_NEW_PRIVS. Do not set that here, since this
+  // new child will be privileged and trusted.
+  new_options.allow_new_privs = true;
+#endif
+
+  Process process;
+
+  {
+    // Note how we grab the lock before the process possibly gets created.
+    // This ensures that when the lock is held, ALL the processes are registered
+    // in the set.
+    AutoLock lock(*GetLiveProcessesLock());
+
+#if defined(OS_WIN)
+    // Allow the handle used to capture stdio and stdout to be inherited by the
+    // child. Note that this is done under GetLiveProcessesLock() to ensure that
+    // only the desired child receives the handle.
+    if (new_options.stdout_handle) {
+      ::SetHandleInformation(new_options.stdout_handle, HANDLE_FLAG_INHERIT,
+                             HANDLE_FLAG_INHERIT);
+    }
+#endif
+
+    process = LaunchProcess(command_line, new_options);
+
+#if defined(OS_WIN)
+    // Revoke inheritance so that the handle isn't leaked into other children.
+    // Note that this is done under GetLiveProcessesLock() to ensure that only
+    // the desired child receives the handle.
+    if (new_options.stdout_handle)
+      ::SetHandleInformation(new_options.stdout_handle, HANDLE_FLAG_INHERIT, 0);
+#endif
+
+    if (!process.IsValid())
+      return -1;
+
+    // TODO(rvargas) crbug.com/417532: Don't store process handles.
+    GetLiveProcesses()->insert(std::make_pair(process.Handle(), command_line));
+  }
+
+  if (observer)
+    observer->OnLaunched(process.Handle(), process.Pid());
+
+  int exit_code = 0;
+  bool did_exit = false;
+
+  {
+    base::ScopedAllowBaseSyncPrimitivesForTesting allow_base_sync_primitives;
+    did_exit = process.WaitForExitWithTimeout(timeout, &exit_code);
+  }
+
+  if (!did_exit) {
+    if (observer)
+      observer->OnTimedOut(command_line);
+
+    *was_timeout = true;
+    exit_code = -1;  // Set a non-zero exit code to signal a failure.
+
+    {
+      base::ScopedAllowBaseSyncPrimitivesForTesting allow_base_sync_primitives;
+      // Ensure that the process terminates.
+      process.Terminate(-1, true);
+    }
+  }
+
+  {
+    // Note how we grab the log before issuing a possibly broad process kill.
+    // Other code parts that grab the log kill processes, so avoid trying
+    // to do that twice and trigger all kinds of log messages.
+    AutoLock lock(*GetLiveProcessesLock());
+
+#if defined(OS_FUCHSIA)
+    zx_status_t status = job_handle.kill();
+    ZX_CHECK(status == ZX_OK, status);
+#elif defined(OS_POSIX)
+    if (exit_code != 0) {
+      // On POSIX, in case the test does not exit cleanly, either due to a crash
+      // or due to it timing out, we need to clean up any child processes that
+      // it might have created. On Windows, child processes are automatically
+      // cleaned up using JobObjects.
+      KillProcessGroup(process.Handle());
+    }
+#endif
+
+    GetLiveProcesses()->erase(process.Handle());
+  }
+
+  GetTestLauncherTracer()->RecordProcessExecution(
+      start_time, TimeTicks::Now() - start_time);
+
+  return exit_code;
+}
+
+void DoLaunchChildTestProcess(
+    const CommandLine& command_line,
+    TimeDelta timeout,
+    const TestLauncher::LaunchOptions& test_launch_options,
+    bool redirect_stdio,
+    SingleThreadTaskRunner* task_runner,
+    std::unique_ptr<ProcessLifetimeObserver> observer) {
+  TimeTicks start_time = TimeTicks::Now();
+
+  ScopedFILE output_file;
+  FilePath output_filename;
+  if (redirect_stdio) {
+    FILE* raw_output_file = CreateAndOpenTemporaryFile(&output_filename);
+    output_file.reset(raw_output_file);
+    CHECK(output_file);
+  }
+
+  LaunchOptions options;
+#if defined(OS_WIN)
+
+  options.inherit_mode = test_launch_options.inherit_mode;
+  options.handles_to_inherit = test_launch_options.handles_to_inherit;
+  if (redirect_stdio) {
+    HANDLE handle =
+        reinterpret_cast<HANDLE>(_get_osfhandle(_fileno(output_file.get())));
+    CHECK_NE(INVALID_HANDLE_VALUE, handle);
+    options.stdin_handle = INVALID_HANDLE_VALUE;
+    options.stdout_handle = handle;
+    options.stderr_handle = handle;
+    // See LaunchOptions.stdout_handle comments for why this compares against
+    // FILE_TYPE_CHAR.
+    if (options.inherit_mode == base::LaunchOptions::Inherit::kSpecific &&
+        GetFileType(handle) != FILE_TYPE_CHAR) {
+      options.handles_to_inherit.push_back(handle);
+    }
+  }
+
+#else  // if !defined(OS_WIN)
+
+  options.fds_to_remap = test_launch_options.fds_to_remap;
+  if (redirect_stdio) {
+    int output_file_fd = fileno(output_file.get());
+    CHECK_LE(0, output_file_fd);
+    options.fds_to_remap.push_back(
+        std::make_pair(output_file_fd, STDOUT_FILENO));
+    options.fds_to_remap.push_back(
+        std::make_pair(output_file_fd, STDERR_FILENO));
+  }
+
+#if !defined(OS_FUCHSIA)
+  options.new_process_group = true;
+#endif
+#if defined(OS_LINUX)
+  options.kill_on_parent_death = true;
+#endif
+
+#endif  // !defined(OS_WIN)
+
+  bool was_timeout = false;
+  int exit_code = LaunchChildTestProcessWithOptions(
+      command_line, options, test_launch_options.flags, timeout, observer.get(),
+      &was_timeout);
+
+  std::string output_file_contents;
+  if (redirect_stdio) {
+    fflush(output_file.get());
+    output_file.reset();
+    // Reading the file can sometimes fail when the process was killed midflight
+    // (e.g. on test suite timeout): https://crbug.com/826408. Attempt to read
+    // the output file anyways, but do not crash on failure in this case.
+    CHECK(ReadFileToString(output_filename, &output_file_contents) ||
+          exit_code != 0);
+
+    if (!DeleteFile(output_filename, false)) {
+      // This needs to be non-fatal at least for Windows.
+      LOG(WARNING) << "Failed to delete " << output_filename.AsUTF8Unsafe();
+    }
+  }
+
+  // Invoke OnCompleted on the thread it was originating from, not on a worker
+  // pool thread.
+  task_runner->PostTask(
+      FROM_HERE,
+      BindOnce(&ProcessLifetimeObserver::OnCompleted, std::move(observer),
+               exit_code, TimeTicks::Now() - start_time, was_timeout,
+               output_file_contents));
+}
+
+}  // namespace
+
+const char kGTestBreakOnFailure[] = "gtest_break_on_failure";
+const char kGTestFilterFlag[] = "gtest_filter";
+const char kGTestFlagfileFlag[] = "gtest_flagfile";
+const char kGTestHelpFlag[]   = "gtest_help";
+const char kGTestListTestsFlag[] = "gtest_list_tests";
+const char kGTestRepeatFlag[] = "gtest_repeat";
+const char kGTestRunDisabledTestsFlag[] = "gtest_also_run_disabled_tests";
+const char kGTestOutputFlag[] = "gtest_output";
+const char kGTestShuffleFlag[] = "gtest_shuffle";
+const char kGTestRandomSeedFlag[] = "gtest_random_seed";
+
+TestLauncherDelegate::~TestLauncherDelegate() = default;
+
+TestLauncher::LaunchOptions::LaunchOptions() = default;
+TestLauncher::LaunchOptions::LaunchOptions(const LaunchOptions& other) =
+    default;
+TestLauncher::LaunchOptions::~LaunchOptions() = default;
+
+TestLauncher::TestLauncher(TestLauncherDelegate* launcher_delegate,
+                           size_t parallel_jobs)
+    : launcher_delegate_(launcher_delegate),
+      total_shards_(1),
+      shard_index_(0),
+      cycles_(1),
+      test_found_count_(0),
+      test_started_count_(0),
+      test_finished_count_(0),
+      test_success_count_(0),
+      test_broken_count_(0),
+      retry_count_(0),
+      retry_limit_(0),
+      force_run_broken_tests_(false),
+      run_result_(true),
+      shuffle_(false),
+      shuffle_seed_(0),
+      watchdog_timer_(FROM_HERE,
+                      kOutputTimeout,
+                      this,
+                      &TestLauncher::OnOutputTimeout),
+      parallel_jobs_(parallel_jobs) {}
+
+TestLauncher::~TestLauncher() {
+  if (base::TaskScheduler::GetInstance()) {
+    base::TaskScheduler::GetInstance()->Shutdown();
+  }
+}
+
+bool TestLauncher::Run() {
+  if (!Init())
+    return false;
+
+  // Value of |cycles_| changes after each iteration. Keep track of the
+  // original value.
+  int requested_cycles = cycles_;
+
+#if defined(OS_POSIX)
+  CHECK_EQ(0, pipe(g_shutdown_pipe));
+
+  struct sigaction action;
+  memset(&action, 0, sizeof(action));
+  sigemptyset(&action.sa_mask);
+  action.sa_handler = &ShutdownPipeSignalHandler;
+
+  CHECK_EQ(0, sigaction(SIGINT, &action, nullptr));
+  CHECK_EQ(0, sigaction(SIGQUIT, &action, nullptr));
+  CHECK_EQ(0, sigaction(SIGTERM, &action, nullptr));
+
+  auto controller = base::FileDescriptorWatcher::WatchReadable(
+      g_shutdown_pipe[0],
+      base::Bind(&TestLauncher::OnShutdownPipeReadable, Unretained(this)));
+#endif  // defined(OS_POSIX)
+
+  // Start the watchdog timer.
+  watchdog_timer_.Reset();
+
+  ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, BindOnce(&TestLauncher::RunTestIteration, Unretained(this)));
+
+  RunLoop().Run();
+
+  if (requested_cycles != 1)
+    results_tracker_.PrintSummaryOfAllIterations();
+
+  MaybeSaveSummaryAsJSON(std::vector<std::string>());
+
+  return run_result_;
+}
+
+void TestLauncher::LaunchChildGTestProcess(
+    const CommandLine& command_line,
+    const std::string& wrapper,
+    TimeDelta timeout,
+    const LaunchOptions& options,
+    std::unique_ptr<ProcessLifetimeObserver> observer) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  // Record the exact command line used to launch the child.
+  CommandLine new_command_line(
+      PrepareCommandLineForGTest(command_line, wrapper));
+
+  // When running in parallel mode we need to redirect stdio to avoid mixed-up
+  // output. We also always redirect on the bots to get the test output into
+  // JSON summary.
+  bool redirect_stdio = (parallel_jobs_ > 1) || BotModeEnabled();
+
+  PostTaskWithTraits(
+      FROM_HERE, {MayBlock(), TaskShutdownBehavior::BLOCK_SHUTDOWN},
+      BindOnce(&DoLaunchChildTestProcess, new_command_line, timeout, options,
+               redirect_stdio, RetainedRef(ThreadTaskRunnerHandle::Get()),
+               std::move(observer)));
+}
+
+void TestLauncher::OnTestFinished(const TestResult& original_result) {
+  ++test_finished_count_;
+
+  TestResult result(original_result);
+
+  if (result.output_snippet.length() > kOutputSnippetBytesLimit) {
+    if (result.status == TestResult::TEST_SUCCESS)
+      result.status = TestResult::TEST_EXCESSIVE_OUTPUT;
+
+    // Keep the top and bottom of the log and truncate the middle part.
+    result.output_snippet =
+        result.output_snippet.substr(0, kOutputSnippetBytesLimit / 2) + "\n" +
+        StringPrintf("<truncated (%" PRIuS " bytes)>\n",
+                     result.output_snippet.length()) +
+        result.output_snippet.substr(result.output_snippet.length() -
+                                     kOutputSnippetBytesLimit / 2) +
+        "\n";
+  }
+
+  bool print_snippet = false;
+  std::string print_test_stdio("auto");
+  if (CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kTestLauncherPrintTestStdio)) {
+    print_test_stdio = CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+        switches::kTestLauncherPrintTestStdio);
+  }
+  if (print_test_stdio == "auto") {
+    print_snippet = (result.status != TestResult::TEST_SUCCESS);
+  } else if (print_test_stdio == "always") {
+    print_snippet = true;
+  } else if (print_test_stdio == "never") {
+    print_snippet = false;
+  } else {
+    LOG(WARNING) << "Invalid value of " << switches::kTestLauncherPrintTestStdio
+                 << ": " << print_test_stdio;
+  }
+  if (print_snippet) {
+    std::vector<base::StringPiece> snippet_lines =
+        SplitStringPiece(result.output_snippet, "\n", base::KEEP_WHITESPACE,
+                         base::SPLIT_WANT_ALL);
+    if (snippet_lines.size() > kOutputSnippetLinesLimit) {
+      size_t truncated_size = snippet_lines.size() - kOutputSnippetLinesLimit;
+      snippet_lines.erase(
+          snippet_lines.begin(),
+          snippet_lines.begin() + truncated_size);
+      snippet_lines.insert(snippet_lines.begin(), "<truncated>");
+    }
+    fprintf(stdout, "%s", base::JoinString(snippet_lines, "\n").c_str());
+    fflush(stdout);
+  }
+
+  if (result.status == TestResult::TEST_SUCCESS) {
+    ++test_success_count_;
+  } else {
+    tests_to_retry_.insert(result.full_name);
+  }
+
+  results_tracker_.AddTestResult(result);
+
+  // TODO(phajdan.jr): Align counter (padding).
+  std::string status_line(
+      StringPrintf("[%" PRIuS "/%" PRIuS "] %s ",
+                   test_finished_count_,
+                   test_started_count_,
+                   result.full_name.c_str()));
+  if (result.completed()) {
+    status_line.append(StringPrintf("(%" PRId64 " ms)",
+                                    result.elapsed_time.InMilliseconds()));
+  } else if (result.status == TestResult::TEST_TIMEOUT) {
+    status_line.append("(TIMED OUT)");
+  } else if (result.status == TestResult::TEST_CRASH) {
+    status_line.append("(CRASHED)");
+  } else if (result.status == TestResult::TEST_SKIPPED) {
+    status_line.append("(SKIPPED)");
+  } else if (result.status == TestResult::TEST_UNKNOWN) {
+    status_line.append("(UNKNOWN)");
+  } else {
+    // Fail very loudly so it's not ignored.
+    CHECK(false) << "Unhandled test result status: " << result.status;
+  }
+  fprintf(stdout, "%s\n", status_line.c_str());
+  fflush(stdout);
+
+  // We just printed a status line, reset the watchdog timer.
+  watchdog_timer_.Reset();
+
+  // Do not waste time on timeouts. We include tests with unknown results here
+  // because sometimes (e.g. hang in between unit tests) that's how a timeout
+  // gets reported.
+  if (result.status == TestResult::TEST_TIMEOUT ||
+      result.status == TestResult::TEST_UNKNOWN) {
+    test_broken_count_++;
+  }
+  size_t broken_threshold =
+      std::max(static_cast<size_t>(20), test_found_count_ / 10);
+  if (!force_run_broken_tests_ && test_broken_count_ >= broken_threshold) {
+    fprintf(stdout, "Too many badly broken tests (%" PRIuS "), exiting now.\n",
+            test_broken_count_);
+    fflush(stdout);
+
+#if defined(OS_POSIX)
+    KillSpawnedTestProcesses();
+#endif  // defined(OS_POSIX)
+
+    MaybeSaveSummaryAsJSON({"BROKEN_TEST_EARLY_EXIT", kUnreliableResultsTag});
+
+    exit(1);
+  }
+
+  if (test_finished_count_ != test_started_count_)
+    return;
+
+  if (tests_to_retry_.empty() || retry_count_ >= retry_limit_) {
+    OnTestIterationFinished();
+    return;
+  }
+
+  if (!force_run_broken_tests_ && tests_to_retry_.size() >= broken_threshold) {
+    fprintf(stdout,
+            "Too many failing tests (%" PRIuS "), skipping retries.\n",
+            tests_to_retry_.size());
+    fflush(stdout);
+
+    results_tracker_.AddGlobalTag("BROKEN_TEST_SKIPPED_RETRIES");
+    results_tracker_.AddGlobalTag(kUnreliableResultsTag);
+
+    OnTestIterationFinished();
+    return;
+  }
+
+  retry_count_++;
+
+  std::vector<std::string> test_names(tests_to_retry_.begin(),
+                                      tests_to_retry_.end());
+
+  tests_to_retry_.clear();
+
+  size_t retry_started_count = launcher_delegate_->RetryTests(this, test_names);
+  if (retry_started_count == 0) {
+    // Signal failure, but continue to run all requested test iterations.
+    // With the summary of all iterations at the end this is a good default.
+    run_result_ = false;
+
+    OnTestIterationFinished();
+    return;
+  }
+
+  fprintf(stdout, "Retrying %" PRIuS " test%s (retry #%" PRIuS ")\n",
+          retry_started_count,
+          retry_started_count > 1 ? "s" : "",
+          retry_count_);
+  fflush(stdout);
+
+  test_started_count_ += retry_started_count;
+}
+
+// Helper used to parse test filter files. Syntax is documented in
+// //testing/buildbot/filters/README.md .
+bool LoadFilterFile(const FilePath& file_path,
+                    std::vector<std::string>* positive_filter,
+                    std::vector<std::string>* negative_filter) {
+  std::string file_content;
+  if (!ReadFileToString(file_path, &file_content)) {
+    LOG(ERROR) << "Failed to read the filter file.";
+    return false;
+  }
+
+  std::vector<std::string> filter_lines = SplitString(
+      file_content, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
+  int line_num = 0;
+  for (const std::string& filter_line : filter_lines) {
+    line_num++;
+
+    size_t hash_pos = filter_line.find('#');
+
+    // In case when # symbol is not in the beginning of the line and is not
+    // proceeded with a space then it's likely that the comment was
+    // unintentional.
+    if (hash_pos != std::string::npos && hash_pos > 0 &&
+        filter_line[hash_pos - 1] != ' ') {
+      LOG(WARNING) << "Content of line " << line_num << " in " << file_path
+                   << " after # is treated as a comment, " << filter_line;
+    }
+
+    // Strip comments and whitespace from each line.
+    std::string trimmed_line =
+        TrimWhitespaceASCII(filter_line.substr(0, hash_pos), TRIM_ALL)
+            .as_string();
+
+    if (trimmed_line.substr(0, 2) == "//") {
+      LOG(ERROR) << "Line " << line_num << " in " << file_path
+                 << " starts with //, use # for comments.";
+      return false;
+    }
+
+    // Treat a line starting with '//' as a comment.
+    if (trimmed_line.empty())
+      continue;
+
+    if (trimmed_line[0] == '-')
+      negative_filter->push_back(trimmed_line.substr(1));
+    else
+      positive_filter->push_back(trimmed_line);
+  }
+
+  return true;
+}
+
+bool TestLauncher::Init() {
+  const CommandLine* command_line = CommandLine::ForCurrentProcess();
+
+  // Initialize sharding. Command line takes precedence over legacy environment
+  // variables.
+  if (command_line->HasSwitch(switches::kTestLauncherTotalShards) &&
+      command_line->HasSwitch(switches::kTestLauncherShardIndex)) {
+    if (!StringToInt(
+            command_line->GetSwitchValueASCII(
+                switches::kTestLauncherTotalShards),
+            &total_shards_)) {
+      LOG(ERROR) << "Invalid value for " << switches::kTestLauncherTotalShards;
+      return false;
+    }
+    if (!StringToInt(
+            command_line->GetSwitchValueASCII(
+                switches::kTestLauncherShardIndex),
+            &shard_index_)) {
+      LOG(ERROR) << "Invalid value for " << switches::kTestLauncherShardIndex;
+      return false;
+    }
+    fprintf(stdout,
+            "Using sharding settings from command line. This is shard %d/%d\n",
+            shard_index_, total_shards_);
+    fflush(stdout);
+  } else {
+    if (!TakeInt32FromEnvironment(kTestTotalShards, &total_shards_))
+      return false;
+    if (!TakeInt32FromEnvironment(kTestShardIndex, &shard_index_))
+      return false;
+    fprintf(stdout,
+            "Using sharding settings from environment. This is shard %d/%d\n",
+            shard_index_, total_shards_);
+    fflush(stdout);
+  }
+  if (shard_index_ < 0 ||
+      total_shards_ < 0 ||
+      shard_index_ >= total_shards_) {
+    LOG(ERROR) << "Invalid sharding settings: we require 0 <= "
+               << kTestShardIndex << " < " << kTestTotalShards
+               << ", but you have " << kTestShardIndex << "=" << shard_index_
+               << ", " << kTestTotalShards << "=" << total_shards_ << ".\n";
+    return false;
+  }
+
+  // Make sure we don't pass any sharding-related environment to the child
+  // processes. This test launcher implements the sharding completely.
+  CHECK(UnsetEnvironmentVariableIfExists("GTEST_TOTAL_SHARDS"));
+  CHECK(UnsetEnvironmentVariableIfExists("GTEST_SHARD_INDEX"));
+
+  if (command_line->HasSwitch(kGTestRepeatFlag) &&
+      !StringToInt(command_line->GetSwitchValueASCII(kGTestRepeatFlag),
+                   &cycles_)) {
+    LOG(ERROR) << "Invalid value for " << kGTestRepeatFlag;
+    return false;
+  }
+
+  if (command_line->HasSwitch(switches::kTestLauncherRetryLimit)) {
+    int retry_limit = -1;
+    if (!StringToInt(command_line->GetSwitchValueASCII(
+                         switches::kTestLauncherRetryLimit), &retry_limit) ||
+        retry_limit < 0) {
+      LOG(ERROR) << "Invalid value for " << switches::kTestLauncherRetryLimit;
+      return false;
+    }
+
+    retry_limit_ = retry_limit;
+  } else if (!command_line->HasSwitch(kGTestFilterFlag) || BotModeEnabled()) {
+    // Retry failures 3 times by default if we are running all of the tests or
+    // in bot mode.
+    retry_limit_ = 3;
+  }
+
+  if (command_line->HasSwitch(switches::kTestLauncherForceRunBrokenTests))
+    force_run_broken_tests_ = true;
+
+  // Some of the TestLauncherDelegate implementations don't call into gtest
+  // until they've already split into test-specific processes. This results
+  // in gtest's native shuffle implementation attempting to shuffle one test.
+  // Shuffling the list of tests in the test launcher (before the delegate
+  // gets involved) ensures that the entire shard is shuffled.
+  if (command_line->HasSwitch(kGTestShuffleFlag)) {
+    shuffle_ = true;
+
+    if (command_line->HasSwitch(kGTestRandomSeedFlag)) {
+      const std::string custom_seed_str =
+          command_line->GetSwitchValueASCII(kGTestRandomSeedFlag);
+      uint32_t custom_seed = 0;
+      if (!StringToUint(custom_seed_str, &custom_seed)) {
+        LOG(ERROR) << "Unable to parse seed \"" << custom_seed_str << "\".";
+        return false;
+      }
+      if (custom_seed >= kRandomSeedUpperBound) {
+        LOG(ERROR) << "Seed " << custom_seed << " outside of expected range "
+                   << "[0, " << kRandomSeedUpperBound << ")";
+        return false;
+      }
+      shuffle_seed_ = custom_seed;
+    } else {
+      std::uniform_int_distribution<uint32_t> dist(0, kRandomSeedUpperBound);
+      std::random_device random_dev;
+      shuffle_seed_ = dist(random_dev);
+    }
+  } else if (command_line->HasSwitch(kGTestRandomSeedFlag)) {
+    LOG(ERROR) << kGTestRandomSeedFlag << " requires " << kGTestShuffleFlag;
+    return false;
+  }
+
+  fprintf(stdout, "Using %" PRIuS " parallel jobs.\n", parallel_jobs_);
+  fflush(stdout);
+
+  CreateAndStartTaskScheduler(static_cast<int>(parallel_jobs_));
+
+  std::vector<std::string> positive_file_filter;
+  std::vector<std::string> positive_gtest_filter;
+
+  if (command_line->HasSwitch(switches::kTestLauncherFilterFile)) {
+    base::FilePath filter_file_path = base::MakeAbsoluteFilePath(
+        command_line->GetSwitchValuePath(switches::kTestLauncherFilterFile));
+    if (!LoadFilterFile(filter_file_path, &positive_file_filter,
+                        &negative_test_filter_))
+      return false;
+  }
+
+  // Split --gtest_filter at '-', if there is one, to separate into
+  // positive filter and negative filter portions.
+  std::string filter = command_line->GetSwitchValueASCII(kGTestFilterFlag);
+  size_t dash_pos = filter.find('-');
+  if (dash_pos == std::string::npos) {
+    positive_gtest_filter =
+        SplitString(filter, ":", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
+  } else {
+    // Everything up to the dash.
+    positive_gtest_filter =
+        SplitString(filter.substr(0, dash_pos), ":", base::TRIM_WHITESPACE,
+                    base::SPLIT_WANT_ALL);
+
+    // Everything after the dash.
+    for (std::string pattern :
+         SplitString(filter.substr(dash_pos + 1), ":", base::TRIM_WHITESPACE,
+                     base::SPLIT_WANT_ALL)) {
+      negative_test_filter_.push_back(pattern);
+    }
+  }
+
+  if (!launcher_delegate_->GetTests(&tests_)) {
+    LOG(ERROR) << "Failed to get list of tests.";
+    return false;
+  }
+
+  CombinePositiveTestFilters(std::move(positive_gtest_filter),
+                             std::move(positive_file_filter));
+
+  if (!results_tracker_.Init(*command_line)) {
+    LOG(ERROR) << "Failed to initialize test results tracker.";
+    return 1;
+  }
+
+#if defined(NDEBUG)
+  results_tracker_.AddGlobalTag("MODE_RELEASE");
+#else
+  results_tracker_.AddGlobalTag("MODE_DEBUG");
+#endif
+
+  // Operating systems (sorted alphabetically).
+  // Note that they can deliberately overlap, e.g. OS_LINUX is a subset
+  // of OS_POSIX.
+#if defined(OS_ANDROID)
+  results_tracker_.AddGlobalTag("OS_ANDROID");
+#endif
+
+#if defined(OS_BSD)
+  results_tracker_.AddGlobalTag("OS_BSD");
+#endif
+
+#if defined(OS_FREEBSD)
+  results_tracker_.AddGlobalTag("OS_FREEBSD");
+#endif
+
+#if defined(OS_FUCHSIA)
+  results_tracker_.AddGlobalTag("OS_FUCHSIA");
+#endif
+
+#if defined(OS_IOS)
+  results_tracker_.AddGlobalTag("OS_IOS");
+#endif
+
+#if defined(OS_LINUX)
+  results_tracker_.AddGlobalTag("OS_LINUX");
+#endif
+
+#if defined(OS_MACOSX)
+  results_tracker_.AddGlobalTag("OS_MACOSX");
+#endif
+
+#if defined(OS_NACL)
+  results_tracker_.AddGlobalTag("OS_NACL");
+#endif
+
+#if defined(OS_OPENBSD)
+  results_tracker_.AddGlobalTag("OS_OPENBSD");
+#endif
+
+#if defined(OS_POSIX)
+  results_tracker_.AddGlobalTag("OS_POSIX");
+#endif
+
+#if defined(OS_SOLARIS)
+  results_tracker_.AddGlobalTag("OS_SOLARIS");
+#endif
+
+#if defined(OS_WIN)
+  results_tracker_.AddGlobalTag("OS_WIN");
+#endif
+
+  // CPU-related tags.
+#if defined(ARCH_CPU_32_BITS)
+  results_tracker_.AddGlobalTag("CPU_32_BITS");
+#endif
+
+#if defined(ARCH_CPU_64_BITS)
+  results_tracker_.AddGlobalTag("CPU_64_BITS");
+#endif
+
+  return true;
+}
+
+void TestLauncher::CombinePositiveTestFilters(
+    std::vector<std::string> filter_a,
+    std::vector<std::string> filter_b) {
+  has_at_least_one_positive_filter_ = !filter_a.empty() || !filter_b.empty();
+  if (!has_at_least_one_positive_filter_) {
+    return;
+  }
+  // If two positive filters are present, only run tests that match a pattern
+  // in both filters.
+  if (!filter_a.empty() && !filter_b.empty()) {
+    for (size_t i = 0; i < tests_.size(); i++) {
+      std::string test_name =
+          FormatFullTestName(tests_[i].test_case_name, tests_[i].test_name);
+      bool found_a = false;
+      bool found_b = false;
+      for (size_t k = 0; k < filter_a.size(); ++k) {
+        found_a = found_a || MatchPattern(test_name, filter_a[k]);
+      }
+      for (size_t k = 0; k < filter_b.size(); ++k) {
+        found_b = found_b || MatchPattern(test_name, filter_b[k]);
+      }
+      if (found_a && found_b) {
+        positive_test_filter_.push_back(test_name);
+      }
+    }
+  } else if (!filter_a.empty()) {
+    positive_test_filter_ = std::move(filter_a);
+  } else {
+    positive_test_filter_ = std::move(filter_b);
+  }
+}
+
+void TestLauncher::RunTests() {
+  std::vector<std::string> test_names;
+  const CommandLine* command_line = CommandLine::ForCurrentProcess();
+  for (const TestIdentifier& test_id : tests_) {
+    std::string test_name =
+        FormatFullTestName(test_id.test_case_name, test_id.test_name);
+
+    results_tracker_.AddTest(test_name);
+
+    if (test_name.find("DISABLED") != std::string::npos) {
+      results_tracker_.AddDisabledTest(test_name);
+
+      // Skip disabled tests unless explicitly requested.
+      if (!command_line->HasSwitch(kGTestRunDisabledTestsFlag))
+        continue;
+    }
+
+    if (!launcher_delegate_->ShouldRunTest(test_id.test_case_name,
+                                           test_id.test_name)) {
+      continue;
+    }
+
+    // Count tests in the binary, before we apply filter and sharding.
+    test_found_count_++;
+
+    std::string test_name_no_disabled =
+        TestNameWithoutDisabledPrefix(test_name);
+
+    // Skip the test that doesn't match the filter (if given).
+    if (has_at_least_one_positive_filter_) {
+      bool found = false;
+      for (auto filter : positive_test_filter_) {
+        if (MatchPattern(test_name, filter) ||
+            MatchPattern(test_name_no_disabled, filter)) {
+          found = true;
+          break;
+        }
+      }
+
+      if (!found)
+        continue;
+    }
+    if (!negative_test_filter_.empty()) {
+      bool excluded = false;
+      for (auto filter : negative_test_filter_) {
+        if (MatchPattern(test_name, filter) ||
+            MatchPattern(test_name_no_disabled, filter)) {
+          excluded = true;
+          break;
+        }
+      }
+
+      if (excluded)
+        continue;
+    }
+
+    if (Hash(test_name) % total_shards_ != static_cast<uint32_t>(shard_index_))
+      continue;
+
+    // Report test locations after applying all filters, so that we report test
+    // locations only for those tests that were run as part of this shard.
+    results_tracker_.AddTestLocation(test_name, test_id.file, test_id.line);
+
+    test_names.push_back(test_name);
+  }
+
+  if (shuffle_) {
+    std::mt19937 randomizer;
+    randomizer.seed(shuffle_seed_);
+    std::shuffle(test_names.begin(), test_names.end(), randomizer);
+
+    fprintf(stdout, "Randomizing with seed %u\n", shuffle_seed_);
+    fflush(stdout);
+  }
+
+  // Save an early test summary in case the launcher crashes or gets killed.
+  MaybeSaveSummaryAsJSON({"EARLY_SUMMARY", kUnreliableResultsTag});
+
+  test_started_count_ = launcher_delegate_->RunTests(this, test_names);
+
+  if (test_started_count_ == 0) {
+    fprintf(stdout, "0 tests run\n");
+    fflush(stdout);
+
+    // No tests have actually been started, so kick off the next iteration.
+    ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE, BindOnce(&TestLauncher::RunTestIteration, Unretained(this)));
+  }
+}
+
+void TestLauncher::RunTestIteration() {
+  const bool stop_on_failure =
+      CommandLine::ForCurrentProcess()->HasSwitch(kGTestBreakOnFailure);
+  if (cycles_ == 0 ||
+      (stop_on_failure && test_success_count_ != test_finished_count_)) {
+    RunLoop::QuitCurrentWhenIdleDeprecated();
+    return;
+  }
+
+  // Special value "-1" means "repeat indefinitely".
+  cycles_ = (cycles_ == -1) ? cycles_ : cycles_ - 1;
+
+  test_found_count_ = 0;
+  test_started_count_ = 0;
+  test_finished_count_ = 0;
+  test_success_count_ = 0;
+  test_broken_count_ = 0;
+  retry_count_ = 0;
+  tests_to_retry_.clear();
+  results_tracker_.OnTestIterationStarting();
+
+  ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, BindOnce(&TestLauncher::RunTests, Unretained(this)));
+}
+
+#if defined(OS_POSIX)
+// I/O watcher for the reading end of the self-pipe above.
+// Terminates any launched child processes and exits the process.
+void TestLauncher::OnShutdownPipeReadable() {
+  fprintf(stdout, "\nCaught signal. Killing spawned test processes...\n");
+  fflush(stdout);
+
+  KillSpawnedTestProcesses();
+
+  MaybeSaveSummaryAsJSON({"CAUGHT_TERMINATION_SIGNAL", kUnreliableResultsTag});
+
+  // The signal would normally kill the process, so exit now.
+  _exit(1);
+}
+#endif  // defined(OS_POSIX)
+
+void TestLauncher::MaybeSaveSummaryAsJSON(
+    const std::vector<std::string>& additional_tags) {
+  const CommandLine* command_line = CommandLine::ForCurrentProcess();
+  if (command_line->HasSwitch(switches::kTestLauncherSummaryOutput)) {
+    FilePath summary_path(command_line->GetSwitchValuePath(
+                              switches::kTestLauncherSummaryOutput));
+    if (!results_tracker_.SaveSummaryAsJSON(summary_path, additional_tags)) {
+      LOG(ERROR) << "Failed to save test launcher output summary.";
+    }
+  }
+  if (command_line->HasSwitch(switches::kTestLauncherTrace)) {
+    FilePath trace_path(
+        command_line->GetSwitchValuePath(switches::kTestLauncherTrace));
+    if (!GetTestLauncherTracer()->Dump(trace_path)) {
+      LOG(ERROR) << "Failed to save test launcher trace.";
+    }
+  }
+}
+
+void TestLauncher::OnTestIterationFinished() {
+  TestResultsTracker::TestStatusMap tests_by_status(
+      results_tracker_.GetTestStatusMapForCurrentIteration());
+  if (!tests_by_status[TestResult::TEST_UNKNOWN].empty())
+    results_tracker_.AddGlobalTag(kUnreliableResultsTag);
+
+  // When we retry tests, success is determined by having nothing more
+  // to retry (everything eventually passed), as opposed to having
+  // no failures at all.
+  if (tests_to_retry_.empty()) {
+    fprintf(stdout, "SUCCESS: all tests passed.\n");
+    fflush(stdout);
+  } else {
+    // Signal failure, but continue to run all requested test iterations.
+    // With the summary of all iterations at the end this is a good default.
+    run_result_ = false;
+  }
+
+  results_tracker_.PrintSummaryOfCurrentIteration();
+
+  // Kick off the next iteration.
+  ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, BindOnce(&TestLauncher::RunTestIteration, Unretained(this)));
+}
+
+void TestLauncher::OnOutputTimeout() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  AutoLock lock(*GetLiveProcessesLock());
+
+  fprintf(stdout, "Still waiting for the following processes to finish:\n");
+
+  for (const auto& pair : *GetLiveProcesses()) {
+#if defined(OS_WIN)
+    fwprintf(stdout, L"\t%s\n", pair.second.GetCommandLineString().c_str());
+#else
+    fprintf(stdout, "\t%s\n", pair.second.GetCommandLineString().c_str());
+#endif
+  }
+
+  fflush(stdout);
+
+  // Arm the timer again - otherwise it would fire only once.
+  watchdog_timer_.Reset();
+}
+
+size_t NumParallelJobs() {
+  const CommandLine* command_line = CommandLine::ForCurrentProcess();
+  if (command_line->HasSwitch(switches::kTestLauncherJobs)) {
+    // If the number of test launcher jobs was specified, return that number.
+    size_t jobs = 0U;
+
+    if (!StringToSizeT(
+            command_line->GetSwitchValueASCII(switches::kTestLauncherJobs),
+            &jobs) ||
+        !jobs) {
+      LOG(ERROR) << "Invalid value for " << switches::kTestLauncherJobs;
+      return 0U;
+    }
+    return jobs;
+  }
+  if (command_line->HasSwitch(kGTestFilterFlag) && !BotModeEnabled()) {
+    // Do not run jobs in parallel by default if we are running a subset of
+    // the tests and if bot mode is off.
+    return 1U;
+  }
+
+  // Default to the number of processor cores.
+  return base::checked_cast<size_t>(SysInfo::NumberOfProcessors());
+}
+
+std::string GetTestOutputSnippet(const TestResult& result,
+                                 const std::string& full_output) {
+  size_t run_pos = full_output.find(std::string("[ RUN      ] ") +
+                                    result.full_name);
+  if (run_pos == std::string::npos)
+    return std::string();
+
+  size_t end_pos = full_output.find(std::string("[  FAILED  ] ") +
+                                    result.full_name,
+                                    run_pos);
+  // Only clip the snippet to the "OK" message if the test really
+  // succeeded. It still might have e.g. crashed after printing it.
+  if (end_pos == std::string::npos &&
+      result.status == TestResult::TEST_SUCCESS) {
+    end_pos = full_output.find(std::string("[       OK ] ") +
+                               result.full_name,
+                               run_pos);
+  }
+  if (end_pos != std::string::npos) {
+    size_t newline_pos = full_output.find("\n", end_pos);
+    if (newline_pos != std::string::npos)
+      end_pos = newline_pos + 1;
+  }
+
+  std::string snippet(full_output.substr(run_pos));
+  if (end_pos != std::string::npos)
+    snippet = full_output.substr(run_pos, end_pos - run_pos);
+
+  return snippet;
+}
+
+}  // namespace base
diff --git a/base/test/launcher/test_launcher.h b/base/test/launcher/test_launcher.h
new file mode 100644
index 0000000..9ac45ba
--- /dev/null
+++ b/base/test/launcher/test_launcher.h
@@ -0,0 +1,268 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_LAUNCHER_TEST_LAUNCHER_H_
+#define BASE_TEST_LAUNCHER_TEST_LAUNCHER_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/process/launch.h"
+#include "base/test/gtest_util.h"
+#include "base/test/launcher/test_result.h"
+#include "base/test/launcher/test_results_tracker.h"
+#include "base/threading/thread_checker.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
+#include "build/build_config.h"
+
+namespace base {
+
+class CommandLine;
+struct LaunchOptions;
+class TestLauncher;
+
+// Constants for GTest command-line flags.
+extern const char kGTestFilterFlag[];
+extern const char kGTestFlagfileFlag[];
+extern const char kGTestHelpFlag[];
+extern const char kGTestListTestsFlag[];
+extern const char kGTestRepeatFlag[];
+extern const char kGTestRunDisabledTestsFlag[];
+extern const char kGTestOutputFlag[];
+extern const char kGTestShuffleFlag[];
+extern const char kGTestRandomSeedFlag[];
+
+// Interface for use with LaunchTests that abstracts away exact details
+// which tests and how are run.
+class TestLauncherDelegate {
+ public:
+  // Called to get names of tests available for running. The delegate
+  // must put the result in |output| and return true on success.
+  virtual bool GetTests(std::vector<TestIdentifier>* output) = 0;
+
+  // Called before a test is considered for running. If it returns false,
+  // the test is not run. If it returns true, the test will be run provided
+  // it is part of the current shard.
+  virtual bool ShouldRunTest(const std::string& test_case_name,
+                             const std::string& test_name) = 0;
+
+  // Called to make the delegate run the specified tests. The delegate must
+  // return the number of actual tests it's going to run (can be smaller,
+  // equal to, or larger than size of |test_names|). It must also call
+  // |test_launcher|'s OnTestFinished method once per every run test,
+  // regardless of its success.
+  virtual size_t RunTests(TestLauncher* test_launcher,
+                          const std::vector<std::string>& test_names) = 0;
+
+  // Called to make the delegate retry the specified tests. The delegate must
+  // return the number of actual tests it's going to retry (can be smaller,
+  // equal to, or larger than size of |test_names|). It must also call
+  // |test_launcher|'s OnTestFinished method once per every retried test,
+  // regardless of its success.
+  virtual size_t RetryTests(TestLauncher* test_launcher,
+                            const std::vector<std::string>& test_names) = 0;
+
+ protected:
+  virtual ~TestLauncherDelegate();
+};
+
+// An observer of child process lifetime events generated by
+// LaunchChildGTestProcess.
+class ProcessLifetimeObserver {
+ public:
+  virtual ~ProcessLifetimeObserver() = default;
+
+  // Invoked once the child process is started. |handle| is a handle to the
+  // child process and |id| is its pid. NOTE: this method is invoked on the
+  // thread the process is launched on immediately after it is launched. The
+  // caller owns the ProcessHandle.
+  virtual void OnLaunched(ProcessHandle handle, ProcessId id) {}
+
+  // Invoked when a test process exceeds its runtime, immediately before it is
+  // terminated. |command_line| is the command line used to launch the process.
+  // NOTE: this method is invoked on the thread the process is launched on.
+  virtual void OnTimedOut(const CommandLine& command_line) {}
+
+  // Invoked after a child process finishes, reporting the process |exit_code|,
+  // child process |elapsed_time|, whether or not the process was terminated as
+  // a result of a timeout, and the output of the child (stdout and stderr
+  // together). NOTE: this method is invoked on the same thread as
+  // LaunchChildGTestProcess.
+  virtual void OnCompleted(int exit_code,
+                           TimeDelta elapsed_time,
+                           bool was_timeout,
+                           const std::string& output) {}
+
+ protected:
+  ProcessLifetimeObserver() = default;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ProcessLifetimeObserver);
+};
+
+// Launches tests using a TestLauncherDelegate.
+class TestLauncher {
+ public:
+  // Flags controlling behavior of LaunchChildGTestProcess.
+  enum LaunchChildGTestProcessFlags {
+    // Allows usage of job objects on Windows. Helps properly clean up child
+    // processes.
+    USE_JOB_OBJECTS = (1 << 0),
+
+    // Allows breakaway from job on Windows. May result in some child processes
+    // not being properly terminated after launcher dies if these processes
+    // fail to cooperate.
+    ALLOW_BREAKAWAY_FROM_JOB = (1 << 1),
+  };
+
+  struct LaunchOptions {
+    LaunchOptions();
+    LaunchOptions(const LaunchOptions& other);
+    ~LaunchOptions();
+
+    int flags = 0;
+    // These mirror values in base::LaunchOptions, see it for details.
+#if defined(OS_WIN)
+    base::LaunchOptions::Inherit inherit_mode =
+        base::LaunchOptions::Inherit::kSpecific;
+    base::HandlesToInheritVector handles_to_inherit;
+#else
+    FileHandleMappingVector fds_to_remap;
+#endif
+  };
+
+  // Constructor. |parallel_jobs| is the limit of simultaneous parallel test
+  // jobs.
+  TestLauncher(TestLauncherDelegate* launcher_delegate, size_t parallel_jobs);
+  ~TestLauncher();
+
+  // Runs the launcher. Must be called at most once.
+  bool Run() WARN_UNUSED_RESULT;
+
+  // Launches a child process (assumed to be gtest-based binary) using
+  // |command_line|. If |wrapper| is not empty, it is prepended to the final
+  // command line. |observer|, if not null, is used to convey process lifetime
+  // events to the caller. |observer| is destroyed after its OnCompleted
+  // method is invoked.
+  void LaunchChildGTestProcess(
+      const CommandLine& command_line,
+      const std::string& wrapper,
+      TimeDelta timeout,
+      const LaunchOptions& options,
+      std::unique_ptr<ProcessLifetimeObserver> observer);
+
+  // Called when a test has finished running.
+  void OnTestFinished(const TestResult& result);
+
+ private:
+  bool Init() WARN_UNUSED_RESULT;
+
+  // Runs all tests in current iteration.
+  void RunTests();
+
+  void CombinePositiveTestFilters(std::vector<std::string> filter_a,
+                                  std::vector<std::string> filter_b);
+
+  void RunTestIteration();
+
+#if defined(OS_POSIX)
+  void OnShutdownPipeReadable();
+#endif
+
+  // Saves test results summary as JSON if requested from command line.
+  void MaybeSaveSummaryAsJSON(const std::vector<std::string>& additional_tags);
+
+  // Called when a test iteration is finished.
+  void OnTestIterationFinished();
+
+  // Called by the delay timer when no output was made for a while.
+  void OnOutputTimeout();
+
+  // Make sure we don't accidentally call the wrong methods e.g. on the worker
+  // pool thread.  Should be the first member so that it's destroyed last: when
+  // destroying other members, especially the worker pool, we may check the code
+  // is running on the correct thread.
+  ThreadChecker thread_checker_;
+
+  TestLauncherDelegate* launcher_delegate_;
+
+  // Support for outer sharding, just like gtest does.
+  int32_t total_shards_;  // Total number of outer shards, at least one.
+  int32_t shard_index_;   // Index of shard the launcher is to run.
+
+  int cycles_;  // Number of remaining test iterations, or -1 for infinite.
+
+  // Test filters (empty means no filter).
+  bool has_at_least_one_positive_filter_;
+  std::vector<std::string> positive_test_filter_;
+  std::vector<std::string> negative_test_filter_;
+
+  // Tests to use (cached result of TestLauncherDelegate::GetTests).
+  std::vector<TestIdentifier> tests_;
+
+  // Number of tests found in this binary.
+  size_t test_found_count_;
+
+  // Number of tests started in this iteration.
+  size_t test_started_count_;
+
+  // Number of tests finished in this iteration.
+  size_t test_finished_count_;
+
+  // Number of tests successfully finished in this iteration.
+  size_t test_success_count_;
+
+  // Number of tests either timing out or having an unknown result,
+  // likely indicating a more systemic problem if widespread.
+  size_t test_broken_count_;
+
+  // Number of retries in this iteration.
+  size_t retry_count_;
+
+  // Maximum number of retries per iteration.
+  size_t retry_limit_;
+
+  // If true will not early exit nor skip retries even if too many tests are
+  // broken.
+  bool force_run_broken_tests_;
+
+  // Tests to retry in this iteration.
+  std::set<std::string> tests_to_retry_;
+
+  // Result to be returned from Run.
+  bool run_result_;
+
+  // Support for test shuffling, just like gtest does.
+  bool shuffle_;
+  uint32_t shuffle_seed_;
+
+  TestResultsTracker results_tracker_;
+
+  // Watchdog timer to make sure we do not go without output for too long.
+  DelayTimer watchdog_timer_;
+
+  // Number of jobs to run in parallel.
+  size_t parallel_jobs_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestLauncher);
+};
+
+// Return the number of parallel jobs to use, or 0U in case of error.
+size_t NumParallelJobs();
+
+// Extract part from |full_output| that applies to |result|.
+std::string GetTestOutputSnippet(const TestResult& result,
+                                 const std::string& full_output);
+
+}  // namespace base
+
+#endif  // BASE_TEST_LAUNCHER_TEST_LAUNCHER_H_
diff --git a/base/test/launcher/test_launcher_tracer.cc b/base/test/launcher/test_launcher_tracer.cc
new file mode 100644
index 0000000..d525df7
--- /dev/null
+++ b/base/test/launcher/test_launcher_tracer.cc
@@ -0,0 +1,53 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/launcher/test_launcher_tracer.h"
+
+#include "base/json/json_file_value_serializer.h"
+#include "base/strings/stringprintf.h"
+#include "base/values.h"
+
+namespace base {
+
+TestLauncherTracer::TestLauncherTracer()
+    : trace_start_time_(TimeTicks::Now()) {}
+
+TestLauncherTracer::~TestLauncherTracer() = default;
+
+void TestLauncherTracer::RecordProcessExecution(TimeTicks start_time,
+                                                TimeDelta duration) {
+  AutoLock lock(lock_);
+
+  Event event;
+  event.name = StringPrintf("process #%zu", events_.size());
+  event.timestamp = start_time;
+  event.duration = duration;
+  event.thread_id = PlatformThread::CurrentId();
+  events_.push_back(event);
+}
+
+bool TestLauncherTracer::Dump(const FilePath& path) {
+  AutoLock lock(lock_);
+
+  std::unique_ptr<ListValue> json_events(new ListValue);
+  for (const Event& event : events_) {
+    std::unique_ptr<DictionaryValue> json_event(new DictionaryValue);
+    json_event->SetString("name", event.name);
+    json_event->SetString("ph", "X");
+    json_event->SetInteger(
+        "ts", (event.timestamp - trace_start_time_).InMicroseconds());
+    json_event->SetInteger("dur", event.duration.InMicroseconds());
+    json_event->SetInteger("tid", event.thread_id);
+
+    // Add fake values required by the trace viewer.
+    json_event->SetInteger("pid", 0);
+
+    json_events->Append(std::move(json_event));
+  }
+
+  JSONFileValueSerializer serializer(path);
+  return serializer.Serialize(*json_events);
+}
+
+}  // namespace base
diff --git a/base/test/launcher/test_launcher_tracer.h b/base/test/launcher/test_launcher_tracer.h
new file mode 100644
index 0000000..58bc1b0
--- /dev/null
+++ b/base/test/launcher/test_launcher_tracer.h
@@ -0,0 +1,55 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_LAUNCHER_TEST_LAUNCHER_TRACER_H_
+#define BASE_TEST_LAUNCHER_TEST_LAUNCHER_TRACER_H_
+
+#include <string>
+#include <vector>
+
+#include "base/synchronization/lock.h"
+#include "base/threading/platform_thread.h"
+#include "base/time/time.h"
+
+namespace base {
+
+class FilePath;
+
+// Records traces of test execution, e.g. to analyze performance.
+// Thread safe.
+class TestLauncherTracer {
+ public:
+  TestLauncherTracer();
+  ~TestLauncherTracer();
+
+  // Records an event corresponding to test process execution.
+  void RecordProcessExecution(TimeTicks start_time, TimeDelta duration);
+
+  // Dumps trace data as JSON. Returns true on success.
+  bool Dump(const FilePath& path) WARN_UNUSED_RESULT;
+
+ private:
+  // Simplified version of base::TraceEvent.
+  struct Event {
+    std::string name;            // Displayed name.
+    TimeTicks timestamp;         // Timestamp when this event began.
+    TimeDelta duration;          // How long was this event.
+    PlatformThreadId thread_id;  // Thread ID where event was reported.
+  };
+
+  // Timestamp when tracing started.
+  TimeTicks trace_start_time_;
+
+  // Log of trace events.
+  std::vector<Event> events_;
+
+  // Lock to protect all member variables.
+  Lock lock_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestLauncherTracer);
+};
+
+}  // namespace base
+
+#endif  // BASE_TEST_LAUNCHER_TEST_LAUNCHER_TRACER_H_
diff --git a/base/test/launcher/test_result.cc b/base/test/launcher/test_result.cc
new file mode 100644
index 0000000..9f37a2b
--- /dev/null
+++ b/base/test/launcher/test_result.cc
@@ -0,0 +1,96 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/launcher/test_result.h"
+
+#include <stddef.h>
+
+#include "base/logging.h"
+
+namespace base {
+
+TestResultPart::TestResultPart() = default;
+TestResultPart::~TestResultPart() = default;
+
+TestResultPart::TestResultPart(const TestResultPart& other) = default;
+TestResultPart::TestResultPart(TestResultPart&& other) = default;
+TestResultPart& TestResultPart::operator=(const TestResultPart& other) =
+    default;
+TestResultPart& TestResultPart::operator=(TestResultPart&& other) = default;
+
+// static
+bool TestResultPart::TypeFromString(const std::string& str, Type* type) {
+  if (str == "success")
+    *type = kSuccess;
+  else if (str == "failure")
+    *type = kNonFatalFailure;
+  else if (str == "fatal_failure")
+    *type = kFatalFailure;
+  else
+    return false;
+  return true;
+}
+
+std::string TestResultPart::TypeAsString() const {
+  switch (type) {
+    case kSuccess:
+      return "success";
+    case kNonFatalFailure:
+      return "failure";
+    case kFatalFailure:
+      return "fatal_failure";
+    default:
+      NOTREACHED();
+  }
+  return "unknown";
+}
+
+TestResult::TestResult() : status(TEST_UNKNOWN) {
+}
+
+TestResult::~TestResult() = default;
+
+TestResult::TestResult(const TestResult& other) = default;
+TestResult::TestResult(TestResult&& other) = default;
+TestResult& TestResult::operator=(const TestResult& other) = default;
+TestResult& TestResult::operator=(TestResult&& other) = default;
+
+std::string TestResult::StatusAsString() const {
+  switch (status) {
+    case TEST_UNKNOWN:
+      return "UNKNOWN";
+    case TEST_SUCCESS:
+      return "SUCCESS";
+    case TEST_FAILURE:
+      return "FAILURE";
+    case TEST_FAILURE_ON_EXIT:
+      return "FAILURE_ON_EXIT";
+    case TEST_CRASH:
+      return "CRASH";
+    case TEST_TIMEOUT:
+      return "TIMEOUT";
+    case TEST_SKIPPED:
+      return "SKIPPED";
+    case TEST_EXCESSIVE_OUTPUT:
+      return "EXCESSIVE_OUTPUT";
+     // Rely on compiler warnings to ensure all possible values are handled.
+  }
+
+  NOTREACHED();
+  return std::string();
+}
+
+std::string TestResult::GetTestName() const {
+  size_t dot_pos = full_name.find('.');
+  CHECK_NE(dot_pos, std::string::npos);
+  return full_name.substr(dot_pos + 1);
+}
+
+std::string TestResult::GetTestCaseName() const {
+  size_t dot_pos = full_name.find('.');
+  CHECK_NE(dot_pos, std::string::npos);
+  return full_name.substr(0, dot_pos);
+}
+
+}  // namespace base
diff --git a/base/test/launcher/test_result.h b/base/test/launcher/test_result.h
new file mode 100644
index 0000000..07338b3
--- /dev/null
+++ b/base/test/launcher/test_result.h
@@ -0,0 +1,104 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_LAUNCHER_TEST_RESULT_H_
+#define BASE_TEST_LAUNCHER_TEST_RESULT_H_
+
+#include <string>
+#include <vector>
+
+#include "base/time/time.h"
+
+namespace base {
+
+// Structure contains result of a single EXPECT/ASSERT/SUCCESS.
+struct TestResultPart {
+  enum Type {
+    kSuccess,          // SUCCESS
+    kNonFatalFailure,  // EXPECT
+    kFatalFailure,     // ASSERT
+  };
+  Type type;
+
+  TestResultPart();
+  ~TestResultPart();
+
+  TestResultPart(const TestResultPart& other);
+  TestResultPart(TestResultPart&& other);
+  TestResultPart& operator=(const TestResultPart& other);
+  TestResultPart& operator=(TestResultPart&& other);
+
+  // Convert type to string and back.
+  static bool TypeFromString(const std::string& str, Type* type);
+  std::string TypeAsString() const;
+
+  // Filename and line of EXPECT/ASSERT.
+  std::string file_name;
+  int line_number;
+
+  // Message without stacktrace, etc.
+  std::string summary;
+
+  // Complete message.
+  std::string message;
+};
+
+// Structure containing result of a single test.
+struct TestResult {
+  enum Status {
+    TEST_UNKNOWN,           // Status not set.
+    TEST_SUCCESS,           // Test passed.
+    TEST_FAILURE,           // Assertion failure (e.g. EXPECT_TRUE, not DCHECK).
+    TEST_FAILURE_ON_EXIT,   // Passed but executable exit code was non-zero.
+    TEST_TIMEOUT,           // Test timed out and was killed.
+    TEST_CRASH,             // Test crashed (includes CHECK/DCHECK failures).
+    TEST_SKIPPED,           // Test skipped (not run at all).
+    TEST_EXCESSIVE_OUTPUT,  // Test exceeded output limit.
+  };
+
+  TestResult();
+  ~TestResult();
+
+  TestResult(const TestResult& other);
+  TestResult(TestResult&& other);
+  TestResult& operator=(const TestResult& other);
+  TestResult& operator=(TestResult&& other);
+
+  // Returns the test status as string (e.g. for display).
+  std::string StatusAsString() const;
+
+  // Returns the test name (e.g. "B" for "A.B").
+  std::string GetTestName() const;
+
+  // Returns the test case name (e.g. "A" for "A.B").
+  std::string GetTestCaseName() const;
+
+  // Returns true if the test has completed (i.e. the test binary exited
+  // normally, possibly with an exit code indicating failure, but didn't crash
+  // or time out in the middle of the test).
+  bool completed() const {
+    return status == TEST_SUCCESS ||
+        status == TEST_FAILURE ||
+        status == TEST_FAILURE_ON_EXIT ||
+        status == TEST_EXCESSIVE_OUTPUT;
+  }
+
+  // Full name of the test (e.g. "A.B").
+  std::string full_name;
+
+  Status status;
+
+  // Time it took to run the test.
+  base::TimeDelta elapsed_time;
+
+  // Output of just this test (optional).
+  std::string output_snippet;
+
+  // Information about failed expectations.
+  std::vector<TestResultPart> test_result_parts;
+};
+
+}  // namespace base
+
+#endif  // BASE_TEST_LAUNCHER_TEST_RESULT_H_
diff --git a/base/test/launcher/test_results_tracker.cc b/base/test/launcher/test_results_tracker.cc
new file mode 100644
index 0000000..a7e590c
--- /dev/null
+++ b/base/test/launcher/test_results_tracker.cc
@@ -0,0 +1,541 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/launcher/test_results_tracker.h"
+
+#include <stddef.h>
+
+#include <memory>
+#include <utility>
+
+#include "base/base64.h"
+#include "base/command_line.h"
+#include "base/files/file.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/format_macros.h"
+#include "base/json/json_writer.h"
+#include "base/json/string_escape.h"
+#include "base/logging.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/test/gtest_util.h"
+#include "base/test/launcher/test_launcher.h"
+#include "base/time/time.h"
+#include "base/values.h"
+
+namespace base {
+
+namespace {
+
+// The default output file for XML output.
+const FilePath::CharType kDefaultOutputFile[] = FILE_PATH_LITERAL(
+    "test_detail.xml");
+
+// Converts the given epoch time in milliseconds to a date string in the ISO
+// 8601 format, without the timezone information.
+// TODO(xyzzyz): Find a good place in Chromium to put it and refactor all uses
+// to point to it.
+std::string FormatTimeAsIso8601(Time time) {
+  Time::Exploded exploded;
+  time.UTCExplode(&exploded);
+  return StringPrintf("%04d-%02d-%02dT%02d:%02d:%02d",
+                      exploded.year,
+                      exploded.month,
+                      exploded.day_of_month,
+                      exploded.hour,
+                      exploded.minute,
+                      exploded.second);
+}
+
+struct TestSuiteResultsAggregator {
+  TestSuiteResultsAggregator()
+      : tests(0), failures(0), disabled(0), errors(0) {}
+
+  void Add(const TestResult& result) {
+    tests++;
+    elapsed_time += result.elapsed_time;
+
+    switch (result.status) {
+      case TestResult::TEST_SUCCESS:
+        break;
+      case TestResult::TEST_FAILURE:
+        failures++;
+        break;
+      case TestResult::TEST_EXCESSIVE_OUTPUT:
+      case TestResult::TEST_FAILURE_ON_EXIT:
+      case TestResult::TEST_TIMEOUT:
+      case TestResult::TEST_CRASH:
+      case TestResult::TEST_UNKNOWN:
+        errors++;
+        break;
+      case TestResult::TEST_SKIPPED:
+        disabled++;
+        break;
+    }
+  }
+
+  int tests;
+  int failures;
+  int disabled;
+  int errors;
+
+  TimeDelta elapsed_time;
+};
+
+}  // namespace
+
+TestResultsTracker::TestResultsTracker() : iteration_(-1), out_(nullptr) {}
+
+TestResultsTracker::~TestResultsTracker() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  if (!out_)
+    return;
+
+  // Maps test case names to test results.
+  typedef std::map<std::string, std::vector<TestResult> > TestCaseMap;
+  TestCaseMap test_case_map;
+
+  TestSuiteResultsAggregator all_tests_aggregator;
+  for (const PerIterationData::ResultsMap::value_type& i
+           : per_iteration_data_[iteration_].results) {
+    // Use the last test result as the final one.
+    TestResult result = i.second.test_results.back();
+    test_case_map[result.GetTestCaseName()].push_back(result);
+    all_tests_aggregator.Add(result);
+  }
+
+  fprintf(out_, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
+  fprintf(out_,
+          "<testsuites name=\"AllTests\" tests=\"%d\" failures=\"%d\""
+          " disabled=\"%d\" errors=\"%d\" time=\"%.3f\" timestamp=\"%s\">\n",
+          all_tests_aggregator.tests, all_tests_aggregator.failures,
+          all_tests_aggregator.disabled, all_tests_aggregator.errors,
+          all_tests_aggregator.elapsed_time.InSecondsF(),
+          FormatTimeAsIso8601(Time::Now()).c_str());
+
+  for (const TestCaseMap::value_type& i : test_case_map) {
+    const std::string testsuite_name = i.first;
+    const std::vector<TestResult>& results = i.second;
+
+    TestSuiteResultsAggregator aggregator;
+    for (const TestResult& result : results) {
+      aggregator.Add(result);
+    }
+    fprintf(out_,
+            "  <testsuite name=\"%s\" tests=\"%d\" "
+            "failures=\"%d\" disabled=\"%d\" errors=\"%d\" time=\"%.3f\" "
+            "timestamp=\"%s\">\n",
+            testsuite_name.c_str(), aggregator.tests, aggregator.failures,
+            aggregator.disabled, aggregator.errors,
+            aggregator.elapsed_time.InSecondsF(),
+            FormatTimeAsIso8601(Time::Now()).c_str());
+
+    for (const TestResult& result : results) {
+      fprintf(out_, "    <testcase name=\"%s\" status=\"run\" time=\"%.3f\""
+              " classname=\"%s\">\n",
+              result.GetTestName().c_str(),
+              result.elapsed_time.InSecondsF(),
+              result.GetTestCaseName().c_str());
+      if (result.status != TestResult::TEST_SUCCESS) {
+        // The actual failure message is not propagated up to here, as it's too
+        // much work to escape it properly, and in case of failure, almost
+        // always one needs to look into full log anyway.
+        fprintf(out_, "      <failure message=\"\" type=\"\"></failure>\n");
+      }
+      fprintf(out_, "    </testcase>\n");
+    }
+    fprintf(out_, "  </testsuite>\n");
+  }
+
+  fprintf(out_, "</testsuites>\n");
+  fclose(out_);
+}
+
+bool TestResultsTracker::Init(const CommandLine& command_line) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  // Prevent initializing twice.
+  if (out_) {
+    NOTREACHED();
+    return false;
+  }
+
+  if (!command_line.HasSwitch(kGTestOutputFlag))
+    return true;
+
+  std::string flag = command_line.GetSwitchValueASCII(kGTestOutputFlag);
+  size_t colon_pos = flag.find(':');
+  FilePath path;
+  if (colon_pos != std::string::npos) {
+    FilePath flag_path =
+        command_line.GetSwitchValuePath(kGTestOutputFlag);
+    FilePath::StringType path_string = flag_path.value();
+    path = FilePath(path_string.substr(colon_pos + 1));
+    // If the given path ends with '/', consider it is a directory.
+    // Note: This does NOT check that a directory (or file) actually exists
+    // (the behavior is same as what gtest does).
+    if (path.EndsWithSeparator()) {
+      FilePath executable = command_line.GetProgram().BaseName();
+      path = path.Append(executable.ReplaceExtension(
+                             FilePath::StringType(FILE_PATH_LITERAL("xml"))));
+    }
+  }
+  if (path.value().empty())
+    path = FilePath(kDefaultOutputFile);
+  FilePath dir_name = path.DirName();
+  if (!DirectoryExists(dir_name)) {
+    LOG(WARNING) << "The output directory does not exist. "
+                 << "Creating the directory: " << dir_name.value();
+    // Create the directory if necessary (because the gtest does the same).
+    if (!CreateDirectory(dir_name)) {
+      LOG(ERROR) << "Failed to created directory " << dir_name.value();
+      return false;
+    }
+  }
+  out_ = OpenFile(path, "w");
+  if (!out_) {
+    LOG(ERROR) << "Cannot open output file: "
+               << path.value() << ".";
+    return false;
+  }
+
+  return true;
+}
+
+void TestResultsTracker::OnTestIterationStarting() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  // Start with a fresh state for new iteration.
+  iteration_++;
+  per_iteration_data_.push_back(PerIterationData());
+}
+
+void TestResultsTracker::AddTest(const std::string& test_name) {
+  // Record disabled test names without DISABLED_ prefix so that they are easy
+  // to compare with regular test names, e.g. before or after disabling.
+  all_tests_.insert(TestNameWithoutDisabledPrefix(test_name));
+}
+
+void TestResultsTracker::AddDisabledTest(const std::string& test_name) {
+  // Record disabled test names without DISABLED_ prefix so that they are easy
+  // to compare with regular test names, e.g. before or after disabling.
+  disabled_tests_.insert(TestNameWithoutDisabledPrefix(test_name));
+}
+
+void TestResultsTracker::AddTestLocation(const std::string& test_name,
+                                         const std::string& file,
+                                         int line) {
+  test_locations_.insert(std::make_pair(test_name, CodeLocation(file, line)));
+}
+
+void TestResultsTracker::AddTestResult(const TestResult& result) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  // Record disabled test names without DISABLED_ prefix so that they are easy
+  // to compare with regular test names, e.g. before or after disabling.
+  per_iteration_data_[iteration_].results[
+      TestNameWithoutDisabledPrefix(result.full_name)].test_results.push_back(
+          result);
+}
+
+void TestResultsTracker::PrintSummaryOfCurrentIteration() const {
+  TestStatusMap tests_by_status(GetTestStatusMapForCurrentIteration());
+
+  PrintTests(tests_by_status[TestResult::TEST_FAILURE].begin(),
+             tests_by_status[TestResult::TEST_FAILURE].end(),
+             "failed");
+  PrintTests(tests_by_status[TestResult::TEST_FAILURE_ON_EXIT].begin(),
+             tests_by_status[TestResult::TEST_FAILURE_ON_EXIT].end(),
+             "failed on exit");
+  PrintTests(tests_by_status[TestResult::TEST_EXCESSIVE_OUTPUT].begin(),
+             tests_by_status[TestResult::TEST_EXCESSIVE_OUTPUT].end(),
+             "produced excessive output");
+  PrintTests(tests_by_status[TestResult::TEST_TIMEOUT].begin(),
+             tests_by_status[TestResult::TEST_TIMEOUT].end(),
+             "timed out");
+  PrintTests(tests_by_status[TestResult::TEST_CRASH].begin(),
+             tests_by_status[TestResult::TEST_CRASH].end(),
+             "crashed");
+  PrintTests(tests_by_status[TestResult::TEST_SKIPPED].begin(),
+             tests_by_status[TestResult::TEST_SKIPPED].end(),
+             "skipped");
+  PrintTests(tests_by_status[TestResult::TEST_UNKNOWN].begin(),
+             tests_by_status[TestResult::TEST_UNKNOWN].end(),
+             "had unknown result");
+}
+
+void TestResultsTracker::PrintSummaryOfAllIterations() const {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  TestStatusMap tests_by_status(GetTestStatusMapForAllIterations());
+
+  fprintf(stdout, "Summary of all test iterations:\n");
+  fflush(stdout);
+
+  PrintTests(tests_by_status[TestResult::TEST_FAILURE].begin(),
+             tests_by_status[TestResult::TEST_FAILURE].end(),
+             "failed");
+  PrintTests(tests_by_status[TestResult::TEST_FAILURE_ON_EXIT].begin(),
+             tests_by_status[TestResult::TEST_FAILURE_ON_EXIT].end(),
+             "failed on exit");
+  PrintTests(tests_by_status[TestResult::TEST_EXCESSIVE_OUTPUT].begin(),
+             tests_by_status[TestResult::TEST_EXCESSIVE_OUTPUT].end(),
+             "produced excessive output");
+  PrintTests(tests_by_status[TestResult::TEST_TIMEOUT].begin(),
+             tests_by_status[TestResult::TEST_TIMEOUT].end(),
+             "timed out");
+  PrintTests(tests_by_status[TestResult::TEST_CRASH].begin(),
+             tests_by_status[TestResult::TEST_CRASH].end(),
+             "crashed");
+  PrintTests(tests_by_status[TestResult::TEST_SKIPPED].begin(),
+             tests_by_status[TestResult::TEST_SKIPPED].end(),
+             "skipped");
+  PrintTests(tests_by_status[TestResult::TEST_UNKNOWN].begin(),
+             tests_by_status[TestResult::TEST_UNKNOWN].end(),
+             "had unknown result");
+
+  fprintf(stdout, "End of the summary.\n");
+  fflush(stdout);
+}
+
+void TestResultsTracker::AddGlobalTag(const std::string& tag) {
+  global_tags_.insert(tag);
+}
+
+bool TestResultsTracker::SaveSummaryAsJSON(
+    const FilePath& path,
+    const std::vector<std::string>& additional_tags) const {
+  std::unique_ptr<DictionaryValue> summary_root(new DictionaryValue);
+
+  std::unique_ptr<ListValue> global_tags(new ListValue);
+  for (const auto& global_tag : global_tags_) {
+    global_tags->AppendString(global_tag);
+  }
+  for (const auto& tag : additional_tags) {
+    global_tags->AppendString(tag);
+  }
+  summary_root->Set("global_tags", std::move(global_tags));
+
+  std::unique_ptr<ListValue> all_tests(new ListValue);
+  for (const auto& test : all_tests_) {
+    all_tests->AppendString(test);
+  }
+  summary_root->Set("all_tests", std::move(all_tests));
+
+  std::unique_ptr<ListValue> disabled_tests(new ListValue);
+  for (const auto& disabled_test : disabled_tests_) {
+    disabled_tests->AppendString(disabled_test);
+  }
+  summary_root->Set("disabled_tests", std::move(disabled_tests));
+
+  std::unique_ptr<ListValue> per_iteration_data(new ListValue);
+
+  for (int i = 0; i <= iteration_; i++) {
+    std::unique_ptr<DictionaryValue> current_iteration_data(
+        new DictionaryValue);
+
+    for (PerIterationData::ResultsMap::const_iterator j =
+             per_iteration_data_[i].results.begin();
+         j != per_iteration_data_[i].results.end();
+         ++j) {
+      std::unique_ptr<ListValue> test_results(new ListValue);
+
+      for (size_t k = 0; k < j->second.test_results.size(); k++) {
+        const TestResult& test_result = j->second.test_results[k];
+
+        std::unique_ptr<DictionaryValue> test_result_value(new DictionaryValue);
+
+        test_result_value->SetString("status", test_result.StatusAsString());
+        test_result_value->SetInteger(
+            "elapsed_time_ms",
+            static_cast<int>(test_result.elapsed_time.InMilliseconds()));
+
+        bool lossless_snippet = false;
+        if (IsStringUTF8(test_result.output_snippet)) {
+          test_result_value->SetString(
+              "output_snippet", test_result.output_snippet);
+          lossless_snippet = true;
+        } else {
+          test_result_value->SetString(
+              "output_snippet",
+              "<non-UTF-8 snippet, see output_snippet_base64>");
+        }
+
+        // TODO(phajdan.jr): Fix typo in JSON key (losless -> lossless)
+        // making sure not to break any consumers of this data.
+        test_result_value->SetBoolean("losless_snippet", lossless_snippet);
+
+        // Also include the raw version (base64-encoded so that it can be safely
+        // JSON-serialized - there are no guarantees about character encoding
+        // of the snippet). This can be very useful piece of information when
+        // debugging a test failure related to character encoding.
+        std::string base64_output_snippet;
+        Base64Encode(test_result.output_snippet, &base64_output_snippet);
+        test_result_value->SetString("output_snippet_base64",
+                                     base64_output_snippet);
+
+        std::unique_ptr<ListValue> test_result_parts(new ListValue);
+        for (const TestResultPart& result_part :
+             test_result.test_result_parts) {
+          std::unique_ptr<DictionaryValue> result_part_value(
+              new DictionaryValue);
+          result_part_value->SetString("type", result_part.TypeAsString());
+          result_part_value->SetString("file", result_part.file_name);
+          result_part_value->SetInteger("line", result_part.line_number);
+
+          bool lossless_summary = IsStringUTF8(result_part.summary);
+          if (lossless_summary) {
+            result_part_value->SetString("summary", result_part.summary);
+          } else {
+            result_part_value->SetString(
+                "summary", "<non-UTF-8 snippet, see summary_base64>");
+          }
+          result_part_value->SetBoolean("lossless_summary", lossless_summary);
+
+          std::string encoded_summary;
+          Base64Encode(result_part.summary, &encoded_summary);
+          result_part_value->SetString("summary_base64", encoded_summary);
+
+          bool lossless_message = IsStringUTF8(result_part.message);
+          if (lossless_message) {
+            result_part_value->SetString("message", result_part.message);
+          } else {
+            result_part_value->SetString(
+                "message", "<non-UTF-8 snippet, see message_base64>");
+          }
+          result_part_value->SetBoolean("lossless_message", lossless_message);
+
+          std::string encoded_message;
+          Base64Encode(result_part.message, &encoded_message);
+          result_part_value->SetString("message_base64", encoded_message);
+
+          test_result_parts->Append(std::move(result_part_value));
+        }
+        test_result_value->Set("result_parts", std::move(test_result_parts));
+
+        test_results->Append(std::move(test_result_value));
+      }
+
+      current_iteration_data->SetWithoutPathExpansion(j->first,
+                                                      std::move(test_results));
+    }
+    per_iteration_data->Append(std::move(current_iteration_data));
+  }
+  summary_root->Set("per_iteration_data", std::move(per_iteration_data));
+
+  std::unique_ptr<DictionaryValue> test_locations(new DictionaryValue);
+  for (const auto& item : test_locations_) {
+    std::string test_name = item.first;
+    CodeLocation location = item.second;
+    std::unique_ptr<DictionaryValue> location_value(new DictionaryValue);
+    location_value->SetString("file", location.file);
+    location_value->SetInteger("line", location.line);
+    test_locations->SetWithoutPathExpansion(test_name,
+                                            std::move(location_value));
+  }
+  summary_root->Set("test_locations", std::move(test_locations));
+
+  std::string json;
+  if (!JSONWriter::Write(*summary_root, &json))
+    return false;
+
+  File output(path, File::FLAG_CREATE_ALWAYS | File::FLAG_WRITE);
+  if (!output.IsValid())
+    return false;
+
+  int json_size = static_cast<int>(json.size());
+  if (output.WriteAtCurrentPos(json.data(), json_size) != json_size) {
+    return false;
+  }
+
+  // File::Flush() will call fsync(). This is important on Fuchsia to ensure
+  // that the file is written to the disk - the system running under qemu will
+  // shutdown shortly after the test completes. On Fuchsia fsync() times out
+  // after 15 seconds. Apparently this may not be enough in some cases,
+  // particularly when running net_unittests on buildbots, see
+  // https://crbug.com/796318. Try calling fsync() more than once to workaround
+  // this issue.
+  //
+  // TODO(sergeyu): Figure out a better solution.
+  int flush_attempts_left = 4;
+  while (flush_attempts_left-- > 0) {
+    if (output.Flush())
+      return true;
+    LOG(ERROR) << "fsync() failed when saving test output summary. "
+               << ((flush_attempts_left > 0) ? "Retrying." : " Giving up.");
+  }
+
+  return false;
+}
+
+TestResultsTracker::TestStatusMap
+    TestResultsTracker::GetTestStatusMapForCurrentIteration() const {
+  TestStatusMap tests_by_status;
+  GetTestStatusForIteration(iteration_, &tests_by_status);
+  return tests_by_status;
+}
+
+TestResultsTracker::TestStatusMap
+    TestResultsTracker::GetTestStatusMapForAllIterations() const {
+  TestStatusMap tests_by_status;
+  for (int i = 0; i <= iteration_; i++)
+    GetTestStatusForIteration(i, &tests_by_status);
+  return tests_by_status;
+}
+
+void TestResultsTracker::GetTestStatusForIteration(
+    int iteration, TestStatusMap* map) const {
+  for (PerIterationData::ResultsMap::const_iterator j =
+           per_iteration_data_[iteration].results.begin();
+       j != per_iteration_data_[iteration].results.end();
+       ++j) {
+    // Use the last test result as the final one.
+    const TestResult& result = j->second.test_results.back();
+    (*map)[result.status].insert(result.full_name);
+  }
+}
+
+// Utility function to print a list of test names. Uses iterator to be
+// compatible with different containers, like vector and set.
+template<typename InputIterator>
+void TestResultsTracker::PrintTests(InputIterator first,
+                                    InputIterator last,
+                                    const std::string& description) const {
+  size_t count = std::distance(first, last);
+  if (count == 0)
+    return;
+
+  fprintf(stdout,
+          "%" PRIuS " test%s %s:\n",
+          count,
+          count != 1 ? "s" : "",
+          description.c_str());
+  for (InputIterator it = first; it != last; ++it) {
+    const std::string& test_name = *it;
+    const auto location_it = test_locations_.find(test_name);
+    DCHECK(location_it != test_locations_.end()) << test_name;
+    const CodeLocation& location = location_it->second;
+    fprintf(stdout, "    %s (%s:%d)\n", test_name.c_str(),
+            location.file.c_str(), location.line);
+  }
+  fflush(stdout);
+}
+
+TestResultsTracker::AggregateTestResult::AggregateTestResult() = default;
+
+TestResultsTracker::AggregateTestResult::AggregateTestResult(
+    const AggregateTestResult& other) = default;
+
+TestResultsTracker::AggregateTestResult::~AggregateTestResult() = default;
+
+TestResultsTracker::PerIterationData::PerIterationData() = default;
+
+TestResultsTracker::PerIterationData::PerIterationData(
+    const PerIterationData& other) = default;
+
+TestResultsTracker::PerIterationData::~PerIterationData() = default;
+
+}  // namespace base
diff --git a/base/test/launcher/test_results_tracker.h b/base/test/launcher/test_results_tracker.h
new file mode 100644
index 0000000..d89821d
--- /dev/null
+++ b/base/test/launcher/test_results_tracker.h
@@ -0,0 +1,149 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_LAUNCHER_TEST_RESULTS_TRACKER_H_
+#define BASE_TEST_LAUNCHER_TEST_RESULTS_TRACKER_H_
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/test/launcher/test_result.h"
+#include "base/threading/thread_checker.h"
+
+namespace base {
+
+class CommandLine;
+class FilePath;
+
+// A helper class to output results.
+// Note: as currently XML is the only supported format by gtest, we don't
+// check output format (e.g. "xml:" prefix) here and output an XML file
+// unconditionally.
+// Note: we don't output per-test-case or total summary info like
+// total failed_test_count, disabled_test_count, elapsed_time and so on.
+// Only each test (testcase element in the XML) will have the correct
+// failed/disabled/elapsed_time information. Each test won't include
+// detailed failure messages either.
+class TestResultsTracker {
+ public:
+  TestResultsTracker();
+  ~TestResultsTracker();
+
+  // Initialize the result tracker. Must be called exactly once before
+  // calling any other methods. Returns true on success.
+  bool Init(const CommandLine& command_line) WARN_UNUSED_RESULT;
+
+  // Called when a test iteration is starting.
+  void OnTestIterationStarting();
+
+  // Adds |test_name| to the set of discovered tests (this includes all tests
+  // present in the executable, not necessarily run).
+  void AddTest(const std::string& test_name);
+
+  // Adds |test_name| to the set of disabled tests.
+  void AddDisabledTest(const std::string& test_name);
+
+  // Adds location for the |test_name|.
+  void AddTestLocation(const std::string& test_name,
+                       const std::string& file,
+                       int line);
+
+  // Adds |result| to the stored test results.
+  void AddTestResult(const TestResult& result);
+
+  // Prints a summary of current test iteration to stdout.
+  void PrintSummaryOfCurrentIteration() const;
+
+  // Prints a summary of all test iterations (not just the last one) to stdout.
+  void PrintSummaryOfAllIterations() const;
+
+  // Adds a string tag to the JSON summary. This is intended to indicate
+  // conditions that affect the entire test run, as opposed to individual tests.
+  void AddGlobalTag(const std::string& tag);
+
+  // Saves a JSON summary of all test iterations results to |path|. Adds
+  // |additional_tags| to the summary (just for this invocation). Returns
+  // true on success.
+  bool SaveSummaryAsJSON(
+      const FilePath& path,
+      const std::vector<std::string>& additional_tags) const WARN_UNUSED_RESULT;
+
+  // Map where keys are test result statuses, and values are sets of tests
+  // which finished with that status.
+  typedef std::map<TestResult::Status, std::set<std::string> > TestStatusMap;
+
+  // Returns a test status map (see above) for current test iteration.
+  TestStatusMap GetTestStatusMapForCurrentIteration() const;
+
+  // Returns a test status map (see above) for all test iterations.
+  TestStatusMap GetTestStatusMapForAllIterations() const;
+
+ private:
+  void GetTestStatusForIteration(int iteration, TestStatusMap* map) const;
+
+  template<typename InputIterator>
+  void PrintTests(InputIterator first,
+                  InputIterator last,
+                  const std::string& description) const;
+
+  struct AggregateTestResult {
+    AggregateTestResult();
+    AggregateTestResult(const AggregateTestResult& other);
+    ~AggregateTestResult();
+
+    std::vector<TestResult> test_results;
+  };
+
+  struct PerIterationData {
+    PerIterationData();
+    PerIterationData(const PerIterationData& other);
+    ~PerIterationData();
+
+    // Aggregate test results grouped by full test name.
+    typedef std::map<std::string, AggregateTestResult> ResultsMap;
+    ResultsMap results;
+  };
+
+  struct CodeLocation {
+    CodeLocation(const std::string& f, int l) : file(f), line(l) {
+    }
+
+    std::string file;
+    int line;
+  };
+
+  ThreadChecker thread_checker_;
+
+  // Set of global tags, i.e. strings indicating conditions that apply to
+  // the entire test run.
+  std::set<std::string> global_tags_;
+
+  // Set of all test names discovered in the current executable.
+  std::set<std::string> all_tests_;
+
+  std::map<std::string, CodeLocation> test_locations_;
+
+  // Set of all disabled tests in the current executable.
+  std::set<std::string> disabled_tests_;
+
+  // Store test results for each iteration.
+  std::vector<PerIterationData> per_iteration_data_;
+
+  // Index of current iteration (starting from 0). -1 before the first
+  // iteration.
+  int iteration_;
+
+  // File handle of output file (can be NULL if no file).
+  FILE* out_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestResultsTracker);
+};
+
+}  // namespace base
+
+#endif  // BASE_TEST_LAUNCHER_TEST_RESULTS_TRACKER_H_
diff --git a/base/test/launcher/unit_test_launcher.cc b/base/test/launcher/unit_test_launcher.cc
new file mode 100644
index 0000000..1d4439c
--- /dev/null
+++ b/base/test/launcher/unit_test_launcher.cc
@@ -0,0 +1,750 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/launcher/unit_test_launcher.h"
+
+#include <map>
+#include <memory>
+#include <utility>
+
+#include "base/base_switches.h"
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/command_line.h"
+#include "base/compiler_specific.h"
+#include "base/debug/debugger.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/format_macros.h"
+#include "base/location.h"
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "base/sequence_checker.h"
+#include "base/single_thread_task_runner.h"
+#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/sys_info.h"
+#include "base/test/gtest_xml_util.h"
+#include "base/test/launcher/test_launcher.h"
+#include "base/test/test_switches.h"
+#include "base/test/test_timeouts.h"
+#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
+#include "base/threading/thread_checker.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if defined(OS_POSIX)
+#include "base/files/file_descriptor_watcher_posix.h"
+#endif
+
+namespace base {
+
+namespace {
+
+// This constant controls how many tests are run in a single batch by default.
+const size_t kDefaultTestBatchLimit = 10;
+
+const char kHelpFlag[] = "help";
+
+// Flag to run all tests in a single process.
+const char kSingleProcessTestsFlag[] = "single-process-tests";
+
+void PrintUsage() {
+  fprintf(stdout,
+          "Runs tests using the gtest framework, each batch of tests being\n"
+          "run in their own process. Supported command-line flags:\n"
+          "\n"
+          " Common flags:\n"
+          "  --gtest_filter=...\n"
+          "    Runs a subset of tests (see --gtest_help for more info).\n"
+          "\n"
+          "  --help\n"
+          "    Shows this message.\n"
+          "\n"
+          "  --gtest_help\n"
+          "    Shows the gtest help message.\n"
+          "\n"
+          "  --test-launcher-jobs=N\n"
+          "    Sets the number of parallel test jobs to N.\n"
+          "\n"
+          "  --single-process-tests\n"
+          "    Runs the tests and the launcher in the same process. Useful\n"
+          "    for debugging a specific test in a debugger.\n"
+          "\n"
+          " Other flags:\n"
+          "  --test-launcher-filter-file=PATH\n"
+          "    Like --gtest_filter, but read the test filter from PATH.\n"
+          "    One pattern per line; lines starting with '-' are exclusions.\n"
+          "    See also //testing/buildbot/filters/README.md file.\n"
+          "\n"
+          "  --test-launcher-batch-limit=N\n"
+          "    Sets the limit of test batch to run in a single process to N.\n"
+          "\n"
+          "  --test-launcher-debug-launcher\n"
+          "    Disables autodetection of debuggers and similar tools,\n"
+          "    making it possible to use them to debug launcher itself.\n"
+          "\n"
+          "  --test-launcher-retry-limit=N\n"
+          "    Sets the limit of test retries on failures to N.\n"
+          "\n"
+          "  --test-launcher-summary-output=PATH\n"
+          "    Saves a JSON machine-readable summary of the run.\n"
+          "\n"
+          "  --test-launcher-print-test-stdio=auto|always|never\n"
+          "    Controls when full test output is printed.\n"
+          "    auto means to print it when the test failed.\n"
+          "\n"
+          "  --test-launcher-test-part-results-limit=N\n"
+          "    Sets the limit of failed EXPECT/ASSERT entries in the xml and\n"
+          "    JSON outputs per test to N (default N=10). Negative value \n"
+          "    will disable this limit.\n"
+          "\n"
+          "  --test-launcher-total-shards=N\n"
+          "    Sets the total number of shards to N.\n"
+          "\n"
+          "  --test-launcher-shard-index=N\n"
+          "    Sets the shard index to run to N (from 0 to TOTAL - 1).\n");
+  fflush(stdout);
+}
+
+class DefaultUnitTestPlatformDelegate : public UnitTestPlatformDelegate {
+ public:
+  DefaultUnitTestPlatformDelegate() = default;
+
+ private:
+  // UnitTestPlatformDelegate:
+  bool GetTests(std::vector<TestIdentifier>* output) override {
+    *output = GetCompiledInTests();
+    return true;
+  }
+
+  bool CreateResultsFile(base::FilePath* path) override {
+    if (!CreateNewTempDirectory(FilePath::StringType(), path))
+      return false;
+    *path = path->AppendASCII("test_results.xml");
+    return true;
+  }
+
+  bool CreateTemporaryFile(base::FilePath* path) override {
+    if (!temp_dir_.IsValid() && !temp_dir_.CreateUniqueTempDir())
+      return false;
+    return CreateTemporaryFileInDir(temp_dir_.GetPath(), path);
+  }
+
+  CommandLine GetCommandLineForChildGTestProcess(
+      const std::vector<std::string>& test_names,
+      const base::FilePath& output_file,
+      const base::FilePath& flag_file) override {
+    CommandLine new_cmd_line(*CommandLine::ForCurrentProcess());
+
+    CHECK(base::PathExists(flag_file));
+
+    std::string long_flags(
+        std::string("--") + kGTestFilterFlag + "=" +
+        JoinString(test_names, ":"));
+    CHECK_EQ(static_cast<int>(long_flags.size()),
+             WriteFile(flag_file, long_flags.data(),
+                       static_cast<int>(long_flags.size())));
+
+    new_cmd_line.AppendSwitchPath(switches::kTestLauncherOutput, output_file);
+    new_cmd_line.AppendSwitchPath(kGTestFlagfileFlag, flag_file);
+    new_cmd_line.AppendSwitch(kSingleProcessTestsFlag);
+
+    return new_cmd_line;
+  }
+
+  std::string GetWrapperForChildGTestProcess() override {
+    return std::string();
+  }
+
+  void RelaunchTests(TestLauncher* test_launcher,
+                     const std::vector<std::string>& test_names,
+                     int launch_flags) override {
+    // Relaunch requested tests in parallel, but only use single
+    // test per batch for more precise results (crashes, etc).
+    for (const std::string& test_name : test_names) {
+      std::vector<std::string> batch;
+      batch.push_back(test_name);
+      RunUnitTestsBatch(test_launcher, this, batch, launch_flags);
+    }
+  }
+
+  ScopedTempDir temp_dir_;
+
+  DISALLOW_COPY_AND_ASSIGN(DefaultUnitTestPlatformDelegate);
+};
+
+bool GetSwitchValueAsInt(const std::string& switch_name, int* result) {
+  if (!CommandLine::ForCurrentProcess()->HasSwitch(switch_name))
+    return true;
+
+  std::string switch_value =
+      CommandLine::ForCurrentProcess()->GetSwitchValueASCII(switch_name);
+  if (!StringToInt(switch_value, result) || *result < 0) {
+    LOG(ERROR) << "Invalid value for " << switch_name << ": " << switch_value;
+    return false;
+  }
+
+  return true;
+}
+
+int LaunchUnitTestsInternal(RunTestSuiteCallback run_test_suite,
+                            size_t parallel_jobs,
+                            int default_batch_limit,
+                            bool use_job_objects,
+                            OnceClosure gtest_init) {
+#if defined(OS_ANDROID)
+  // We can't easily fork on Android, just run the test suite directly.
+  return std::move(run_test_suite).Run();
+#else
+  bool force_single_process = false;
+  if (CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kTestLauncherDebugLauncher)) {
+    fprintf(stdout, "Forcing test launcher debugging mode.\n");
+    fflush(stdout);
+  } else {
+    if (base::debug::BeingDebugged()) {
+      fprintf(stdout,
+              "Debugger detected, switching to single process mode.\n"
+              "Pass --test-launcher-debug-launcher to debug the launcher "
+              "itself.\n");
+      fflush(stdout);
+      force_single_process = true;
+    }
+  }
+
+  if (CommandLine::ForCurrentProcess()->HasSwitch(kGTestHelpFlag) ||
+      CommandLine::ForCurrentProcess()->HasSwitch(kGTestListTestsFlag) ||
+      CommandLine::ForCurrentProcess()->HasSwitch(kSingleProcessTestsFlag) ||
+      CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kTestChildProcess) ||
+      force_single_process) {
+    return std::move(run_test_suite).Run();
+  }
+#endif
+
+  if (CommandLine::ForCurrentProcess()->HasSwitch(kHelpFlag)) {
+    PrintUsage();
+    return 0;
+  }
+
+  TimeTicks start_time(TimeTicks::Now());
+
+  std::move(gtest_init).Run();
+  TestTimeouts::Initialize();
+
+  int batch_limit = default_batch_limit;
+  if (!GetSwitchValueAsInt(switches::kTestLauncherBatchLimit, &batch_limit))
+    return 1;
+
+  fprintf(stdout,
+          "IMPORTANT DEBUGGING NOTE: batches of tests are run inside their\n"
+          "own process. For debugging a test inside a debugger, use the\n"
+          "--gtest_filter=<your_test_name> flag along with\n"
+          "--single-process-tests.\n");
+  fflush(stdout);
+
+  MessageLoopForIO message_loop;
+#if defined(OS_POSIX)
+  FileDescriptorWatcher file_descriptor_watcher(&message_loop);
+#endif
+
+  DefaultUnitTestPlatformDelegate platform_delegate;
+  UnitTestLauncherDelegate delegate(
+      &platform_delegate, batch_limit, use_job_objects);
+  TestLauncher launcher(&delegate, parallel_jobs);
+  bool success = launcher.Run();
+
+  fprintf(stdout, "Tests took %" PRId64 " seconds.\n",
+          (TimeTicks::Now() - start_time).InSeconds());
+  fflush(stdout);
+
+  return (success ? 0 : 1);
+}
+
+void InitGoogleTestChar(int* argc, char** argv) {
+  testing::InitGoogleTest(argc, argv);
+}
+
+#if defined(OS_WIN)
+void InitGoogleTestWChar(int* argc, wchar_t** argv) {
+  testing::InitGoogleTest(argc, argv);
+}
+#endif  // defined(OS_WIN)
+
+// Interprets test results and reports to the test launcher. Returns true
+// on success.
+bool ProcessTestResults(
+    TestLauncher* test_launcher,
+    const std::vector<std::string>& test_names,
+    const base::FilePath& output_file,
+    const std::string& output,
+    int exit_code,
+    bool was_timeout,
+    std::vector<std::string>* tests_to_relaunch) {
+  std::vector<TestResult> test_results;
+  bool crashed = false;
+  bool have_test_results =
+      ProcessGTestOutput(output_file, &test_results, &crashed);
+
+  bool called_any_callback = false;
+
+  if (have_test_results) {
+    // TODO(phajdan.jr): Check for duplicates and mismatches between
+    // the results we got from XML file and tests we intended to run.
+    std::map<std::string, TestResult> results_map;
+    for (size_t i = 0; i < test_results.size(); i++)
+      results_map[test_results[i].full_name] = test_results[i];
+
+    bool had_interrupted_test = false;
+
+    // Results to be reported back to the test launcher.
+    std::vector<TestResult> final_results;
+
+    for (size_t i = 0; i < test_names.size(); i++) {
+      if (ContainsKey(results_map, test_names[i])) {
+        TestResult test_result = results_map[test_names[i]];
+        if (test_result.status == TestResult::TEST_CRASH) {
+          had_interrupted_test = true;
+
+          if (was_timeout) {
+            // Fix up the test status: we forcibly kill the child process
+            // after the timeout, so from XML results it looks just like
+            // a crash.
+            test_result.status = TestResult::TEST_TIMEOUT;
+          }
+        } else if (test_result.status == TestResult::TEST_SUCCESS ||
+                   test_result.status == TestResult::TEST_FAILURE) {
+          // We run multiple tests in a batch with a timeout applied
+          // to the entire batch. It is possible that with other tests
+          // running quickly some tests take longer than the per-test timeout.
+          // For consistent handling of tests independent of order and other
+          // factors, mark them as timing out.
+          if (test_result.elapsed_time >
+              TestTimeouts::test_launcher_timeout()) {
+            test_result.status = TestResult::TEST_TIMEOUT;
+          }
+        }
+        test_result.output_snippet = GetTestOutputSnippet(test_result, output);
+        final_results.push_back(test_result);
+      } else if (had_interrupted_test) {
+        tests_to_relaunch->push_back(test_names[i]);
+      } else {
+        // TODO(phajdan.jr): Explicitly pass the info that the test didn't
+        // run for a mysterious reason.
+        LOG(ERROR) << "no test result for " << test_names[i];
+        TestResult test_result;
+        test_result.full_name = test_names[i];
+        test_result.status = TestResult::TEST_UNKNOWN;
+        test_result.output_snippet = GetTestOutputSnippet(test_result, output);
+        final_results.push_back(test_result);
+      }
+    }
+
+    // TODO(phajdan.jr): Handle the case where processing XML output
+    // indicates a crash but none of the test results is marked as crashing.
+
+    if (final_results.empty())
+      return false;
+
+    bool has_non_success_test = false;
+    for (size_t i = 0; i < final_results.size(); i++) {
+      if (final_results[i].status != TestResult::TEST_SUCCESS) {
+        has_non_success_test = true;
+        break;
+      }
+    }
+
+    if (!has_non_success_test && exit_code != 0) {
+      // This is a bit surprising case: all tests are marked as successful,
+      // but the exit code was not zero. This can happen e.g. under memory
+      // tools that report leaks this way. Mark all tests as a failure on exit,
+      // and for more precise info they'd need to be retried serially.
+      for (size_t i = 0; i < final_results.size(); i++)
+        final_results[i].status = TestResult::TEST_FAILURE_ON_EXIT;
+    }
+
+    for (size_t i = 0; i < final_results.size(); i++) {
+      // Fix the output snippet after possible changes to the test result.
+      final_results[i].output_snippet =
+          GetTestOutputSnippet(final_results[i], output);
+      test_launcher->OnTestFinished(final_results[i]);
+      called_any_callback = true;
+    }
+  } else {
+    fprintf(stdout,
+            "Failed to get out-of-band test success data, "
+            "dumping full stdio below:\n%s\n",
+            output.c_str());
+    fflush(stdout);
+
+    // We do not have reliable details about test results (parsing test
+    // stdout is known to be unreliable).
+    if (test_names.size() == 1) {
+      // There is only one test. Try to determine status by exit code.
+      const std::string& test_name = test_names.front();
+      TestResult test_result;
+      test_result.full_name = test_name;
+
+      if (was_timeout) {
+        test_result.status = TestResult::TEST_TIMEOUT;
+      } else if (exit_code != 0) {
+        test_result.status = TestResult::TEST_FAILURE;
+      } else {
+        // It's strange case when test executed successfully,
+        // but we failed to read machine-readable report for it.
+        test_result.status = TestResult::TEST_UNKNOWN;
+      }
+
+      test_launcher->OnTestFinished(test_result);
+      called_any_callback = true;
+    } else {
+      // There is more than one test. Retry them individually.
+      for (const std::string& test_name : test_names)
+        tests_to_relaunch->push_back(test_name);
+    }
+  }
+
+  return called_any_callback;
+}
+
+class UnitTestProcessLifetimeObserver : public ProcessLifetimeObserver {
+ public:
+  ~UnitTestProcessLifetimeObserver() override {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  }
+
+  TestLauncher* test_launcher() { return test_launcher_; }
+  UnitTestPlatformDelegate* platform_delegate() { return platform_delegate_; }
+  const std::vector<std::string>& test_names() { return test_names_; }
+  int launch_flags() { return launch_flags_; }
+  const FilePath& output_file() { return output_file_; }
+  const FilePath& flag_file() { return flag_file_; }
+
+ protected:
+  UnitTestProcessLifetimeObserver(TestLauncher* test_launcher,
+                                  UnitTestPlatformDelegate* platform_delegate,
+                                  const std::vector<std::string>& test_names,
+                                  int launch_flags,
+                                  const FilePath& output_file,
+                                  const FilePath& flag_file)
+      : ProcessLifetimeObserver(),
+        test_launcher_(test_launcher),
+        platform_delegate_(platform_delegate),
+        test_names_(test_names),
+        launch_flags_(launch_flags),
+        output_file_(output_file),
+        flag_file_(flag_file) {}
+
+  SEQUENCE_CHECKER(sequence_checker_);
+
+ private:
+  TestLauncher* const test_launcher_;
+  UnitTestPlatformDelegate* const platform_delegate_;
+  const std::vector<std::string> test_names_;
+  const int launch_flags_;
+  const FilePath output_file_;
+  const FilePath flag_file_;
+
+  DISALLOW_COPY_AND_ASSIGN(UnitTestProcessLifetimeObserver);
+};
+
+class ParallelUnitTestProcessLifetimeObserver
+    : public UnitTestProcessLifetimeObserver {
+ public:
+  ParallelUnitTestProcessLifetimeObserver(
+      TestLauncher* test_launcher,
+      UnitTestPlatformDelegate* platform_delegate,
+      const std::vector<std::string>& test_names,
+      int launch_flags,
+      const FilePath& output_file,
+      const FilePath& flag_file)
+      : UnitTestProcessLifetimeObserver(test_launcher,
+                                        platform_delegate,
+                                        test_names,
+                                        launch_flags,
+                                        output_file,
+                                        flag_file) {}
+  ~ParallelUnitTestProcessLifetimeObserver() override = default;
+
+ private:
+  // ProcessLifetimeObserver:
+  void OnCompleted(int exit_code,
+                   TimeDelta elapsed_time,
+                   bool was_timeout,
+                   const std::string& output) override;
+
+  DISALLOW_COPY_AND_ASSIGN(ParallelUnitTestProcessLifetimeObserver);
+};
+
+void ParallelUnitTestProcessLifetimeObserver::OnCompleted(
+    int exit_code,
+    TimeDelta elapsed_time,
+    bool was_timeout,
+    const std::string& output) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  std::vector<std::string> tests_to_relaunch;
+  ProcessTestResults(test_launcher(), test_names(), output_file(), output,
+                     exit_code, was_timeout, &tests_to_relaunch);
+
+  if (!tests_to_relaunch.empty()) {
+    platform_delegate()->RelaunchTests(test_launcher(), tests_to_relaunch,
+                                       launch_flags());
+  }
+
+  // The temporary file's directory is also temporary.
+  DeleteFile(output_file().DirName(), true);
+  if (!flag_file().empty())
+    DeleteFile(flag_file(), false);
+}
+
+class SerialUnitTestProcessLifetimeObserver
+    : public UnitTestProcessLifetimeObserver {
+ public:
+  SerialUnitTestProcessLifetimeObserver(
+      TestLauncher* test_launcher,
+      UnitTestPlatformDelegate* platform_delegate,
+      const std::vector<std::string>& test_names,
+      int launch_flags,
+      const FilePath& output_file,
+      const FilePath& flag_file,
+      std::vector<std::string>&& next_test_names)
+      : UnitTestProcessLifetimeObserver(test_launcher,
+                                        platform_delegate,
+                                        test_names,
+                                        launch_flags,
+                                        output_file,
+                                        flag_file),
+        next_test_names_(std::move(next_test_names)) {}
+  ~SerialUnitTestProcessLifetimeObserver() override = default;
+
+ private:
+  // ProcessLifetimeObserver:
+  void OnCompleted(int exit_code,
+                   TimeDelta elapsed_time,
+                   bool was_timeout,
+                   const std::string& output) override;
+
+  std::vector<std::string> next_test_names_;
+
+  DISALLOW_COPY_AND_ASSIGN(SerialUnitTestProcessLifetimeObserver);
+};
+
+void SerialUnitTestProcessLifetimeObserver::OnCompleted(
+    int exit_code,
+    TimeDelta elapsed_time,
+    bool was_timeout,
+    const std::string& output) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  std::vector<std::string> tests_to_relaunch;
+  bool called_any_callbacks =
+      ProcessTestResults(test_launcher(), test_names(), output_file(), output,
+                         exit_code, was_timeout, &tests_to_relaunch);
+
+  // There is only one test, there cannot be other tests to relaunch
+  // due to a crash.
+  DCHECK(tests_to_relaunch.empty());
+
+  // There is only one test, we should have called back with its result.
+  DCHECK(called_any_callbacks);
+
+  // The temporary file's directory is also temporary.
+  DeleteFile(output_file().DirName(), true);
+
+  if (!flag_file().empty())
+    DeleteFile(flag_file(), false);
+
+  ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE,
+      BindOnce(&RunUnitTestsSerially, test_launcher(), platform_delegate(),
+               std::move(next_test_names_), launch_flags()));
+}
+
+}  // namespace
+
+int LaunchUnitTests(int argc,
+                    char** argv,
+                    RunTestSuiteCallback run_test_suite) {
+  CommandLine::Init(argc, argv);
+  size_t parallel_jobs = NumParallelJobs();
+  if (parallel_jobs == 0U) {
+    return 1;
+  }
+  return LaunchUnitTestsInternal(std::move(run_test_suite), parallel_jobs,
+                                 kDefaultTestBatchLimit, true,
+                                 BindOnce(&InitGoogleTestChar, &argc, argv));
+}
+
+int LaunchUnitTestsSerially(int argc,
+                            char** argv,
+                            RunTestSuiteCallback run_test_suite) {
+  CommandLine::Init(argc, argv);
+  return LaunchUnitTestsInternal(std::move(run_test_suite), 1U,
+                                 kDefaultTestBatchLimit, true,
+                                 BindOnce(&InitGoogleTestChar, &argc, argv));
+}
+
+int LaunchUnitTestsWithOptions(int argc,
+                               char** argv,
+                               size_t parallel_jobs,
+                               int default_batch_limit,
+                               bool use_job_objects,
+                               RunTestSuiteCallback run_test_suite) {
+  CommandLine::Init(argc, argv);
+  return LaunchUnitTestsInternal(std::move(run_test_suite), parallel_jobs,
+                                 default_batch_limit, use_job_objects,
+                                 BindOnce(&InitGoogleTestChar, &argc, argv));
+}
+
+#if defined(OS_WIN)
+int LaunchUnitTests(int argc,
+                    wchar_t** argv,
+                    bool use_job_objects,
+                    RunTestSuiteCallback run_test_suite) {
+  // Windows CommandLine::Init ignores argv anyway.
+  CommandLine::Init(argc, NULL);
+  size_t parallel_jobs = NumParallelJobs();
+  if (parallel_jobs == 0U) {
+    return 1;
+  }
+  return LaunchUnitTestsInternal(std::move(run_test_suite), parallel_jobs,
+                                 kDefaultTestBatchLimit, use_job_objects,
+                                 BindOnce(&InitGoogleTestWChar, &argc, argv));
+}
+#endif  // defined(OS_WIN)
+
+void RunUnitTestsSerially(
+    TestLauncher* test_launcher,
+    UnitTestPlatformDelegate* platform_delegate,
+    const std::vector<std::string>& test_names,
+    int launch_flags) {
+  if (test_names.empty())
+    return;
+
+  // Create a dedicated temporary directory to store the xml result data
+  // per run to ensure clean state and make it possible to launch multiple
+  // processes in parallel.
+  FilePath output_file;
+  CHECK(platform_delegate->CreateResultsFile(&output_file));
+  FilePath flag_file;
+  platform_delegate->CreateTemporaryFile(&flag_file);
+
+  auto observer = std::make_unique<SerialUnitTestProcessLifetimeObserver>(
+      test_launcher, platform_delegate,
+      std::vector<std::string>(1, test_names.back()), launch_flags, output_file,
+      flag_file,
+      std::vector<std::string>(test_names.begin(), test_names.end() - 1));
+
+  CommandLine cmd_line(platform_delegate->GetCommandLineForChildGTestProcess(
+      observer->test_names(), output_file, flag_file));
+
+  TestLauncher::LaunchOptions launch_options;
+  launch_options.flags = launch_flags;
+  test_launcher->LaunchChildGTestProcess(
+      cmd_line, platform_delegate->GetWrapperForChildGTestProcess(),
+      TestTimeouts::test_launcher_timeout(), launch_options,
+      std::move(observer));
+}
+
+void RunUnitTestsBatch(
+    TestLauncher* test_launcher,
+    UnitTestPlatformDelegate* platform_delegate,
+    const std::vector<std::string>& test_names,
+    int launch_flags) {
+  if (test_names.empty())
+    return;
+
+  // Create a dedicated temporary directory to store the xml result data
+  // per run to ensure clean state and make it possible to launch multiple
+  // processes in parallel.
+  FilePath output_file;
+  CHECK(platform_delegate->CreateResultsFile(&output_file));
+  FilePath flag_file;
+  platform_delegate->CreateTemporaryFile(&flag_file);
+
+  auto observer = std::make_unique<ParallelUnitTestProcessLifetimeObserver>(
+      test_launcher, platform_delegate, test_names, launch_flags, output_file,
+      flag_file);
+
+  CommandLine cmd_line(platform_delegate->GetCommandLineForChildGTestProcess(
+      test_names, output_file, flag_file));
+
+  // Adjust the timeout depending on how many tests we're running
+  // (note that e.g. the last batch of tests will be smaller).
+  // TODO(phajdan.jr): Consider an adaptive timeout, which can change
+  // depending on how many tests ran and how many remain.
+  // Note: do NOT parse child's stdout to do that, it's known to be
+  // unreliable (e.g. buffering issues can mix up the output).
+  TimeDelta timeout = test_names.size() * TestTimeouts::test_launcher_timeout();
+
+  TestLauncher::LaunchOptions options;
+  options.flags = launch_flags;
+  test_launcher->LaunchChildGTestProcess(
+      cmd_line, platform_delegate->GetWrapperForChildGTestProcess(), timeout,
+      options, std::move(observer));
+}
+
+UnitTestLauncherDelegate::UnitTestLauncherDelegate(
+    UnitTestPlatformDelegate* platform_delegate,
+    size_t batch_limit,
+    bool use_job_objects)
+    : platform_delegate_(platform_delegate),
+      batch_limit_(batch_limit),
+      use_job_objects_(use_job_objects) {
+}
+
+UnitTestLauncherDelegate::~UnitTestLauncherDelegate() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+}
+
+bool UnitTestLauncherDelegate::GetTests(std::vector<TestIdentifier>* output) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  return platform_delegate_->GetTests(output);
+}
+
+bool UnitTestLauncherDelegate::ShouldRunTest(const std::string& test_case_name,
+                                             const std::string& test_name) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  // There is no additional logic to disable specific tests.
+  return true;
+}
+
+size_t UnitTestLauncherDelegate::RunTests(
+    TestLauncher* test_launcher,
+    const std::vector<std::string>& test_names) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  int launch_flags = use_job_objects_ ? TestLauncher::USE_JOB_OBJECTS : 0;
+
+  std::vector<std::string> batch;
+  for (size_t i = 0; i < test_names.size(); i++) {
+    batch.push_back(test_names[i]);
+
+    // Use 0 to indicate unlimited batch size.
+    if (batch.size() >= batch_limit_ && batch_limit_ != 0) {
+      RunUnitTestsBatch(test_launcher, platform_delegate_, batch, launch_flags);
+      batch.clear();
+    }
+  }
+
+  RunUnitTestsBatch(test_launcher, platform_delegate_, batch, launch_flags);
+
+  return test_names.size();
+}
+
+size_t UnitTestLauncherDelegate::RetryTests(
+    TestLauncher* test_launcher,
+    const std::vector<std::string>& test_names) {
+  ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE,
+      BindOnce(&RunUnitTestsSerially, test_launcher, platform_delegate_,
+               test_names,
+               use_job_objects_ ? TestLauncher::USE_JOB_OBJECTS : 0));
+  return test_names.size();
+}
+
+}  // namespace base
diff --git a/base/test/launcher/unit_test_launcher.h b/base/test/launcher/unit_test_launcher.h
new file mode 100644
index 0000000..0d1c21e
--- /dev/null
+++ b/base/test/launcher/unit_test_launcher.h
@@ -0,0 +1,134 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_LAUNCHER_UNIT_TEST_LAUNCHER_H_
+#define BASE_TEST_LAUNCHER_UNIT_TEST_LAUNCHER_H_
+
+#include <stddef.h>
+
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "base/test/launcher/test_launcher.h"
+#include "build/build_config.h"
+
+namespace base {
+
+// Callback that runs a test suite and returns exit code.
+using RunTestSuiteCallback = OnceCallback<int(void)>;
+
+// Launches unit tests in given test suite. Returns exit code.
+int LaunchUnitTests(int argc, char** argv, RunTestSuiteCallback run_test_suite);
+
+// Same as above, but always runs tests serially.
+int LaunchUnitTestsSerially(int argc,
+                            char** argv,
+                            RunTestSuiteCallback run_test_suite);
+
+// Launches unit tests in given test suite. Returns exit code.
+// |parallel_jobs| is the number of parallel test jobs.
+// |default_batch_limit| is the default size of test batch
+// (use 0 to disable batching).
+// |use_job_objects| determines whether to use job objects.
+int LaunchUnitTestsWithOptions(int argc,
+                               char** argv,
+                               size_t parallel_jobs,
+                               int default_batch_limit,
+                               bool use_job_objects,
+                               RunTestSuiteCallback run_test_suite);
+
+#if defined(OS_WIN)
+// Launches unit tests in given test suite. Returns exit code.
+// |use_job_objects| determines whether to use job objects.
+int LaunchUnitTests(int argc,
+                    wchar_t** argv,
+                    bool use_job_objects,
+                    RunTestSuiteCallback run_test_suite);
+#endif  // defined(OS_WIN)
+
+// Delegate to abstract away platform differences for unit tests.
+class UnitTestPlatformDelegate {
+ public:
+  // Called to get names of tests available for running. The delegate
+  // must put the result in |output| and return true on success.
+  virtual bool GetTests(std::vector<TestIdentifier>* output) = 0;
+
+  // Called to create a temporary for storing test results. The delegate
+  // must put the resulting path in |path| and return true on success.
+  virtual bool CreateResultsFile(base::FilePath* path) = 0;
+
+  // Called to create a new temporary file. The delegate must put the resulting
+  // path in |path| and return true on success.
+  virtual bool CreateTemporaryFile(base::FilePath* path) = 0;
+
+  // Returns command line for child GTest process based on the command line
+  // of current process. |test_names| is a vector of test full names
+  // (e.g. "A.B"), |output_file| is path to the GTest XML output file.
+  virtual CommandLine GetCommandLineForChildGTestProcess(
+      const std::vector<std::string>& test_names,
+      const base::FilePath& output_file,
+      const base::FilePath& flag_file) = 0;
+
+  // Returns wrapper to use for child GTest process. Empty string means
+  // no wrapper.
+  virtual std::string GetWrapperForChildGTestProcess() = 0;
+
+  // Relaunch tests, e.g. after a crash.
+  virtual void RelaunchTests(TestLauncher* test_launcher,
+                             const std::vector<std::string>& test_names,
+                             int launch_flags) = 0;
+
+ protected:
+  ~UnitTestPlatformDelegate() = default;
+};
+
+// Runs tests serially, each in its own process.
+void RunUnitTestsSerially(TestLauncher* test_launcher,
+                          UnitTestPlatformDelegate* platform_delegate,
+                          const std::vector<std::string>& test_names,
+                          int launch_flags);
+
+// Runs tests in batches (each batch in its own process).
+void RunUnitTestsBatch(TestLauncher* test_launcher,
+                       UnitTestPlatformDelegate* platform_delegate,
+                       const std::vector<std::string>& test_names,
+                       int launch_flags);
+
+// Test launcher delegate for unit tests (mostly to support batching).
+class UnitTestLauncherDelegate : public TestLauncherDelegate {
+ public:
+  UnitTestLauncherDelegate(UnitTestPlatformDelegate* delegate,
+                           size_t batch_limit,
+                           bool use_job_objects);
+  ~UnitTestLauncherDelegate() override;
+
+ private:
+  // TestLauncherDelegate:
+  bool GetTests(std::vector<TestIdentifier>* output) override;
+  bool ShouldRunTest(const std::string& test_case_name,
+                     const std::string& test_name) override;
+  size_t RunTests(TestLauncher* test_launcher,
+                  const std::vector<std::string>& test_names) override;
+  size_t RetryTests(TestLauncher* test_launcher,
+                    const std::vector<std::string>& test_names) override;
+
+  ThreadChecker thread_checker_;
+
+  UnitTestPlatformDelegate* platform_delegate_;
+
+  // Maximum number of tests to run in a single batch.
+  size_t batch_limit_;
+
+  // Determines whether we use job objects on Windows.
+  bool use_job_objects_;
+
+  DISALLOW_COPY_AND_ASSIGN(UnitTestLauncherDelegate);
+};
+
+}   // namespace base
+
+#endif  // BASE_TEST_LAUNCHER_UNIT_TEST_LAUNCHER_H_
diff --git a/base/test/malloc_wrapper.cc b/base/test/malloc_wrapper.cc
new file mode 100644
index 0000000..eb280a3
--- /dev/null
+++ b/base/test/malloc_wrapper.cc
@@ -0,0 +1,11 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "malloc_wrapper.h"
+
+#include <stdlib.h>
+
+void* MallocWrapper(size_t size) {
+  return malloc(size);
+}
diff --git a/base/test/malloc_wrapper.h b/base/test/malloc_wrapper.h
new file mode 100644
index 0000000..d06228d
--- /dev/null
+++ b/base/test/malloc_wrapper.h
@@ -0,0 +1,21 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_MALLOC_WRAPPER_H_
+#define BASE_TEST_MALLOC_WRAPPER_H_
+
+#include <stddef.h>
+
+// BASE_EXPORT depends on COMPONENT_BUILD.
+// This will always be a separate shared library, so don't use BASE_EXPORT here.
+#if defined(WIN32)
+#define MALLOC_WRAPPER_EXPORT __declspec(dllexport)
+#else
+#define MALLOC_WRAPPER_EXPORT __attribute__((visibility("default")))
+#endif  // defined(WIN32)
+
+// Calls malloc directly.
+MALLOC_WRAPPER_EXPORT void* MallocWrapper(size_t size);
+
+#endif  // BASE_TEST_MALLOC_WRAPPER_H_
diff --git a/base/test/metrics/histogram_tester_unittest.cc b/base/test/metrics/histogram_tester_unittest.cc
new file mode 100644
index 0000000..e9b9f20
--- /dev/null
+++ b/base/test/metrics/histogram_tester_unittest.cc
@@ -0,0 +1,157 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/metrics/histogram_tester.h"
+
+#include <memory>
+#include <string>
+
+#include "base/metrics/histogram_macros.h"
+#include "base/metrics/histogram_samples.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::ElementsAre;
+using ::testing::IsEmpty;
+
+namespace {
+
+const char kHistogram1[] = "Test1";
+const char kHistogram2[] = "Test2";
+const char kHistogram3[] = "Test3";
+const char kHistogram4[] = "Test4";
+const char kHistogram5[] = "Test5";
+
+}  // namespace
+
+namespace base {
+
+typedef testing::Test HistogramTesterTest;
+
+TEST_F(HistogramTesterTest, Scope) {
+  // Record a histogram before the creation of the recorder.
+  UMA_HISTOGRAM_BOOLEAN(kHistogram1, true);
+
+  HistogramTester tester;
+
+  // Verify that no histogram is recorded.
+  tester.ExpectTotalCount(kHistogram1, 0);
+
+  // Record a histogram after the creation of the recorder.
+  UMA_HISTOGRAM_BOOLEAN(kHistogram1, true);
+
+  // Verify that one histogram is recorded.
+  std::unique_ptr<HistogramSamples> samples(
+      tester.GetHistogramSamplesSinceCreation(kHistogram1));
+  EXPECT_TRUE(samples);
+  EXPECT_EQ(1, samples->TotalCount());
+}
+
+TEST_F(HistogramTesterTest, GetHistogramSamplesSinceCreationNotNull) {
+  // Chose the histogram name uniquely, to ensure nothing was recorded for it so
+  // far.
+  static const char kHistogram[] =
+      "GetHistogramSamplesSinceCreationNotNullHistogram";
+  HistogramTester tester;
+
+  // Verify that the returned samples are empty but not null.
+  std::unique_ptr<HistogramSamples> samples(
+      tester.GetHistogramSamplesSinceCreation(kHistogram1));
+  EXPECT_TRUE(samples);
+  tester.ExpectTotalCount(kHistogram, 0);
+}
+
+TEST_F(HistogramTesterTest, TestUniqueSample) {
+  HistogramTester tester;
+
+  // Record into a sample thrice
+  UMA_HISTOGRAM_COUNTS_100(kHistogram2, 2);
+  UMA_HISTOGRAM_COUNTS_100(kHistogram2, 2);
+  UMA_HISTOGRAM_COUNTS_100(kHistogram2, 2);
+
+  tester.ExpectUniqueSample(kHistogram2, 2, 3);
+}
+
+TEST_F(HistogramTesterTest, TestBucketsSample) {
+  HistogramTester tester;
+
+  // Record into a sample twice
+  UMA_HISTOGRAM_COUNTS_100(kHistogram3, 2);
+  UMA_HISTOGRAM_COUNTS_100(kHistogram3, 2);
+  UMA_HISTOGRAM_COUNTS_100(kHistogram3, 2);
+  UMA_HISTOGRAM_COUNTS_100(kHistogram3, 2);
+  UMA_HISTOGRAM_COUNTS_100(kHistogram3, 3);
+
+  tester.ExpectBucketCount(kHistogram3, 2, 4);
+  tester.ExpectBucketCount(kHistogram3, 3, 1);
+
+  tester.ExpectTotalCount(kHistogram3, 5);
+}
+
+TEST_F(HistogramTesterTest, TestBucketsSampleWithScope) {
+  // Record into a sample twice, once before the tester creation and once after.
+  UMA_HISTOGRAM_COUNTS_100(kHistogram4, 2);
+
+  HistogramTester tester;
+  UMA_HISTOGRAM_COUNTS_100(kHistogram4, 3);
+
+  tester.ExpectBucketCount(kHistogram4, 2, 0);
+  tester.ExpectBucketCount(kHistogram4, 3, 1);
+
+  tester.ExpectTotalCount(kHistogram4, 1);
+}
+
+TEST_F(HistogramTesterTest, TestGetAllSamples) {
+  HistogramTester tester;
+  UMA_HISTOGRAM_ENUMERATION(kHistogram5, 2, 5);
+  UMA_HISTOGRAM_ENUMERATION(kHistogram5, 3, 5);
+  UMA_HISTOGRAM_ENUMERATION(kHistogram5, 3, 5);
+  UMA_HISTOGRAM_ENUMERATION(kHistogram5, 5, 5);
+
+  EXPECT_THAT(tester.GetAllSamples(kHistogram5),
+              ElementsAre(Bucket(2, 1), Bucket(3, 2), Bucket(5, 1)));
+}
+
+TEST_F(HistogramTesterTest, TestGetAllSamples_NoSamples) {
+  HistogramTester tester;
+  EXPECT_THAT(tester.GetAllSamples(kHistogram5), IsEmpty());
+}
+
+TEST_F(HistogramTesterTest, TestGetTotalCountsForPrefix) {
+  HistogramTester tester;
+  UMA_HISTOGRAM_ENUMERATION("Test1.Test2.Test3", 2, 5);
+
+  // Regression check for bug https://crbug.com/659977.
+  EXPECT_TRUE(tester.GetTotalCountsForPrefix("Test2.").empty());
+
+  EXPECT_EQ(1u, tester.GetTotalCountsForPrefix("Test1.").size());
+}
+
+TEST_F(HistogramTesterTest, TestGetAllChangedHistograms) {
+  // Record into a sample twice, once before the tester creation.
+  UMA_HISTOGRAM_COUNTS_100(kHistogram1, true);
+  UMA_HISTOGRAM_COUNTS_100(kHistogram4, 4);
+
+  HistogramTester tester;
+  UMA_HISTOGRAM_COUNTS_100(kHistogram4, 3);
+
+  UMA_HISTOGRAM_ENUMERATION(kHistogram5, 2, 5);
+  UMA_HISTOGRAM_ENUMERATION(kHistogram5, 3, 5);
+  UMA_HISTOGRAM_ENUMERATION(kHistogram5, 4, 5);
+  UMA_HISTOGRAM_ENUMERATION(kHistogram5, 5, 5);
+
+  UMA_HISTOGRAM_ENUMERATION("Test1.Test2.Test3", 2, 5);
+  std::string results = tester.GetAllHistogramsRecorded();
+
+  EXPECT_EQ(std::string::npos, results.find("Histogram: Test1 recorded"));
+  EXPECT_NE(std::string::npos,
+            results.find("Histogram: Test4 recorded 1 new samples"));
+  EXPECT_NE(std::string::npos,
+            results.find("Histogram: Test5 recorded 4 new samples"));
+  EXPECT_NE(
+      std::string::npos,
+      results.find("Histogram: Test1.Test2.Test3 recorded 1 new samples"));
+}
+
+}  // namespace base
diff --git a/base/test/metrics/user_action_tester.cc b/base/test/metrics/user_action_tester.cc
new file mode 100644
index 0000000..a845bca
--- /dev/null
+++ b/base/test/metrics/user_action_tester.cc
@@ -0,0 +1,38 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/metrics/user_action_tester.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/test/test_simple_task_runner.h"
+
+namespace base {
+
+UserActionTester::UserActionTester()
+    : task_runner_(new base::TestSimpleTaskRunner),
+      action_callback_(
+          base::Bind(&UserActionTester::OnUserAction, base::Unretained(this))) {
+  base::SetRecordActionTaskRunner(task_runner_);
+  base::AddActionCallback(action_callback_);
+}
+
+UserActionTester::~UserActionTester() {
+  base::RemoveActionCallback(action_callback_);
+}
+
+int UserActionTester::GetActionCount(const std::string& user_action) const {
+  UserActionCountMap::const_iterator iter = count_map_.find(user_action);
+  return iter == count_map_.end() ? 0 : iter->second;
+}
+
+void UserActionTester::ResetCounts() {
+  count_map_.clear();
+}
+
+void UserActionTester::OnUserAction(const std::string& user_action) {
+  ++(count_map_[user_action]);
+}
+
+}  // namespace base
diff --git a/base/test/metrics/user_action_tester.h b/base/test/metrics/user_action_tester.h
new file mode 100644
index 0000000..a422c9a
--- /dev/null
+++ b/base/test/metrics/user_action_tester.h
@@ -0,0 +1,50 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_METRICS_USER_ACTION_TESTER_H_
+#define BASE_TEST_METRICS_USER_ACTION_TESTER_H_
+
+#include <map>
+#include <string>
+
+#include "base/macros.h"
+#include "base/metrics/user_metrics.h"
+
+namespace base {
+
+// This class observes and collects user action notifications that are sent
+// by the tests, so that they can be examined afterwards for correctness.
+// Note: This class is NOT thread-safe.
+class UserActionTester {
+ public:
+  UserActionTester();
+  ~UserActionTester();
+
+  // Returns the number of times the given |user_action| occurred.
+  int GetActionCount(const std::string& user_action) const;
+
+  // Resets all user action counts to 0.
+  void ResetCounts();
+
+ private:
+  typedef std::map<std::string, int> UserActionCountMap;
+
+  // The callback that is notified when a user actions occurs.
+  void OnUserAction(const std::string& user_action);
+
+  // A map that tracks the number of times a user action has occurred.
+  UserActionCountMap count_map_;
+
+  // A test task runner used by user metrics.
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+
+  // The callback that is added to the global action callback list.
+  base::ActionCallback action_callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(UserActionTester);
+};
+
+}  // namespace base
+
+#endif  // BASE_TEST_METRICS_USER_ACTION_TESTER_H_
diff --git a/base/test/metrics/user_action_tester_unittest.cc b/base/test/metrics/user_action_tester_unittest.cc
new file mode 100644
index 0000000..14557d2
--- /dev/null
+++ b/base/test/metrics/user_action_tester_unittest.cc
@@ -0,0 +1,86 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/metrics/user_action_tester.h"
+
+#include "base/metrics/user_metrics.h"
+#include "base/metrics/user_metrics_action.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+namespace {
+
+const char kUserAction1[] = "user.action.1";
+const char kUserAction2[] = "user.action.2";
+const char kUserAction3[] = "user.action.3";
+
+// Record an action and cause all ActionCallback observers to be notified.
+void RecordAction(const char user_action[]) {
+  base::RecordAction(base::UserMetricsAction(user_action));
+}
+
+}  // namespace
+
+// Verify user action counts are zero initially.
+TEST(UserActionTesterTest, GetActionCountWhenNoActionsHaveBeenRecorded) {
+  UserActionTester user_action_tester;
+  EXPECT_EQ(0, user_action_tester.GetActionCount(kUserAction1));
+}
+
+// Verify user action counts are tracked properly.
+TEST(UserActionTesterTest, GetActionCountWhenActionsHaveBeenRecorded) {
+  UserActionTester user_action_tester;
+
+  RecordAction(kUserAction1);
+  RecordAction(kUserAction2);
+  RecordAction(kUserAction2);
+
+  EXPECT_EQ(1, user_action_tester.GetActionCount(kUserAction1));
+  EXPECT_EQ(2, user_action_tester.GetActionCount(kUserAction2));
+  EXPECT_EQ(0, user_action_tester.GetActionCount(kUserAction3));
+}
+
+// Verify no seg faults occur when resetting action counts when none have been
+// recorded.
+TEST(UserActionTesterTest, ResetCountsWhenNoActionsHaveBeenRecorded) {
+  UserActionTester user_action_tester;
+  user_action_tester.ResetCounts();
+}
+
+// Verify user action counts are set to zero on a ResetCounts.
+TEST(UserActionTesterTest, ResetCountsWhenActionsHaveBeenRecorded) {
+  UserActionTester user_action_tester;
+
+  RecordAction(kUserAction1);
+  RecordAction(kUserAction1);
+  RecordAction(kUserAction2);
+  user_action_tester.ResetCounts();
+
+  EXPECT_EQ(0, user_action_tester.GetActionCount(kUserAction1));
+  EXPECT_EQ(0, user_action_tester.GetActionCount(kUserAction2));
+  EXPECT_EQ(0, user_action_tester.GetActionCount(kUserAction3));
+}
+
+// Verify the UserActionsTester is notified when base::RecordAction is called.
+TEST(UserActionTesterTest, VerifyUserActionTesterListensForUserActions) {
+  UserActionTester user_action_tester;
+
+  base::RecordAction(base::UserMetricsAction(kUserAction1));
+
+  EXPECT_EQ(1, user_action_tester.GetActionCount(kUserAction1));
+}
+
+// Verify the UserActionsTester is notified when base::RecordComputedAction is
+// called.
+TEST(UserActionTesterTest,
+     VerifyUserActionTesterListensForComputedUserActions) {
+  UserActionTester user_action_tester;
+
+  base::RecordComputedAction(kUserAction1);
+
+  EXPECT_EQ(1, user_action_tester.GetActionCount(kUserAction1));
+}
+
+}  // namespace base
diff --git a/base/test/mock_callback.h b/base/test/mock_callback.h
new file mode 100644
index 0000000..7ac4d34
--- /dev/null
+++ b/base/test/mock_callback.h
@@ -0,0 +1,366 @@
+// This file was GENERATED by command:
+//     pump.py mock_callback.h.pump
+// DO NOT EDIT BY HAND!!!
+
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Analogous to GMock's built-in MockFunction, but for base::Callback instead of
+// std::function. It takes the full callback type as a parameter, so that it can
+// support both OnceCallback and RepeatingCallback.
+//
+// Use:
+//   using FooCallback = base::Callback<int(std::string)>;
+//
+//   TEST(FooTest, RunsCallbackWithBarArgument) {
+//     base::MockCallback<FooCallback> callback;
+//     EXPECT_CALL(callback, Run("bar")).WillOnce(Return(1));
+//     Foo(callback.Get());
+//   }
+//
+// Can be used with StrictMock and NiceMock. Caller must ensure that it outlives
+// any base::Callback obtained from it.
+
+#ifndef BASE_TEST_MOCK_CALLBACK_H_
+#define BASE_TEST_MOCK_CALLBACK_H_
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/macros.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace base {
+
+// clang-format off
+
+template <typename F>
+class MockCallback;
+
+template <typename R>
+class MockCallback<Callback<R()>> {
+ public:
+  MockCallback() = default;
+  MOCK_METHOD0_T(Run, R());
+
+  Callback<R()> Get() {
+    return Bind(&MockCallback::Run, Unretained(this));
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockCallback);
+};
+
+template <typename R>
+class MockCallback<OnceCallback<R()>> {
+ public:
+  MockCallback() = default;
+  MOCK_METHOD0_T(Run, R());
+
+  OnceCallback<R()> Get() {
+    return BindOnce(&MockCallback::Run, Unretained(this));
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockCallback);
+};
+
+template <typename R, typename A1>
+class MockCallback<Callback<R(A1)>> {
+ public:
+  MockCallback() = default;
+  MOCK_METHOD1_T(Run, R(A1));
+
+  Callback<R(A1)> Get() {
+    return Bind(&MockCallback::Run, Unretained(this));
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockCallback);
+};
+
+template <typename R, typename A1>
+class MockCallback<OnceCallback<R(A1)>> {
+ public:
+  MockCallback() = default;
+  MOCK_METHOD1_T(Run, R(A1));
+
+  OnceCallback<R(A1)> Get() {
+    return BindOnce(&MockCallback::Run, Unretained(this));
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockCallback);
+};
+
+template <typename R, typename A1, typename A2>
+class MockCallback<Callback<R(A1, A2)>> {
+ public:
+  MockCallback() = default;
+  MOCK_METHOD2_T(Run, R(A1, A2));
+
+  Callback<R(A1, A2)> Get() {
+    return Bind(&MockCallback::Run, Unretained(this));
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockCallback);
+};
+
+template <typename R, typename A1, typename A2>
+class MockCallback<OnceCallback<R(A1, A2)>> {
+ public:
+  MockCallback() = default;
+  MOCK_METHOD2_T(Run, R(A1, A2));
+
+  OnceCallback<R(A1, A2)> Get() {
+    return BindOnce(&MockCallback::Run, Unretained(this));
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockCallback);
+};
+
+template <typename R, typename A1, typename A2, typename A3>
+class MockCallback<Callback<R(A1, A2, A3)>> {
+ public:
+  MockCallback() = default;
+  MOCK_METHOD3_T(Run, R(A1, A2, A3));
+
+  Callback<R(A1, A2, A3)> Get() {
+    return Bind(&MockCallback::Run, Unretained(this));
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockCallback);
+};
+
+template <typename R, typename A1, typename A2, typename A3>
+class MockCallback<OnceCallback<R(A1, A2, A3)>> {
+ public:
+  MockCallback() = default;
+  MOCK_METHOD3_T(Run, R(A1, A2, A3));
+
+  OnceCallback<R(A1, A2, A3)> Get() {
+    return BindOnce(&MockCallback::Run, Unretained(this));
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockCallback);
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4>
+class MockCallback<Callback<R(A1, A2, A3, A4)>> {
+ public:
+  MockCallback() = default;
+  MOCK_METHOD4_T(Run, R(A1, A2, A3, A4));
+
+  Callback<R(A1, A2, A3, A4)> Get() {
+    return Bind(&MockCallback::Run, Unretained(this));
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockCallback);
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4>
+class MockCallback<OnceCallback<R(A1, A2, A3, A4)>> {
+ public:
+  MockCallback() = default;
+  MOCK_METHOD4_T(Run, R(A1, A2, A3, A4));
+
+  OnceCallback<R(A1, A2, A3, A4)> Get() {
+    return BindOnce(&MockCallback::Run, Unretained(this));
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockCallback);
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+    typename A5>
+class MockCallback<Callback<R(A1, A2, A3, A4, A5)>> {
+ public:
+  MockCallback() = default;
+  MOCK_METHOD5_T(Run, R(A1, A2, A3, A4, A5));
+
+  Callback<R(A1, A2, A3, A4, A5)> Get() {
+    return Bind(&MockCallback::Run, Unretained(this));
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockCallback);
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+    typename A5>
+class MockCallback<OnceCallback<R(A1, A2, A3, A4, A5)>> {
+ public:
+  MockCallback() = default;
+  MOCK_METHOD5_T(Run, R(A1, A2, A3, A4, A5));
+
+  OnceCallback<R(A1, A2, A3, A4, A5)> Get() {
+    return BindOnce(&MockCallback::Run, Unretained(this));
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockCallback);
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+    typename A5, typename A6>
+class MockCallback<Callback<R(A1, A2, A3, A4, A5, A6)>> {
+ public:
+  MockCallback() = default;
+  MOCK_METHOD6_T(Run, R(A1, A2, A3, A4, A5, A6));
+
+  Callback<R(A1, A2, A3, A4, A5, A6)> Get() {
+    return Bind(&MockCallback::Run, Unretained(this));
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockCallback);
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+    typename A5, typename A6>
+class MockCallback<OnceCallback<R(A1, A2, A3, A4, A5, A6)>> {
+ public:
+  MockCallback() = default;
+  MOCK_METHOD6_T(Run, R(A1, A2, A3, A4, A5, A6));
+
+  OnceCallback<R(A1, A2, A3, A4, A5, A6)> Get() {
+    return BindOnce(&MockCallback::Run, Unretained(this));
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockCallback);
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+    typename A5, typename A6, typename A7>
+class MockCallback<Callback<R(A1, A2, A3, A4, A5, A6, A7)>> {
+ public:
+  MockCallback() = default;
+  MOCK_METHOD7_T(Run, R(A1, A2, A3, A4, A5, A6, A7));
+
+  Callback<R(A1, A2, A3, A4, A5, A6, A7)> Get() {
+    return Bind(&MockCallback::Run, Unretained(this));
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockCallback);
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+    typename A5, typename A6, typename A7>
+class MockCallback<OnceCallback<R(A1, A2, A3, A4, A5, A6, A7)>> {
+ public:
+  MockCallback() = default;
+  MOCK_METHOD7_T(Run, R(A1, A2, A3, A4, A5, A6, A7));
+
+  OnceCallback<R(A1, A2, A3, A4, A5, A6, A7)> Get() {
+    return BindOnce(&MockCallback::Run, Unretained(this));
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockCallback);
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+    typename A5, typename A6, typename A7, typename A8>
+class MockCallback<Callback<R(A1, A2, A3, A4, A5, A6, A7, A8)>> {
+ public:
+  MockCallback() = default;
+  MOCK_METHOD8_T(Run, R(A1, A2, A3, A4, A5, A6, A7, A8));
+
+  Callback<R(A1, A2, A3, A4, A5, A6, A7, A8)> Get() {
+    return Bind(&MockCallback::Run, Unretained(this));
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockCallback);
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+    typename A5, typename A6, typename A7, typename A8>
+class MockCallback<OnceCallback<R(A1, A2, A3, A4, A5, A6, A7, A8)>> {
+ public:
+  MockCallback() = default;
+  MOCK_METHOD8_T(Run, R(A1, A2, A3, A4, A5, A6, A7, A8));
+
+  OnceCallback<R(A1, A2, A3, A4, A5, A6, A7, A8)> Get() {
+    return BindOnce(&MockCallback::Run, Unretained(this));
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockCallback);
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+    typename A5, typename A6, typename A7, typename A8, typename A9>
+class MockCallback<Callback<R(A1, A2, A3, A4, A5, A6, A7, A8, A9)>> {
+ public:
+  MockCallback() = default;
+  MOCK_METHOD9_T(Run, R(A1, A2, A3, A4, A5, A6, A7, A8, A9));
+
+  Callback<R(A1, A2, A3, A4, A5, A6, A7, A8, A9)> Get() {
+    return Bind(&MockCallback::Run, Unretained(this));
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockCallback);
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+    typename A5, typename A6, typename A7, typename A8, typename A9>
+class MockCallback<OnceCallback<R(A1, A2, A3, A4, A5, A6, A7, A8, A9)>> {
+ public:
+  MockCallback() = default;
+  MOCK_METHOD9_T(Run, R(A1, A2, A3, A4, A5, A6, A7, A8, A9));
+
+  OnceCallback<R(A1, A2, A3, A4, A5, A6, A7, A8, A9)> Get() {
+    return BindOnce(&MockCallback::Run, Unretained(this));
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockCallback);
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+    typename A5, typename A6, typename A7, typename A8, typename A9,
+    typename A10>
+class MockCallback<Callback<R(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10)>> {
+ public:
+  MockCallback() = default;
+  MOCK_METHOD10_T(Run, R(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10));
+
+  Callback<R(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10)> Get() {
+    return Bind(&MockCallback::Run, Unretained(this));
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockCallback);
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+    typename A5, typename A6, typename A7, typename A8, typename A9,
+    typename A10>
+class MockCallback<OnceCallback<R(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10)>> {
+ public:
+  MockCallback() = default;
+  MOCK_METHOD10_T(Run, R(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10));
+
+  OnceCallback<R(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10)> Get() {
+    return BindOnce(&MockCallback::Run, Unretained(this));
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockCallback);
+};
+
+// clang-format on
+
+}  // namespace base
+
+#endif  // BASE_TEST_MOCK_CALLBACK_H_
diff --git a/base/test/mock_callback.h.pump b/base/test/mock_callback.h.pump
new file mode 100644
index 0000000..3372789
--- /dev/null
+++ b/base/test/mock_callback.h.pump
@@ -0,0 +1,85 @@
+$$ This is a pump file for generating file templates.  Pump is a python
+$$ script that is part of the Google Test suite of utilities.  Description
+$$ can be found here:
+$$
+$$ https://github.com/google/googletest/blob/master/googletest/docs/PumpManual.md
+$$
+$$ MAX_ARITY controls the number of arguments that MockCallback supports.
+$$ It is choosen to match the number GMock supports.
+$var MAX_ARITY = 10
+$$
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Analogous to GMock's built-in MockFunction, but for base::Callback instead of
+// std::function. It takes the full callback type as a parameter, so that it can
+// support both OnceCallback and RepeatingCallback.
+//
+// Use:
+//   using FooCallback = base::Callback<int(std::string)>;
+//
+//   TEST(FooTest, RunsCallbackWithBarArgument) {
+//     base::MockCallback<FooCallback> callback;
+//     EXPECT_CALL(callback, Run("bar")).WillOnce(Return(1));
+//     Foo(callback.Get());
+//   }
+//
+// Can be used with StrictMock and NiceMock. Caller must ensure that it outlives
+// any base::Callback obtained from it.
+
+#ifndef BASE_TEST_MOCK_CALLBACK_H_
+#define BASE_TEST_MOCK_CALLBACK_H_
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/macros.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace base {
+
+// clang-format off
+
+template <typename F>
+class MockCallback;
+
+$range i 0..MAX_ARITY
+$for i [[
+$range j 1..i
+$var run_type = [[R($for j, [[A$j]])]]
+
+template <typename R$for j [[, typename A$j]]>
+class MockCallback<Callback<$run_type>> {
+ public:
+  MockCallback() = default;
+  MOCK_METHOD$(i)_T(Run, $run_type);
+
+  Callback<$run_type> Get() {
+    return Bind(&MockCallback::Run, Unretained(this));
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockCallback);
+};
+
+template <typename R$for j [[, typename A$j]]>
+class MockCallback<OnceCallback<$run_type>> {
+ public:
+  MockCallback() = default;
+  MOCK_METHOD$(i)_T(Run, $run_type);
+
+  OnceCallback<$run_type> Get() {
+    return BindOnce(&MockCallback::Run, Unretained(this));
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockCallback);
+};
+
+]]
+
+// clang-format on
+
+}  // namespace base
+
+#endif  // BASE_TEST_MOCK_CALLBACK_H_
diff --git a/base/test/mock_callback_unittest.cc b/base/test/mock_callback_unittest.cc
new file mode 100644
index 0000000..c5f109f
--- /dev/null
+++ b/base/test/mock_callback_unittest.cc
@@ -0,0 +1,59 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/mock_callback.h"
+
+#include "base/callback.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+using testing::InSequence;
+using testing::Return;
+
+namespace base {
+namespace {
+
+TEST(MockCallbackTest, ZeroArgs) {
+  MockCallback<Closure> mock_closure;
+  EXPECT_CALL(mock_closure, Run());
+  mock_closure.Get().Run();
+
+  MockCallback<Callback<int()>> mock_int_callback;
+  {
+    InSequence sequence;
+    EXPECT_CALL(mock_int_callback, Run()).WillOnce(Return(42));
+    EXPECT_CALL(mock_int_callback, Run()).WillOnce(Return(88));
+  }
+  EXPECT_EQ(42, mock_int_callback.Get().Run());
+  EXPECT_EQ(88, mock_int_callback.Get().Run());
+}
+
+TEST(MockCallbackTest, WithArgs) {
+  MockCallback<Callback<int(int, int)>> mock_two_int_callback;
+  EXPECT_CALL(mock_two_int_callback, Run(1, 2)).WillOnce(Return(42));
+  EXPECT_CALL(mock_two_int_callback, Run(0, 0)).WillRepeatedly(Return(-1));
+  Callback<int(int, int)> two_int_callback = mock_two_int_callback.Get();
+  EXPECT_EQ(-1, two_int_callback.Run(0, 0));
+  EXPECT_EQ(42, two_int_callback.Run(1, 2));
+  EXPECT_EQ(-1, two_int_callback.Run(0, 0));
+}
+
+TEST(MockCallbackTest, ZeroArgsOnce) {
+  MockCallback<OnceClosure> mock_closure;
+  EXPECT_CALL(mock_closure, Run());
+  mock_closure.Get().Run();
+
+  MockCallback<OnceCallback<int()>> mock_int_callback;
+  EXPECT_CALL(mock_int_callback, Run()).WillOnce(Return(88));
+  EXPECT_EQ(88, mock_int_callback.Get().Run());
+}
+
+TEST(MockCallbackTest, WithArgsOnce) {
+  MockCallback<OnceCallback<int(int, int)>> mock_two_int_callback;
+  EXPECT_CALL(mock_two_int_callback, Run(1, 2)).WillOnce(Return(42));
+  OnceCallback<int(int, int)> two_int_callback = mock_two_int_callback.Get();
+  EXPECT_EQ(42, std::move(two_int_callback).Run(1, 2));
+}
+
+}  // namespace
+}  // namespace base
diff --git a/base/test/mock_devices_changed_observer.cc b/base/test/mock_devices_changed_observer.cc
new file mode 100644
index 0000000..9fc57cd
--- /dev/null
+++ b/base/test/mock_devices_changed_observer.cc
@@ -0,0 +1,13 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/mock_devices_changed_observer.h"
+
+namespace base {
+
+MockDevicesChangedObserver::MockDevicesChangedObserver() = default;
+
+MockDevicesChangedObserver::~MockDevicesChangedObserver() = default;
+
+}  // namespace base
diff --git a/base/test/mock_devices_changed_observer.h b/base/test/mock_devices_changed_observer.h
new file mode 100644
index 0000000..0734fb4
--- /dev/null
+++ b/base/test/mock_devices_changed_observer.h
@@ -0,0 +1,31 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_MOCK_DEVICES_CHANGED_OBSERVER_H_
+#define BASE_TEST_MOCK_DEVICES_CHANGED_OBSERVER_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "base/system_monitor/system_monitor.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace base {
+
+class MockDevicesChangedObserver
+    : public base::SystemMonitor::DevicesChangedObserver {
+ public:
+  MockDevicesChangedObserver();
+  ~MockDevicesChangedObserver() override;
+
+  MOCK_METHOD1(OnDevicesChanged,
+               void(base::SystemMonitor::DeviceType device_type));
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockDevicesChangedObserver);
+};
+
+}  // namespace base
+
+#endif  // BASE_TEST_MOCK_DEVICES_CHANGED_OBSERVER_H_
diff --git a/base/test/mock_log.cc b/base/test/mock_log.cc
new file mode 100644
index 0000000..a09000d
--- /dev/null
+++ b/base/test/mock_log.cc
@@ -0,0 +1,68 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/mock_log.h"
+
+namespace base {
+namespace test {
+
+// static
+MockLog* MockLog::g_instance_ = nullptr;
+Lock MockLog::g_lock;
+
+MockLog::MockLog() : is_capturing_logs_(false) {
+}
+
+MockLog::~MockLog() {
+  if (is_capturing_logs_) {
+    StopCapturingLogs();
+  }
+}
+
+void MockLog::StartCapturingLogs() {
+  AutoLock scoped_lock(g_lock);
+
+  // We don't use CHECK(), which can generate a new LOG message, and
+  // thus can confuse MockLog objects or other registered
+  // LogSinks.
+  RAW_CHECK(!is_capturing_logs_);
+  RAW_CHECK(!g_instance_);
+
+  is_capturing_logs_ = true;
+  g_instance_ = this;
+  previous_handler_ = logging::GetLogMessageHandler();
+  logging::SetLogMessageHandler(LogMessageHandler);
+}
+
+void MockLog::StopCapturingLogs() {
+  AutoLock scoped_lock(g_lock);
+
+  // We don't use CHECK(), which can generate a new LOG message, and
+  // thus can confuse MockLog objects or other registered
+  // LogSinks.
+  RAW_CHECK(is_capturing_logs_);
+  RAW_CHECK(g_instance_ == this);
+
+  is_capturing_logs_ = false;
+  logging::SetLogMessageHandler(previous_handler_);
+  g_instance_ = nullptr;
+}
+
+// static
+bool MockLog::LogMessageHandler(int severity,
+                                const char* file,
+                                int line,
+                                size_t message_start,
+                                const std::string& str) {
+  // gMock guarantees thread-safety for calling a mocked method
+  // (https://github.com/google/googlemock/blob/master/googlemock/docs/CookBook.md#using-google-mock-and-threads)
+  // but we also need to make sure that Start/StopCapturingLogs are synchronized
+  // with LogMessageHandler.
+  AutoLock scoped_lock(g_lock);
+
+  return g_instance_->Log(severity, file, line, message_start, str);
+}
+
+}  // namespace test
+}  // namespace base
diff --git a/base/test/mock_log.h b/base/test/mock_log.h
new file mode 100644
index 0000000..cda2fcd
--- /dev/null
+++ b/base/test/mock_log.h
@@ -0,0 +1,100 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_MOCK_LOG_H_
+#define BASE_TEST_MOCK_LOG_H_
+
+#include <stddef.h>
+
+#include <string>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/synchronization/lock.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace base {
+namespace test {
+
+// A MockLog object intercepts LOG() messages issued during its lifespan.  Using
+// this together with gMock, it's very easy to test how a piece of code calls
+// LOG().  The typical usage:
+//
+//   TEST(FooTest, LogsCorrectly) {
+//     MockLog log;
+//
+//     // We expect the WARNING "Something bad!" exactly twice.
+//     EXPECT_CALL(log, Log(WARNING, _, "Something bad!"))
+//         .Times(2);
+//
+//     // We allow foo.cc to call LOG(INFO) any number of times.
+//     EXPECT_CALL(log, Log(INFO, HasSubstr("/foo.cc"), _))
+//         .Times(AnyNumber());
+//
+//     log.StartCapturingLogs();  // Call this after done setting expectations.
+//     Foo();  // Exercises the code under test.
+//   }
+//
+// CAVEAT: base/logging does not allow a thread to call LOG() again when it's
+// already inside a LOG() call.  Doing so will cause a deadlock.  Therefore,
+// it's the user's responsibility to not call LOG() in an action triggered by
+// MockLog::Log().  You may call RAW_LOG() instead.
+class MockLog {
+ public:
+  // Creates a MockLog object that is not capturing logs.  If it were to start
+  // to capture logs, it could be a problem if some other threads already exist
+  // and are logging, as the user hasn't had a chance to set up expectation on
+  // this object yet (calling a mock method before setting the expectation is
+  // UNDEFINED behavior).
+  MockLog();
+
+  // When the object is destructed, it stops intercepting logs.
+  ~MockLog();
+
+  // Starts log capturing if the object isn't already doing so.
+  // Otherwise crashes.
+  void StartCapturingLogs();
+
+  // Stops log capturing if the object is capturing logs.  Otherwise crashes.
+  void StopCapturingLogs();
+
+  // Log method is invoked for every log message before it's sent to other log
+  // destinations (if any).  The method should return true to signal that it
+  // handled the message and the message should not be sent to other log
+  // destinations.
+  MOCK_METHOD5(Log,
+               bool(int severity,
+                    const char* file,
+                    int line,
+                    size_t message_start,
+                    const std::string& str));
+
+ private:
+  // The currently active mock log.
+  static MockLog* g_instance_;
+
+  // Lock protecting access to g_instance_.
+  static Lock g_lock;
+
+  // Static function which is set as the logging message handler.
+  // Called once for each message.
+  static bool LogMessageHandler(int severity,
+                                const char* file,
+                                int line,
+                                size_t message_start,
+                                const std::string& str);
+
+  // True if this object is currently capturing logs.
+  bool is_capturing_logs_;
+
+  // The previous handler to restore when the MockLog is destroyed.
+  logging::LogMessageHandlerFunction previous_handler_;
+
+  DISALLOW_COPY_AND_ASSIGN(MockLog);
+};
+
+}  // namespace test
+}  // namespace base
+
+#endif  // BASE_TEST_MOCK_LOG_H_
diff --git a/base/test/native_library_test_utils.cc b/base/test/native_library_test_utils.cc
new file mode 100644
index 0000000..adcb1b0
--- /dev/null
+++ b/base/test/native_library_test_utils.cc
@@ -0,0 +1,19 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/native_library_test_utils.h"
+
+namespace {
+
+int g_static_value = 0;
+
+}  // namespace
+
+extern "C" {
+
+int g_native_library_exported_value = 0;
+
+int NativeLibraryTestIncrement() { return ++g_static_value; }
+
+}  // extern "C"
diff --git a/base/test/native_library_test_utils.h b/base/test/native_library_test_utils.h
new file mode 100644
index 0000000..e26fd1a
--- /dev/null
+++ b/base/test/native_library_test_utils.h
@@ -0,0 +1,26 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_NATIVE_LIBRARY_TEST_UTILS_H_
+#define BASE_TEST_NATIVE_LIBRARY_TEST_UTILS_H_
+
+#include "build/build_config.h"
+
+#if defined(OS_WIN)
+#define NATIVE_LIBRARY_TEST_ALWAYS_EXPORT __declspec(dllexport)
+#else
+#define NATIVE_LIBRARY_TEST_ALWAYS_EXPORT __attribute__((visibility("default")))
+#endif
+
+extern "C" {
+
+extern NATIVE_LIBRARY_TEST_ALWAYS_EXPORT int g_native_library_exported_value;
+
+// A function which increments an internal counter value and returns its value.
+// The first call returns 1, then 2, etc.
+NATIVE_LIBRARY_TEST_ALWAYS_EXPORT int NativeLibraryTestIncrement();
+
+}  // extern "C"
+
+#endif  // BASE_TEST_NATIVE_LIBRARY_TEST_UTILS_H_
diff --git a/base/test/null_task_runner.cc b/base/test/null_task_runner.cc
new file mode 100644
index 0000000..dfa26fa
--- /dev/null
+++ b/base/test/null_task_runner.cc
@@ -0,0 +1,29 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/null_task_runner.h"
+
+namespace base {
+
+NullTaskRunner::NullTaskRunner() = default;
+
+NullTaskRunner::~NullTaskRunner() = default;
+
+bool NullTaskRunner::PostDelayedTask(const Location& from_here,
+                                     OnceClosure task,
+                                     base::TimeDelta delay) {
+  return false;
+}
+
+bool NullTaskRunner::PostNonNestableDelayedTask(const Location& from_here,
+                                                OnceClosure task,
+                                                base::TimeDelta delay) {
+  return false;
+}
+
+bool NullTaskRunner::RunsTasksInCurrentSequence() const {
+  return true;
+}
+
+}  // namespace base
diff --git a/base/test/null_task_runner.h b/base/test/null_task_runner.h
new file mode 100644
index 0000000..c11ab6b
--- /dev/null
+++ b/base/test/null_task_runner.h
@@ -0,0 +1,39 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_NULL_TASK_RUNNER_H_
+#define BASE_TEST_NULL_TASK_RUNNER_H_
+
+#include "base/callback.h"
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/single_thread_task_runner.h"
+
+namespace base {
+
+// Helper class for tests that need to provide an implementation of a
+// *TaskRunner class but don't actually care about tasks being run.
+
+class NullTaskRunner : public base::SingleThreadTaskRunner {
+ public:
+  NullTaskRunner();
+
+  bool PostDelayedTask(const Location& from_here,
+                       base::OnceClosure task,
+                       base::TimeDelta delay) override;
+  bool PostNonNestableDelayedTask(const Location& from_here,
+                                  base::OnceClosure task,
+                                  base::TimeDelta delay) override;
+  // Always returns true to avoid triggering DCHECKs.
+  bool RunsTasksInCurrentSequence() const override;
+
+ protected:
+  ~NullTaskRunner() override;
+
+  DISALLOW_COPY_AND_ASSIGN(NullTaskRunner);
+};
+
+}  // namespace base
+
+#endif  // BASE_TEST_NULL_TASK_RUNNER_H_
diff --git a/base/test/perf_log.cc b/base/test/perf_log.cc
new file mode 100644
index 0000000..9212f4b
--- /dev/null
+++ b/base/test/perf_log.cc
@@ -0,0 +1,45 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/perf_log.h"
+
+#include "base/files/file_util.h"
+#include "base/logging.h"
+
+namespace base {
+
+static FILE* perf_log_file = nullptr;
+
+bool InitPerfLog(const FilePath& log_file) {
+  if (perf_log_file) {
+    // trying to initialize twice
+    NOTREACHED();
+    return false;
+  }
+
+  perf_log_file = OpenFile(log_file, "w");
+  return perf_log_file != nullptr;
+}
+
+void FinalizePerfLog() {
+  if (!perf_log_file) {
+    // trying to cleanup without initializing
+    NOTREACHED();
+    return;
+  }
+  base::CloseFile(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);
+  fflush(stdout);
+}
+
+}  // namespace base
diff --git a/base/test/perf_log.h b/base/test/perf_log.h
new file mode 100644
index 0000000..5d6ed9f
--- /dev/null
+++ b/base/test/perf_log.h
@@ -0,0 +1,24 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_PERF_LOG_H_
+#define BASE_TEST_PERF_LOG_H_
+
+namespace base {
+
+class FilePath;
+
+// 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 FilePath& log_path);
+void FinalizePerfLog();
+
+// 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);
+
+}  // namespace base
+
+#endif  // BASE_TEST_PERF_LOG_H_
diff --git a/base/test/perf_test_suite.cc b/base/test/perf_test_suite.cc
new file mode 100644
index 0000000..2e2cdbb
--- /dev/null
+++ b/base/test/perf_test_suite.cc
@@ -0,0 +1,50 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/perf_test_suite.h"
+
+#include "base/command_line.h"
+#include "base/debug/debugger.h"
+#include "base/files/file_path.h"
+#include "base/path_service.h"
+#include "base/process/launch.h"
+#include "base/strings/string_util.h"
+#include "base/test/perf_log.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+PerfTestSuite::PerfTestSuite(int argc, char** argv) : TestSuite(argc, argv) {}
+
+void PerfTestSuite::Initialize() {
+  TestSuite::Initialize();
+
+  // Initialize the perf timer log
+  FilePath log_path =
+      CommandLine::ForCurrentProcess()->GetSwitchValuePath("log-file");
+  if (log_path.empty()) {
+    PathService::Get(FILE_EXE, &log_path);
+#if defined(OS_ANDROID) || defined(OS_FUCHSIA)
+    base::FilePath tmp_dir;
+    PathService::Get(base::DIR_CACHE, &tmp_dir);
+    log_path = tmp_dir.Append(log_path.BaseName());
+#endif
+    log_path = log_path.ReplaceExtension(FILE_PATH_LITERAL("log"));
+    log_path = log_path.InsertBeforeExtension(FILE_PATH_LITERAL("_perf"));
+  }
+  ASSERT_TRUE(InitPerfLog(log_path));
+
+  // 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 (!debug::BeingDebugged())
+    RaiseProcessToHighPriority();
+}
+
+void PerfTestSuite::Shutdown() {
+  TestSuite::Shutdown();
+  FinalizePerfLog();
+}
+
+}  // namespace base
diff --git a/base/test/perf_test_suite.h b/base/test/perf_test_suite.h
new file mode 100644
index 0000000..52528f0
--- /dev/null
+++ b/base/test/perf_test_suite.h
@@ -0,0 +1,22 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_PERF_TEST_SUITE_H_
+#define BASE_TEST_PERF_TEST_SUITE_H_
+
+#include "base/test/test_suite.h"
+
+namespace base {
+
+class PerfTestSuite : public TestSuite {
+ public:
+  PerfTestSuite(int argc, char** argv);
+
+  void Initialize() override;
+  void Shutdown() override;
+};
+
+}  // namespace base
+
+#endif  // BASE_TEST_PERF_TEST_SUITE_H_
diff --git a/base/test/perf_time_logger.cc b/base/test/perf_time_logger.cc
new file mode 100644
index 0000000..c05ba51
--- /dev/null
+++ b/base/test/perf_time_logger.cc
@@ -0,0 +1,27 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/perf_time_logger.h"
+
+#include "base/test/perf_log.h"
+
+namespace base {
+
+PerfTimeLogger::PerfTimeLogger(const char* test_name)
+    : logged_(false), test_name_(test_name) {}
+
+PerfTimeLogger::~PerfTimeLogger() {
+  if (!logged_)
+    Done();
+}
+
+void PerfTimeLogger::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;
+}
+
+}  // namespace base
diff --git a/base/test/perf_time_logger.h b/base/test/perf_time_logger.h
new file mode 100644
index 0000000..a5f3e8a
--- /dev/null
+++ b/base/test/perf_time_logger.h
@@ -0,0 +1,37 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_PERF_TIME_LOGGER_H_
+#define BASE_TEST_PERF_TIME_LOGGER_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "base/timer/elapsed_timer.h"
+
+namespace base {
+
+// 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);
+  ~PerfTimeLogger();
+
+  void Done();
+
+ private:
+  bool logged_;
+  std::string test_name_;
+  ElapsedTimer timer_;
+
+  DISALLOW_COPY_AND_ASSIGN(PerfTimeLogger);
+};
+
+}  // namespace base
+
+#endif  // BASE_TEST_PERF_TIME_LOGGER_H_
diff --git a/base/test/power_monitor_test_base.cc b/base/test/power_monitor_test_base.cc
new file mode 100644
index 0000000..f362566
--- /dev/null
+++ b/base/test/power_monitor_test_base.cc
@@ -0,0 +1,68 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/power_monitor_test_base.h"
+
+#include "base/message_loop/message_loop.h"
+#include "base/message_loop/message_loop_current.h"
+#include "base/power_monitor/power_monitor.h"
+#include "base/power_monitor/power_monitor_source.h"
+#include "base/run_loop.h"
+
+namespace base {
+
+PowerMonitorTestSource::PowerMonitorTestSource()
+    : test_on_battery_power_(false) {
+  DCHECK(MessageLoopCurrent::Get())
+      << "PowerMonitorTestSource requires a MessageLoop.";
+}
+
+PowerMonitorTestSource::~PowerMonitorTestSource() = default;
+
+void PowerMonitorTestSource::Shutdown() {}
+
+void PowerMonitorTestSource::GeneratePowerStateEvent(bool on_battery_power) {
+  test_on_battery_power_ = on_battery_power;
+  ProcessPowerEvent(POWER_STATE_EVENT);
+  RunLoop().RunUntilIdle();
+}
+
+void PowerMonitorTestSource::GenerateSuspendEvent() {
+  ProcessPowerEvent(SUSPEND_EVENT);
+  RunLoop().RunUntilIdle();
+}
+
+void PowerMonitorTestSource::GenerateResumeEvent() {
+  ProcessPowerEvent(RESUME_EVENT);
+  RunLoop().RunUntilIdle();
+}
+
+bool PowerMonitorTestSource::IsOnBatteryPowerImpl() {
+  return test_on_battery_power_;
+};
+
+PowerMonitorTestObserver::PowerMonitorTestObserver()
+    : last_power_state_(false),
+      power_state_changes_(0),
+      suspends_(0),
+      resumes_(0) {
+}
+
+PowerMonitorTestObserver::~PowerMonitorTestObserver() = default;
+
+// PowerObserver callbacks.
+void PowerMonitorTestObserver::OnPowerStateChange(bool on_battery_power) {
+  last_power_state_ = on_battery_power;
+  power_state_changes_++;
+}
+
+void PowerMonitorTestObserver::OnSuspend() {
+  suspends_++;
+}
+
+void PowerMonitorTestObserver::OnResume() {
+  resumes_++;
+}
+
+}  // namespace base
diff --git a/base/test/power_monitor_test_base.h b/base/test/power_monitor_test_base.h
new file mode 100644
index 0000000..9b14fb0
--- /dev/null
+++ b/base/test/power_monitor_test_base.h
@@ -0,0 +1,54 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_POWER_MONITOR_TEST_BASE_H_
+#define BASE_TEST_POWER_MONITOR_TEST_BASE_H_
+
+#include "base/power_monitor/power_monitor.h"
+#include "base/power_monitor/power_monitor_source.h"
+
+namespace base {
+
+class PowerMonitorTestSource : public PowerMonitorSource {
+ public:
+  PowerMonitorTestSource();
+  ~PowerMonitorTestSource() override;
+  void Shutdown() override;
+
+  void GeneratePowerStateEvent(bool on_battery_power);
+  void GenerateSuspendEvent();
+  void GenerateResumeEvent();
+
+ protected:
+  bool IsOnBatteryPowerImpl() override;
+
+  bool test_on_battery_power_;
+};
+
+class PowerMonitorTestObserver : public PowerObserver {
+ public:
+  PowerMonitorTestObserver();
+  ~PowerMonitorTestObserver() override;
+
+  // PowerObserver callbacks.
+  void OnPowerStateChange(bool on_battery_power) override;
+  void OnSuspend() override;
+  void OnResume() override;
+
+  // Test status counts.
+  bool last_power_state() { return last_power_state_; }
+  int power_state_changes() { return power_state_changes_; }
+  int suspends() { return suspends_; }
+  int resumes() { return resumes_; }
+
+ private:
+  bool last_power_state_; // Last power state we were notified of.
+  int power_state_changes_;  // Count of OnPowerStateChange notifications.
+  int suspends_;  // Count of OnSuspend notifications.
+  int resumes_;  // Count of OnResume notifications.
+};
+
+}  // namespace base
+
+#endif  // BASE_TEST_POWER_MONITOR_TEST_BASE_H_
diff --git a/base/test/run_all_base_unittests.cc b/base/test/run_all_base_unittests.cc
new file mode 100644
index 0000000..da52310
--- /dev/null
+++ b/base/test/run_all_base_unittests.cc
@@ -0,0 +1,15 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/bind.h"
+#include "base/test/launcher/unit_test_launcher.h"
+#include "base/test/test_suite.h"
+#include "build/build_config.h"
+
+int main(int argc, char** argv) {
+  base::TestSuite test_suite(argc, argv);
+  return base::LaunchUnitTests(
+      argc, argv,
+      base::Bind(&base::TestSuite::Run, base::Unretained(&test_suite)));
+}
diff --git a/base/test/run_all_perftests.cc b/base/test/run_all_perftests.cc
new file mode 100644
index 0000000..6e38109
--- /dev/null
+++ b/base/test/run_all_perftests.cc
@@ -0,0 +1,9 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/perf_test_suite.h"
+
+int main(int argc, char** argv) {
+  return base::PerfTestSuite(argc, argv).Run();
+}
diff --git a/base/test/run_all_unittests.cc b/base/test/run_all_unittests.cc
new file mode 100644
index 0000000..0ad84ed
--- /dev/null
+++ b/base/test/run_all_unittests.cc
@@ -0,0 +1,15 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/bind.h"
+#include "base/test/launcher/unit_test_launcher.h"
+#include "base/test/test_suite.h"
+#include "build/build_config.h"
+
+int main(int argc, char** argv) {
+  base::TestSuite test_suite(argc, argv);
+  return base::LaunchUnitTests(
+      argc, argv,
+      base::BindOnce(&base::TestSuite::Run, base::Unretained(&test_suite)));
+}
diff --git a/base/test/scoped_command_line.cc b/base/test/scoped_command_line.cc
new file mode 100644
index 0000000..c74d243
--- /dev/null
+++ b/base/test/scoped_command_line.cc
@@ -0,0 +1,22 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/scoped_command_line.h"
+
+namespace base {
+namespace test {
+
+ScopedCommandLine::ScopedCommandLine()
+    : original_command_line_(*base::CommandLine::ForCurrentProcess()) {}
+
+ScopedCommandLine::~ScopedCommandLine() {
+  *base::CommandLine::ForCurrentProcess() = original_command_line_;
+}
+
+CommandLine* ScopedCommandLine::GetProcessCommandLine() {
+  return base::CommandLine::ForCurrentProcess();
+}
+
+}  // namespace test
+}  // namespace base
diff --git a/base/test/scoped_command_line.h b/base/test/scoped_command_line.h
new file mode 100644
index 0000000..dea0c6a
--- /dev/null
+++ b/base/test/scoped_command_line.h
@@ -0,0 +1,34 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_SCOPED_COMMAND_LINE_H_
+#define BASE_TEST_SCOPED_COMMAND_LINE_H_
+
+#include "base/command_line.h"
+
+namespace base {
+namespace test {
+
+// Helper class to restore the original command line at the end of the scope.
+// NOTE: In most unit tests, the command line is automatically restored per
+//       test, so this class is not necessary if the command line applies to
+//       the entire single test.
+class ScopedCommandLine final {
+ public:
+  ScopedCommandLine();
+  ~ScopedCommandLine();
+
+  // Gets the command line for the current process.
+  // NOTE: Do not name this GetCommandLine as this will conflict with Windows's
+  //       GetCommandLine and get renamed to GetCommandLineW.
+  CommandLine* GetProcessCommandLine();
+
+ private:
+  const CommandLine original_command_line_;
+};
+
+}  // namespace test
+}  // namespace base
+
+#endif  // BASE_TEST_SCOPED_COMMAND_LINE_H_
diff --git a/base/test/scoped_mock_time_message_loop_task_runner.cc b/base/test/scoped_mock_time_message_loop_task_runner.cc
new file mode 100644
index 0000000..8e855e5
--- /dev/null
+++ b/base/test/scoped_mock_time_message_loop_task_runner.cc
@@ -0,0 +1,38 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/scoped_mock_time_message_loop_task_runner.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop_current.h"
+#include "base/run_loop.h"
+#include "base/test/test_pending_task.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/time/time.h"
+
+namespace base {
+
+ScopedMockTimeMessageLoopTaskRunner::ScopedMockTimeMessageLoopTaskRunner()
+    : task_runner_(new TestMockTimeTaskRunner),
+      previous_task_runner_(ThreadTaskRunnerHandle::Get()) {
+  DCHECK(MessageLoopCurrent::Get());
+  // To ensure that we process any initialization tasks posted to the
+  // MessageLoop by a test fixture before replacing its TaskRunner.
+  RunLoop().RunUntilIdle();
+  MessageLoopCurrent::Get()->SetTaskRunner(task_runner_);
+}
+
+ScopedMockTimeMessageLoopTaskRunner::~ScopedMockTimeMessageLoopTaskRunner() {
+  DCHECK(previous_task_runner_->RunsTasksInCurrentSequence());
+  DCHECK_EQ(task_runner_, ThreadTaskRunnerHandle::Get());
+  for (auto& pending_task : task_runner_->TakePendingTasks()) {
+    previous_task_runner_->PostDelayedTask(
+        pending_task.location, std::move(pending_task.task),
+        pending_task.GetTimeToRun() - task_runner_->NowTicks());
+  }
+  MessageLoopCurrent::Get()->SetTaskRunner(std::move(previous_task_runner_));
+}
+
+}  // namespace base
diff --git a/base/test/scoped_mock_time_message_loop_task_runner.h b/base/test/scoped_mock_time_message_loop_task_runner.h
new file mode 100644
index 0000000..2a034ee
--- /dev/null
+++ b/base/test/scoped_mock_time_message_loop_task_runner.h
@@ -0,0 +1,45 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_SCOPED_MOCK_TIME_MESSAGE_LOOP_TASK_RUNNER_H_
+#define BASE_TEST_SCOPED_MOCK_TIME_MESSAGE_LOOP_TASK_RUNNER_H_
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/test/test_mock_time_task_runner.h"
+
+namespace base {
+
+class SingleThreadTaskRunner;
+
+// A scoped wrapper around TestMockTimeTaskRunner that replaces
+// MessageLoopCurrent::Get()'s task runner (and consequently
+// ThreadTaskRunnerHandle) with a TestMockTimeTaskRunner and resets it back at
+// the end of its scope.
+//
+// Note: RunLoop() will not work in the scope of a
+// ScopedMockTimeMessageLoopTaskRunner, the underlying TestMockTimeTaskRunner's
+// methods must be used instead to pump tasks.
+//
+// DEPRECATED: Use a TestMockTimeTaskRunner::Type::kBoundToThread instead of a
+// MessageLoop + ScopedMockTimeMessageLoopTaskRunner.
+// TODO(gab): Remove usage of this API and delete it.
+class ScopedMockTimeMessageLoopTaskRunner {
+ public:
+  ScopedMockTimeMessageLoopTaskRunner();
+  ~ScopedMockTimeMessageLoopTaskRunner();
+
+  TestMockTimeTaskRunner* task_runner() { return task_runner_.get(); }
+  TestMockTimeTaskRunner* operator->() { return task_runner_.get(); }
+
+ private:
+  const scoped_refptr<TestMockTimeTaskRunner> task_runner_;
+  scoped_refptr<SingleThreadTaskRunner> previous_task_runner_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedMockTimeMessageLoopTaskRunner);
+};
+
+}  // namespace base
+
+#endif  // BASE_TEST_SCOPED_MOCK_TIME_MESSAGE_LOOP_TASK_RUNNER_H_
diff --git a/base/test/scoped_mock_time_message_loop_task_runner_unittest.cc b/base/test/scoped_mock_time_message_loop_task_runner_unittest.cc
new file mode 100644
index 0000000..b08323d
--- /dev/null
+++ b/base/test/scoped_mock_time_message_loop_task_runner_unittest.cc
@@ -0,0 +1,120 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/scoped_mock_time_message_loop_task_runner.h"
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback_forward.h"
+#include "base/containers/circular_deque.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted.h"
+#include "base/message_loop/message_loop.h"
+#include "base/message_loop/message_loop_current.h"
+#include "base/test/test_mock_time_task_runner.h"
+#include "base/test/test_pending_task.h"
+#include "base/time/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace {
+
+TaskRunner* GetCurrentTaskRunner() {
+  return MessageLoopCurrent::Get()->task_runner().get();
+}
+
+void AssignTrue(bool* out) {
+  *out = true;
+}
+
+// Pops a task from the front of |pending_tasks| and returns it.
+TestPendingTask PopFront(base::circular_deque<TestPendingTask>* pending_tasks) {
+  TestPendingTask task = std::move(pending_tasks->front());
+  pending_tasks->pop_front();
+  return task;
+}
+
+class ScopedMockTimeMessageLoopTaskRunnerTest : public testing::Test {
+ public:
+  ScopedMockTimeMessageLoopTaskRunnerTest()
+      : original_task_runner_(new TestMockTimeTaskRunner()) {
+    MessageLoopCurrent::Get()->SetTaskRunner(original_task_runner_);
+  }
+
+ protected:
+  TestMockTimeTaskRunner* original_task_runner() {
+    return original_task_runner_.get();
+  }
+
+ private:
+  scoped_refptr<TestMockTimeTaskRunner> original_task_runner_;
+
+  MessageLoop message_loop_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedMockTimeMessageLoopTaskRunnerTest);
+};
+
+// Verifies a new TaskRunner is installed while a
+// ScopedMockTimeMessageLoopTaskRunner exists and the previous one is installed
+// after destruction.
+TEST_F(ScopedMockTimeMessageLoopTaskRunnerTest, CurrentTaskRunners) {
+  auto scoped_task_runner_ =
+      std::make_unique<ScopedMockTimeMessageLoopTaskRunner>();
+  EXPECT_EQ(scoped_task_runner_->task_runner(), GetCurrentTaskRunner());
+  scoped_task_runner_.reset();
+  EXPECT_EQ(original_task_runner(), GetCurrentTaskRunner());
+}
+
+TEST_F(ScopedMockTimeMessageLoopTaskRunnerTest,
+       IncompleteTasksAreCopiedToPreviousTaskRunnerAfterDestruction) {
+  auto scoped_task_runner_ =
+      std::make_unique<ScopedMockTimeMessageLoopTaskRunner>();
+
+  bool task_10_has_run = false;
+  bool task_11_has_run = false;
+
+  Closure task_1 = DoNothing();
+  Closure task_2 = DoNothing();
+  Closure task_10 = Bind(&AssignTrue, &task_10_has_run);
+  Closure task_11 = Bind(&AssignTrue, &task_11_has_run);
+
+  constexpr TimeDelta task_1_delay = TimeDelta::FromSeconds(1);
+  constexpr TimeDelta task_2_delay = TimeDelta::FromSeconds(2);
+  constexpr TimeDelta task_10_delay = TimeDelta::FromSeconds(10);
+  constexpr TimeDelta task_11_delay = TimeDelta::FromSeconds(11);
+
+  constexpr TimeDelta step_time_by = TimeDelta::FromSeconds(5);
+
+  GetCurrentTaskRunner()->PostDelayedTask(FROM_HERE, task_1, task_1_delay);
+  GetCurrentTaskRunner()->PostDelayedTask(FROM_HERE, task_2, task_2_delay);
+  GetCurrentTaskRunner()->PostDelayedTask(FROM_HERE, task_10, task_10_delay);
+  GetCurrentTaskRunner()->PostDelayedTask(FROM_HERE, task_11, task_11_delay);
+
+  scoped_task_runner_->task_runner()->FastForwardBy(step_time_by);
+
+  scoped_task_runner_.reset();
+
+  base::circular_deque<TestPendingTask> pending_tasks =
+      original_task_runner()->TakePendingTasks();
+
+  EXPECT_EQ(2U, pending_tasks.size());
+
+  TestPendingTask pending_task = PopFront(&pending_tasks);
+  EXPECT_FALSE(task_10_has_run);
+  std::move(pending_task.task).Run();
+  EXPECT_TRUE(task_10_has_run);
+  EXPECT_EQ(task_10_delay - step_time_by, pending_task.delay);
+
+  pending_task = PopFront(&pending_tasks);
+  EXPECT_FALSE(task_11_has_run);
+  std::move(pending_task.task).Run();
+  EXPECT_TRUE(task_11_has_run);
+  EXPECT_EQ(task_11_delay - step_time_by, pending_task.delay);
+}
+
+}  // namespace
+}  // namespace base
diff --git a/base/test/scoped_path_override.cc b/base/test/scoped_path_override.cc
new file mode 100644
index 0000000..b8cfd4a
--- /dev/null
+++ b/base/test/scoped_path_override.cc
@@ -0,0 +1,40 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/scoped_path_override.h"
+
+#include "base/logging.h"
+#include "base/path_service.h"
+
+namespace base {
+
+ScopedPathOverride::ScopedPathOverride(int key) : key_(key) {
+  bool result = temp_dir_.CreateUniqueTempDir();
+  CHECK(result);
+  result = PathService::Override(key, temp_dir_.GetPath());
+  CHECK(result);
+}
+
+ScopedPathOverride::ScopedPathOverride(int key, const base::FilePath& dir)
+    : key_(key) {
+  bool result = PathService::Override(key, dir);
+  CHECK(result);
+}
+
+ScopedPathOverride::ScopedPathOverride(int key,
+                                       const FilePath& path,
+                                       bool is_absolute,
+                                       bool create)
+    : key_(key) {
+  bool result =
+      PathService::OverrideAndCreateIfNeeded(key, path, is_absolute, create);
+  CHECK(result);
+}
+
+ScopedPathOverride::~ScopedPathOverride() {
+   bool result = PathService::RemoveOverride(key_);
+   CHECK(result) << "The override seems to have been removed already!";
+}
+
+}  // namespace base
diff --git a/base/test/scoped_path_override.h b/base/test/scoped_path_override.h
new file mode 100644
index 0000000..f589149
--- /dev/null
+++ b/base/test/scoped_path_override.h
@@ -0,0 +1,43 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_SCOPED_PATH_OVERRIDE_H_
+#define BASE_TEST_SCOPED_PATH_OVERRIDE_H_
+
+#include "base/files/scoped_temp_dir.h"
+#include "base/macros.h"
+
+namespace base {
+
+class FilePath;
+
+// Sets a path override on construction, and removes it when the object goes out
+// of scope. This class is intended to be used by tests that need to override
+// paths to ensure their overrides are properly handled and reverted when the
+// scope of the test is left.
+class ScopedPathOverride {
+ public:
+  // Contructor that initializes the override to a scoped temp directory.
+  explicit ScopedPathOverride(int key);
+
+  // Constructor that would use a path provided by the user.
+  ScopedPathOverride(int key, const FilePath& dir);
+
+  // See PathService::OverrideAndCreateIfNeeded.
+  ScopedPathOverride(int key,
+                     const FilePath& path,
+                     bool is_absolute,
+                     bool create);
+  ~ScopedPathOverride();
+
+ private:
+  int key_;
+  ScopedTempDir temp_dir_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedPathOverride);
+};
+
+}  // namespace base
+
+#endif  // BASE_TEST_SCOPED_PATH_OVERRIDE_H_
diff --git a/base/test/sequenced_task_runner_test_template.cc b/base/test/sequenced_task_runner_test_template.cc
new file mode 100644
index 0000000..de68492
--- /dev/null
+++ b/base/test/sequenced_task_runner_test_template.cc
@@ -0,0 +1,269 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/sequenced_task_runner_test_template.h"
+
+#include <ostream>
+
+#include "base/location.h"
+
+namespace base {
+
+namespace internal {
+
+TaskEvent::TaskEvent(int i, Type type)
+  : i(i), type(type) {
+}
+
+SequencedTaskTracker::SequencedTaskTracker()
+    : next_post_i_(0),
+      task_end_count_(0),
+      task_end_cv_(&lock_) {
+}
+
+void SequencedTaskTracker::PostWrappedNonNestableTask(
+    SequencedTaskRunner* task_runner,
+    const Closure& task) {
+  AutoLock event_lock(lock_);
+  const int post_i = next_post_i_++;
+  Closure wrapped_task = Bind(&SequencedTaskTracker::RunTask, this,
+                              task, post_i);
+  task_runner->PostNonNestableTask(FROM_HERE, wrapped_task);
+  TaskPosted(post_i);
+}
+
+void SequencedTaskTracker::PostWrappedNestableTask(
+    SequencedTaskRunner* task_runner,
+    const Closure& task) {
+  AutoLock event_lock(lock_);
+  const int post_i = next_post_i_++;
+  Closure wrapped_task = Bind(&SequencedTaskTracker::RunTask, this,
+                              task, post_i);
+  task_runner->PostTask(FROM_HERE, wrapped_task);
+  TaskPosted(post_i);
+}
+
+void SequencedTaskTracker::PostWrappedDelayedNonNestableTask(
+    SequencedTaskRunner* task_runner,
+    const Closure& task,
+    TimeDelta delay) {
+  AutoLock event_lock(lock_);
+  const int post_i = next_post_i_++;
+  Closure wrapped_task = Bind(&SequencedTaskTracker::RunTask, this,
+                              task, post_i);
+  task_runner->PostNonNestableDelayedTask(FROM_HERE, wrapped_task, delay);
+  TaskPosted(post_i);
+}
+
+void SequencedTaskTracker::PostNonNestableTasks(
+    SequencedTaskRunner* task_runner,
+    int task_count) {
+  for (int i = 0; i < task_count; ++i) {
+    PostWrappedNonNestableTask(task_runner, Closure());
+  }
+}
+
+void SequencedTaskTracker::RunTask(const Closure& task, int task_i) {
+  TaskStarted(task_i);
+  if (!task.is_null())
+    task.Run();
+  TaskEnded(task_i);
+}
+
+void SequencedTaskTracker::TaskPosted(int i) {
+  // Caller must own |lock_|.
+  events_.push_back(TaskEvent(i, TaskEvent::POST));
+}
+
+void SequencedTaskTracker::TaskStarted(int i) {
+  AutoLock lock(lock_);
+  events_.push_back(TaskEvent(i, TaskEvent::START));
+}
+
+void SequencedTaskTracker::TaskEnded(int i) {
+  AutoLock lock(lock_);
+  events_.push_back(TaskEvent(i, TaskEvent::END));
+  ++task_end_count_;
+  task_end_cv_.Signal();
+}
+
+const std::vector<TaskEvent>&
+SequencedTaskTracker::GetTaskEvents() const {
+  return events_;
+}
+
+void SequencedTaskTracker::WaitForCompletedTasks(int count) {
+  AutoLock lock(lock_);
+  while (task_end_count_ < count)
+    task_end_cv_.Wait();
+}
+
+SequencedTaskTracker::~SequencedTaskTracker() = default;
+
+void PrintTo(const TaskEvent& event, std::ostream* os) {
+  *os << "(i=" << event.i << ", type=";
+  switch (event.type) {
+    case TaskEvent::POST: *os << "POST"; break;
+    case TaskEvent::START: *os << "START"; break;
+    case TaskEvent::END: *os << "END"; break;
+  }
+  *os << ")";
+}
+
+namespace {
+
+// Returns the task ordinals for the task event type |type| in the order that
+// they were recorded.
+std::vector<int> GetEventTypeOrder(const std::vector<TaskEvent>& events,
+                                   TaskEvent::Type type) {
+  std::vector<int> tasks;
+  std::vector<TaskEvent>::const_iterator event;
+  for (event = events.begin(); event != events.end(); ++event) {
+    if (event->type == type)
+      tasks.push_back(event->i);
+  }
+  return tasks;
+}
+
+// Returns all task events for task |task_i|.
+std::vector<TaskEvent::Type> GetEventsForTask(
+    const std::vector<TaskEvent>& events,
+    int task_i) {
+  std::vector<TaskEvent::Type> task_event_orders;
+  std::vector<TaskEvent>::const_iterator event;
+  for (event = events.begin(); event != events.end(); ++event) {
+    if (event->i == task_i)
+      task_event_orders.push_back(event->type);
+  }
+  return task_event_orders;
+}
+
+// Checks that the task events for each task in |events| occur in the order
+// {POST, START, END}, and that there is only one instance of each event type
+// per task.
+::testing::AssertionResult CheckEventOrdersForEachTask(
+    const std::vector<TaskEvent>& events,
+    int task_count) {
+  std::vector<TaskEvent::Type> expected_order;
+  expected_order.push_back(TaskEvent::POST);
+  expected_order.push_back(TaskEvent::START);
+  expected_order.push_back(TaskEvent::END);
+
+  // This is O(n^2), but it runs fast enough currently so is not worth
+  // optimizing.
+  for (int i = 0; i < task_count; ++i) {
+    const std::vector<TaskEvent::Type> task_events =
+        GetEventsForTask(events, i);
+    if (task_events != expected_order) {
+      return ::testing::AssertionFailure()
+          << "Events for task " << i << " are out of order; expected: "
+          << ::testing::PrintToString(expected_order) << "; actual: "
+          << ::testing::PrintToString(task_events);
+    }
+  }
+  return ::testing::AssertionSuccess();
+}
+
+// Checks that no two tasks were running at the same time. I.e. the only
+// events allowed between the START and END of a task are the POSTs of other
+// tasks.
+::testing::AssertionResult CheckNoTaskRunsOverlap(
+    const std::vector<TaskEvent>& events) {
+  // If > -1, we're currently inside a START, END pair.
+  int current_task_i = -1;
+
+  std::vector<TaskEvent>::const_iterator event;
+  for (event = events.begin(); event != events.end(); ++event) {
+    bool spurious_event_found = false;
+
+    if (current_task_i == -1) {  // Not inside a START, END pair.
+      switch (event->type) {
+        case TaskEvent::POST:
+          break;
+        case TaskEvent::START:
+          current_task_i = event->i;
+          break;
+        case TaskEvent::END:
+          spurious_event_found = true;
+          break;
+      }
+
+    } else {  // Inside a START, END pair.
+      bool interleaved_task_detected = false;
+
+      switch (event->type) {
+        case TaskEvent::POST:
+          if (event->i == current_task_i)
+            spurious_event_found = true;
+          break;
+        case TaskEvent::START:
+          interleaved_task_detected = true;
+          break;
+        case TaskEvent::END:
+          if (event->i != current_task_i)
+            interleaved_task_detected = true;
+          else
+            current_task_i = -1;
+          break;
+      }
+
+      if (interleaved_task_detected) {
+        return ::testing::AssertionFailure()
+            << "Found event " << ::testing::PrintToString(*event)
+            << " between START and END events for task " << current_task_i
+            << "; event dump: " << ::testing::PrintToString(events);
+      }
+    }
+
+    if (spurious_event_found) {
+      const int event_i = event - events.begin();
+      return ::testing::AssertionFailure()
+          << "Spurious event " << ::testing::PrintToString(*event)
+          << " at position " << event_i << "; event dump: "
+          << ::testing::PrintToString(events);
+    }
+  }
+
+  return ::testing::AssertionSuccess();
+}
+
+}  // namespace
+
+::testing::AssertionResult CheckNonNestableInvariants(
+    const std::vector<TaskEvent>& events,
+    int task_count) {
+  const std::vector<int> post_order =
+      GetEventTypeOrder(events, TaskEvent::POST);
+  const std::vector<int> start_order =
+      GetEventTypeOrder(events, TaskEvent::START);
+  const std::vector<int> end_order =
+      GetEventTypeOrder(events, TaskEvent::END);
+
+  if (start_order != post_order) {
+    return ::testing::AssertionFailure()
+        << "Expected START order (which equals actual POST order): \n"
+        << ::testing::PrintToString(post_order)
+        << "\n Actual START order:\n"
+        << ::testing::PrintToString(start_order);
+  }
+
+  if (end_order != post_order) {
+    return ::testing::AssertionFailure()
+        << "Expected END order (which equals actual POST order): \n"
+        << ::testing::PrintToString(post_order)
+        << "\n Actual END order:\n"
+        << ::testing::PrintToString(end_order);
+  }
+
+  const ::testing::AssertionResult result =
+      CheckEventOrdersForEachTask(events, task_count);
+  if (!result)
+    return result;
+
+  return CheckNoTaskRunsOverlap(events);
+}
+
+}  // namespace internal
+
+}  // namespace base
diff --git a/base/test/sequenced_task_runner_test_template.h b/base/test/sequenced_task_runner_test_template.h
new file mode 100644
index 0000000..a510030
--- /dev/null
+++ b/base/test/sequenced_task_runner_test_template.h
@@ -0,0 +1,350 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// SequencedTaskRunnerTest defines tests that implementations of
+// SequencedTaskRunner should pass in order to be conformant.
+// See task_runner_test_template.h for a description of how to use the
+// constructs in this file; these work the same.
+
+#ifndef BASE_TEST_SEQUENCED_TASK_RUNNER_TEST_TEMPLATE_H_
+#define BASE_TEST_SEQUENCED_TASK_RUNNER_TEST_TEMPLATE_H_
+
+#include <cstddef>
+#include <iosfwd>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/sequenced_task_runner.h"
+#include "base/synchronization/condition_variable.h"
+#include "base/synchronization/lock.h"
+#include "base/time/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+namespace internal {
+
+struct TaskEvent {
+  enum Type { POST, START, END };
+  TaskEvent(int i, Type type);
+  int i;
+  Type type;
+};
+
+// Utility class used in the tests below.
+class SequencedTaskTracker : public RefCountedThreadSafe<SequencedTaskTracker> {
+ public:
+  SequencedTaskTracker();
+
+  // Posts the non-nestable task |task|, and records its post event.
+  void PostWrappedNonNestableTask(SequencedTaskRunner* task_runner,
+                                  const Closure& task);
+
+  // Posts the nestable task |task|, and records its post event.
+  void PostWrappedNestableTask(SequencedTaskRunner* task_runner,
+                               const Closure& task);
+
+  // Posts the delayed non-nestable task |task|, and records its post event.
+  void PostWrappedDelayedNonNestableTask(SequencedTaskRunner* task_runner,
+                                         const Closure& task,
+                                         TimeDelta delay);
+
+  // Posts |task_count| non-nestable tasks.
+  void PostNonNestableTasks(SequencedTaskRunner* task_runner, int task_count);
+
+  const std::vector<TaskEvent>& GetTaskEvents() const;
+
+  // Returns after the tracker observes a total of |count| task completions.
+  void WaitForCompletedTasks(int count);
+
+ private:
+  friend class RefCountedThreadSafe<SequencedTaskTracker>;
+
+  ~SequencedTaskTracker();
+
+  // A task which runs |task|, recording the start and end events.
+  void RunTask(const Closure& task, int task_i);
+
+  // Records a post event for task |i|. The owner is expected to be holding
+  // |lock_| (unlike |TaskStarted| and |TaskEnded|).
+  void TaskPosted(int i);
+
+  // Records a start event for task |i|.
+  void TaskStarted(int i);
+
+  // Records a end event for task |i|.
+  void TaskEnded(int i);
+
+  // Protects events_, next_post_i_, task_end_count_ and task_end_cv_.
+  Lock lock_;
+
+  // The events as they occurred for each task (protected by lock_).
+  std::vector<TaskEvent> events_;
+
+  // The ordinal to be used for the next task-posting task (protected by
+  // lock_).
+  int next_post_i_;
+
+  // The number of task end events we've received.
+  int task_end_count_;
+  ConditionVariable task_end_cv_;
+
+  DISALLOW_COPY_AND_ASSIGN(SequencedTaskTracker);
+};
+
+void PrintTo(const TaskEvent& event, std::ostream* os);
+
+// Checks the non-nestable task invariants for all tasks in |events|.
+//
+// The invariants are:
+// 1) Events started and ended in the same order that they were posted.
+// 2) Events for an individual tasks occur in the order {POST, START, END},
+//    and there is only one instance of each event type for a task.
+// 3) The only events between a task's START and END events are the POSTs of
+//    other tasks. I.e. tasks were run sequentially, not interleaved.
+::testing::AssertionResult CheckNonNestableInvariants(
+    const std::vector<TaskEvent>& events,
+    int task_count);
+
+}  // namespace internal
+
+template <typename TaskRunnerTestDelegate>
+class SequencedTaskRunnerTest : public testing::Test {
+ protected:
+  SequencedTaskRunnerTest()
+      : task_tracker_(new internal::SequencedTaskTracker()) {}
+
+  const scoped_refptr<internal::SequencedTaskTracker> task_tracker_;
+  TaskRunnerTestDelegate delegate_;
+};
+
+TYPED_TEST_CASE_P(SequencedTaskRunnerTest);
+
+// This test posts N non-nestable tasks in sequence, and expects them to run
+// in FIFO order, with no part of any two tasks' execution
+// overlapping. I.e. that each task starts only after the previously-posted
+// one has finished.
+TYPED_TEST_P(SequencedTaskRunnerTest, SequentialNonNestable) {
+  const int kTaskCount = 1000;
+
+  this->delegate_.StartTaskRunner();
+  const scoped_refptr<SequencedTaskRunner> task_runner =
+      this->delegate_.GetTaskRunner();
+
+  this->task_tracker_->PostWrappedNonNestableTask(
+      task_runner.get(),
+      Bind(&PlatformThread::Sleep, TimeDelta::FromSeconds(1)));
+  for (int i = 1; i < kTaskCount; ++i) {
+    this->task_tracker_->PostWrappedNonNestableTask(task_runner.get(),
+                                                    Closure());
+  }
+
+  this->delegate_.StopTaskRunner();
+
+  EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(),
+                                         kTaskCount));
+}
+
+// This test posts N nestable tasks in sequence. It has the same expectations
+// as SequentialNonNestable because even though the tasks are nestable, they
+// will not be run nestedly in this case.
+TYPED_TEST_P(SequencedTaskRunnerTest, SequentialNestable) {
+  const int kTaskCount = 1000;
+
+  this->delegate_.StartTaskRunner();
+  const scoped_refptr<SequencedTaskRunner> task_runner =
+      this->delegate_.GetTaskRunner();
+
+  this->task_tracker_->PostWrappedNestableTask(
+      task_runner.get(),
+      Bind(&PlatformThread::Sleep, TimeDelta::FromSeconds(1)));
+  for (int i = 1; i < kTaskCount; ++i) {
+    this->task_tracker_->PostWrappedNestableTask(task_runner.get(), Closure());
+  }
+
+  this->delegate_.StopTaskRunner();
+
+  EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(),
+                                         kTaskCount));
+}
+
+// This test posts non-nestable tasks in order of increasing delay, and checks
+// that that the tasks are run in FIFO order and that there is no execution
+// overlap whatsoever between any two tasks.
+TYPED_TEST_P(SequencedTaskRunnerTest, SequentialDelayedNonNestable) {
+  const int kTaskCount = 20;
+  const int kDelayIncrementMs = 50;
+
+  this->delegate_.StartTaskRunner();
+  const scoped_refptr<SequencedTaskRunner> task_runner =
+      this->delegate_.GetTaskRunner();
+
+  for (int i = 0; i < kTaskCount; ++i) {
+    this->task_tracker_->PostWrappedDelayedNonNestableTask(
+        task_runner.get(), Closure(),
+        TimeDelta::FromMilliseconds(kDelayIncrementMs * i));
+  }
+
+  this->task_tracker_->WaitForCompletedTasks(kTaskCount);
+  this->delegate_.StopTaskRunner();
+
+  EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(),
+                                         kTaskCount));
+}
+
+// This test posts a fast, non-nestable task from within each of a number of
+// slow, non-nestable tasks and checks that they all run in the sequence they
+// were posted in and that there is no execution overlap whatsoever.
+TYPED_TEST_P(SequencedTaskRunnerTest, NonNestablePostFromNonNestableTask) {
+  const int kParentCount = 10;
+  const int kChildrenPerParent = 10;
+
+  this->delegate_.StartTaskRunner();
+  const scoped_refptr<SequencedTaskRunner> task_runner =
+      this->delegate_.GetTaskRunner();
+
+  for (int i = 0; i < kParentCount; ++i) {
+    Closure task = Bind(
+        &internal::SequencedTaskTracker::PostNonNestableTasks,
+        this->task_tracker_,
+        RetainedRef(task_runner),
+        kChildrenPerParent);
+    this->task_tracker_->PostWrappedNonNestableTask(task_runner.get(), task);
+  }
+
+  this->delegate_.StopTaskRunner();
+
+  EXPECT_TRUE(CheckNonNestableInvariants(
+      this->task_tracker_->GetTaskEvents(),
+      kParentCount * (kChildrenPerParent + 1)));
+}
+
+// This test posts two tasks with the same delay, and checks that the tasks are
+// run in the order in which they were posted.
+//
+// NOTE: This is actually an approximate test since the API only takes a
+// "delay" parameter, so we are not exactly simulating two tasks that get
+// posted at the exact same time. It would be nice if the API allowed us to
+// specify the desired run time.
+TYPED_TEST_P(SequencedTaskRunnerTest, DelayedTasksSameDelay) {
+  const int kTaskCount = 2;
+  const TimeDelta kDelay = TimeDelta::FromMilliseconds(100);
+
+  this->delegate_.StartTaskRunner();
+  const scoped_refptr<SequencedTaskRunner> task_runner =
+      this->delegate_.GetTaskRunner();
+
+  this->task_tracker_->PostWrappedDelayedNonNestableTask(task_runner.get(),
+                                                         Closure(), kDelay);
+  this->task_tracker_->PostWrappedDelayedNonNestableTask(task_runner.get(),
+                                                         Closure(), kDelay);
+  this->task_tracker_->WaitForCompletedTasks(kTaskCount);
+  this->delegate_.StopTaskRunner();
+
+  EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(),
+                                         kTaskCount));
+}
+
+// This test posts a normal task and a delayed task, and checks that the
+// delayed task runs after the normal task even if the normal task takes
+// a long time to run.
+TYPED_TEST_P(SequencedTaskRunnerTest, DelayedTaskAfterLongTask) {
+  const int kTaskCount = 2;
+
+  this->delegate_.StartTaskRunner();
+  const scoped_refptr<SequencedTaskRunner> task_runner =
+      this->delegate_.GetTaskRunner();
+
+  this->task_tracker_->PostWrappedNonNestableTask(
+      task_runner.get(),
+      base::Bind(&PlatformThread::Sleep, TimeDelta::FromMilliseconds(50)));
+  this->task_tracker_->PostWrappedDelayedNonNestableTask(
+      task_runner.get(), Closure(), TimeDelta::FromMilliseconds(10));
+  this->task_tracker_->WaitForCompletedTasks(kTaskCount);
+  this->delegate_.StopTaskRunner();
+
+  EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(),
+                                         kTaskCount));
+}
+
+// Test that a pile of normal tasks and a delayed task run in the
+// time-to-run order.
+TYPED_TEST_P(SequencedTaskRunnerTest, DelayedTaskAfterManyLongTasks) {
+  const int kTaskCount = 11;
+
+  this->delegate_.StartTaskRunner();
+  const scoped_refptr<SequencedTaskRunner> task_runner =
+      this->delegate_.GetTaskRunner();
+
+  for (int i = 0; i < kTaskCount - 1; i++) {
+    this->task_tracker_->PostWrappedNonNestableTask(
+        task_runner.get(),
+        base::Bind(&PlatformThread::Sleep, TimeDelta::FromMilliseconds(50)));
+  }
+  this->task_tracker_->PostWrappedDelayedNonNestableTask(
+      task_runner.get(), Closure(), TimeDelta::FromMilliseconds(10));
+  this->task_tracker_->WaitForCompletedTasks(kTaskCount);
+  this->delegate_.StopTaskRunner();
+
+  EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(),
+                                         kTaskCount));
+}
+
+
+// TODO(francoisk777@gmail.com) Add a test, similiar to the above, which runs
+// some tasked nestedly (which should be implemented in the test
+// delegate). Also add, to the the test delegate, a predicate which checks
+// whether the implementation supports nested tasks.
+//
+
+// The SequencedTaskRunnerTest test case verifies behaviour that is expected
+// from a sequenced task runner in order to be conformant.
+REGISTER_TYPED_TEST_CASE_P(SequencedTaskRunnerTest,
+                           SequentialNonNestable,
+                           SequentialNestable,
+                           SequentialDelayedNonNestable,
+                           NonNestablePostFromNonNestableTask,
+                           DelayedTasksSameDelay,
+                           DelayedTaskAfterLongTask,
+                           DelayedTaskAfterManyLongTasks);
+
+template <typename TaskRunnerTestDelegate>
+class SequencedTaskRunnerDelayedTest
+    : public SequencedTaskRunnerTest<TaskRunnerTestDelegate> {};
+
+TYPED_TEST_CASE_P(SequencedTaskRunnerDelayedTest);
+
+// This test posts a delayed task, and checks that the task is run later than
+// the specified time.
+TYPED_TEST_P(SequencedTaskRunnerDelayedTest, DelayedTaskBasic) {
+  const int kTaskCount = 1;
+  const TimeDelta kDelay = TimeDelta::FromMilliseconds(100);
+
+  this->delegate_.StartTaskRunner();
+  const scoped_refptr<SequencedTaskRunner> task_runner =
+      this->delegate_.GetTaskRunner();
+
+  Time time_before_run = Time::Now();
+  this->task_tracker_->PostWrappedDelayedNonNestableTask(task_runner.get(),
+                                                         Closure(), kDelay);
+  this->task_tracker_->WaitForCompletedTasks(kTaskCount);
+  this->delegate_.StopTaskRunner();
+  Time time_after_run = Time::Now();
+
+  EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(),
+                                         kTaskCount));
+  EXPECT_LE(kDelay, time_after_run - time_before_run);
+}
+
+// SequencedTaskRunnerDelayedTest tests that the |delay| parameter of
+// is used to actually wait for |delay| ms before executing the task.
+// This is not mandatory for a SequencedTaskRunner to be compliant.
+REGISTER_TYPED_TEST_CASE_P(SequencedTaskRunnerDelayedTest, DelayedTaskBasic);
+
+}  // namespace base
+
+#endif  // BASE_TEST_SEQUENCED_TASK_RUNNER_TEST_TEMPLATE_H_
diff --git a/base/test/task_runner_test_template.cc b/base/test/task_runner_test_template.cc
new file mode 100644
index 0000000..fe70247
--- /dev/null
+++ b/base/test/task_runner_test_template.cc
@@ -0,0 +1,47 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/task_runner_test_template.h"
+
+namespace base {
+
+namespace test {
+
+TaskTracker::TaskTracker() : task_runs_(0), task_runs_cv_(&lock_) {}
+
+TaskTracker::~TaskTracker() = default;
+
+Closure TaskTracker::WrapTask(const Closure& task, int i) {
+  return Bind(&TaskTracker::RunTask, this, task, i);
+}
+
+void TaskTracker::RunTask(const Closure& task, int i) {
+  AutoLock lock(lock_);
+  if (!task.is_null()) {
+    task.Run();
+  }
+  ++task_run_counts_[i];
+  ++task_runs_;
+  task_runs_cv_.Signal();
+}
+
+std::map<int, int> TaskTracker::GetTaskRunCounts() const {
+  AutoLock lock(lock_);
+  return task_run_counts_;
+}
+
+void TaskTracker::WaitForCompletedTasks(int count) {
+  AutoLock lock(lock_);
+  while (task_runs_ < count)
+    task_runs_cv_.Wait();
+}
+
+void ExpectRunsTasksInCurrentSequence(bool expected_value,
+                                      TaskRunner* task_runner) {
+  EXPECT_EQ(expected_value, task_runner->RunsTasksInCurrentSequence());
+}
+
+}  // namespace test
+
+}  // namespace base
diff --git a/base/test/task_runner_test_template.h b/base/test/task_runner_test_template.h
new file mode 100644
index 0000000..4670522
--- /dev/null
+++ b/base/test/task_runner_test_template.h
@@ -0,0 +1,230 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file defines tests that implementations of TaskRunner should
+// pass in order to be conformant, as well as test cases for optional behavior.
+// Here's how you use it to test your implementation.
+//
+// Say your class is called MyTaskRunner.  Then you need to define a
+// class called MyTaskRunnerTestDelegate in my_task_runner_unittest.cc
+// like this:
+//
+//   class MyTaskRunnerTestDelegate {
+//    public:
+//     // Tasks posted to the task runner after this and before
+//     // StopTaskRunner() is called is called should run successfully.
+//     void StartTaskRunner() {
+//       ...
+//     }
+//
+//     // Should return the task runner implementation.  Only called
+//     // after StartTaskRunner and before StopTaskRunner.
+//     scoped_refptr<MyTaskRunner> GetTaskRunner() {
+//       ...
+//     }
+//
+//     // Stop the task runner and make sure all tasks posted before
+//     // this is called are run. Caveat: delayed tasks are not run,
+       // they're simply deleted.
+//     void StopTaskRunner() {
+//       ...
+//     }
+//   };
+//
+// The TaskRunnerTest test harness will have a member variable of
+// this delegate type and will call its functions in the various
+// tests.
+//
+// Then you simply #include this file as well as gtest.h and add the
+// following statement to my_task_runner_unittest.cc:
+//
+//   INSTANTIATE_TYPED_TEST_CASE_P(
+//       MyTaskRunner, TaskRunnerTest, MyTaskRunnerTestDelegate);
+//
+// Easy!
+//
+// The optional test harnesses TaskRunnerAffinityTest can be
+// instanciated in the same way, using the same delegate:
+//
+//   INSTANTIATE_TYPED_TEST_CASE_P(
+//       MyTaskRunner, TaskRunnerAffinityTest, MyTaskRunnerTestDelegate);
+
+
+#ifndef BASE_TEST_TASK_RUNNER_TEST_TEMPLATE_H_
+#define BASE_TEST_TASK_RUNNER_TEST_TEMPLATE_H_
+
+#include <cstddef>
+#include <map>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/location.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/single_thread_task_runner.h"
+#include "base/synchronization/condition_variable.h"
+#include "base/synchronization/lock.h"
+#include "base/task_runner.h"
+#include "base/threading/thread.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+namespace test {
+
+// Utility class that keeps track of how many times particular tasks
+// are run.
+class TaskTracker : public RefCountedThreadSafe<TaskTracker> {
+ public:
+  TaskTracker();
+
+  // Returns a closure that runs the given task and increments the run
+  // count of |i| by one.  |task| may be null.  It is guaranteed that
+  // only one task wrapped by a given tracker will be run at a time.
+  Closure WrapTask(const Closure& task, int i);
+
+  std::map<int, int> GetTaskRunCounts() const;
+
+  // Returns after the tracker observes a total of |count| task completions.
+  void WaitForCompletedTasks(int count);
+
+ private:
+  friend class RefCountedThreadSafe<TaskTracker>;
+
+  ~TaskTracker();
+
+  void RunTask(const Closure& task, int i);
+
+  mutable Lock lock_;
+  std::map<int, int> task_run_counts_;
+  int task_runs_;
+  ConditionVariable task_runs_cv_;
+
+  DISALLOW_COPY_AND_ASSIGN(TaskTracker);
+};
+
+}  // namespace test
+
+template <typename TaskRunnerTestDelegate>
+class TaskRunnerTest : public testing::Test {
+ protected:
+  TaskRunnerTest() : task_tracker_(new test::TaskTracker()) {}
+
+  const scoped_refptr<test::TaskTracker> task_tracker_;
+  TaskRunnerTestDelegate delegate_;
+};
+
+TYPED_TEST_CASE_P(TaskRunnerTest);
+
+// We can't really test much, since TaskRunner provides very few
+// guarantees.
+
+// Post a bunch of tasks to the task runner.  They should all
+// complete.
+TYPED_TEST_P(TaskRunnerTest, Basic) {
+  std::map<int, int> expected_task_run_counts;
+
+  this->delegate_.StartTaskRunner();
+  scoped_refptr<TaskRunner> task_runner = this->delegate_.GetTaskRunner();
+  // Post each ith task i+1 times.
+  for (int i = 0; i < 20; ++i) {
+    const Closure& ith_task = this->task_tracker_->WrapTask(Closure(), i);
+    for (int j = 0; j < i + 1; ++j) {
+      task_runner->PostTask(FROM_HERE, ith_task);
+      ++expected_task_run_counts[i];
+    }
+  }
+  this->delegate_.StopTaskRunner();
+
+  EXPECT_EQ(expected_task_run_counts,
+            this->task_tracker_->GetTaskRunCounts());
+}
+
+// Post a bunch of delayed tasks to the task runner.  They should all
+// complete.
+TYPED_TEST_P(TaskRunnerTest, Delayed) {
+  std::map<int, int> expected_task_run_counts;
+  int expected_total_tasks = 0;
+
+  this->delegate_.StartTaskRunner();
+  scoped_refptr<TaskRunner> task_runner = this->delegate_.GetTaskRunner();
+  // Post each ith task i+1 times with delays from 0-i.
+  for (int i = 0; i < 20; ++i) {
+    const Closure& ith_task = this->task_tracker_->WrapTask(Closure(), i);
+    for (int j = 0; j < i + 1; ++j) {
+      task_runner->PostDelayedTask(
+          FROM_HERE, ith_task, base::TimeDelta::FromMilliseconds(j));
+      ++expected_task_run_counts[i];
+      ++expected_total_tasks;
+    }
+  }
+  this->task_tracker_->WaitForCompletedTasks(expected_total_tasks);
+  this->delegate_.StopTaskRunner();
+
+  EXPECT_EQ(expected_task_run_counts,
+            this->task_tracker_->GetTaskRunCounts());
+}
+
+// The TaskRunnerTest test case verifies behaviour that is expected from a
+// task runner in order to be conformant.
+REGISTER_TYPED_TEST_CASE_P(TaskRunnerTest, Basic, Delayed);
+
+namespace test {
+
+// Calls RunsTasksInCurrentSequence() on |task_runner| and expects it to
+// equal |expected_value|.
+void ExpectRunsTasksInCurrentSequence(bool expected_value,
+                                      TaskRunner* task_runner);
+
+}  // namespace test
+
+template <typename TaskRunnerTestDelegate>
+class TaskRunnerAffinityTest : public TaskRunnerTest<TaskRunnerTestDelegate> {};
+
+TYPED_TEST_CASE_P(TaskRunnerAffinityTest);
+
+// Post a bunch of tasks to the task runner as well as to a separate
+// thread, each checking the value of RunsTasksInCurrentSequence(),
+// which should return true for the tasks posted on the task runner
+// and false for the tasks posted on the separate thread.
+TYPED_TEST_P(TaskRunnerAffinityTest, RunsTasksInCurrentSequence) {
+  std::map<int, int> expected_task_run_counts;
+
+  Thread thread("Non-task-runner thread");
+  ASSERT_TRUE(thread.Start());
+  this->delegate_.StartTaskRunner();
+
+  scoped_refptr<TaskRunner> task_runner = this->delegate_.GetTaskRunner();
+  // Post each ith task i+1 times on the task runner and i+1 times on
+  // the non-task-runner thread.
+  for (int i = 0; i < 20; ++i) {
+    const Closure& ith_task_runner_task = this->task_tracker_->WrapTask(
+        Bind(&test::ExpectRunsTasksInCurrentSequence, true,
+             base::RetainedRef(task_runner)),
+        i);
+    const Closure& ith_non_task_runner_task = this->task_tracker_->WrapTask(
+        Bind(&test::ExpectRunsTasksInCurrentSequence, false,
+             base::RetainedRef(task_runner)),
+        i);
+    for (int j = 0; j < i + 1; ++j) {
+      task_runner->PostTask(FROM_HERE, ith_task_runner_task);
+      thread.task_runner()->PostTask(FROM_HERE, ith_non_task_runner_task);
+      expected_task_run_counts[i] += 2;
+    }
+  }
+
+  this->delegate_.StopTaskRunner();
+  thread.Stop();
+
+  EXPECT_EQ(expected_task_run_counts,
+            this->task_tracker_->GetTaskRunCounts());
+}
+
+// TaskRunnerAffinityTest tests that the TaskRunner implementation
+// can determine if tasks will never be run on a specific thread.
+REGISTER_TYPED_TEST_CASE_P(TaskRunnerAffinityTest, RunsTasksInCurrentSequence);
+
+}  // namespace base
+
+#endif  // BASE_TEST_TASK_RUNNER_TEST_TEMPLATE_H_
diff --git a/base/test/test_discardable_memory_allocator.cc b/base/test/test_discardable_memory_allocator.cc
new file mode 100644
index 0000000..a9bd097
--- /dev/null
+++ b/base/test/test_discardable_memory_allocator.cc
@@ -0,0 +1,61 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/test_discardable_memory_allocator.h"
+
+#include <cstdint>
+#include <cstring>
+
+#include "base/logging.h"
+#include "base/memory/discardable_memory.h"
+#include "base/memory/ptr_util.h"
+
+namespace base {
+namespace {
+
+class DiscardableMemoryImpl : public DiscardableMemory {
+ public:
+  explicit DiscardableMemoryImpl(size_t size)
+      : data_(new uint8_t[size]), size_(size) {}
+
+  // Overridden from DiscardableMemory:
+  bool Lock() override {
+    DCHECK(!is_locked_);
+    is_locked_ = true;
+    return false;
+  }
+
+  void Unlock() override {
+    DCHECK(is_locked_);
+    is_locked_ = false;
+    // Force eviction to catch clients not correctly checking the return value
+    // of Lock().
+    memset(data_.get(), 0, size_);
+  }
+
+  void* data() const override {
+    DCHECK(is_locked_);
+    return data_.get();
+  }
+
+  trace_event::MemoryAllocatorDump* CreateMemoryAllocatorDump(
+      const char* name,
+      trace_event::ProcessMemoryDump* pmd) const override {
+    return nullptr;
+  }
+
+ private:
+  bool is_locked_ = true;
+  std::unique_ptr<uint8_t[]> data_;
+  size_t size_;
+};
+
+}  // namespace
+
+std::unique_ptr<DiscardableMemory>
+TestDiscardableMemoryAllocator::AllocateLockedDiscardableMemory(size_t size) {
+  return std::make_unique<DiscardableMemoryImpl>(size);
+}
+
+}  // namespace base
diff --git a/base/test/test_discardable_memory_allocator.h b/base/test/test_discardable_memory_allocator.h
new file mode 100644
index 0000000..87436e3
--- /dev/null
+++ b/base/test/test_discardable_memory_allocator.h
@@ -0,0 +1,32 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_TEST_DISCARDABLE_MEMORY_ALLOCATOR_H_
+#define BASE_TEST_TEST_DISCARDABLE_MEMORY_ALLOCATOR_H_
+
+#include <stddef.h>
+
+#include "base/macros.h"
+#include "base/memory/discardable_memory_allocator.h"
+
+namespace base {
+
+// TestDiscardableMemoryAllocator is a simple DiscardableMemoryAllocator
+// implementation that can be used for testing. It allocates one-shot
+// DiscardableMemory instances backed by heap memory.
+class TestDiscardableMemoryAllocator : public DiscardableMemoryAllocator {
+ public:
+  constexpr TestDiscardableMemoryAllocator() = default;
+
+  // Overridden from DiscardableMemoryAllocator:
+  std::unique_ptr<DiscardableMemory> AllocateLockedDiscardableMemory(
+      size_t size) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TestDiscardableMemoryAllocator);
+};
+
+}  // namespace base
+
+#endif  // BASE_TEST_TEST_DISCARDABLE_MEMORY_ALLOCATOR_H_
diff --git a/base/test/test_file_util_android.cc b/base/test/test_file_util_android.cc
new file mode 100644
index 0000000..6e93e24
--- /dev/null
+++ b/base/test/test_file_util_android.cc
@@ -0,0 +1,26 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/test_file_util.h"
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "base/files/file_path.h"
+#include "jni/ContentUriTestUtils_jni.h"
+
+using base::android::ScopedJavaLocalRef;
+
+namespace base {
+
+FilePath InsertImageIntoMediaStore(const FilePath& path) {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  ScopedJavaLocalRef<jstring> j_path =
+      base::android::ConvertUTF8ToJavaString(env, path.value());
+  ScopedJavaLocalRef<jstring> j_uri =
+      Java_ContentUriTestUtils_insertImageIntoMediaStore(env, j_path);
+  std::string uri = base::android::ConvertJavaStringToUTF8(j_uri);
+  return FilePath(uri);
+}
+
+}  // namespace base
diff --git a/base/test/test_message_loop.cc b/base/test/test_message_loop.cc
new file mode 100644
index 0000000..bd3610f
--- /dev/null
+++ b/base/test/test_message_loop.cc
@@ -0,0 +1,18 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/run_loop.h"
+#include "base/test/test_message_loop.h"
+
+namespace base {
+
+TestMessageLoop::TestMessageLoop() = default;
+
+TestMessageLoop::TestMessageLoop(MessageLoop::Type type) : loop_(type) {}
+
+TestMessageLoop::~TestMessageLoop() {
+  RunLoop().RunUntilIdle();
+}
+
+}  // namespace base
diff --git a/base/test/test_message_loop.h b/base/test/test_message_loop.h
new file mode 100644
index 0000000..9c0aed8
--- /dev/null
+++ b/base/test/test_message_loop.h
@@ -0,0 +1,34 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_TEST_MESSAGE_LOOP_H_
+#define BASE_TEST_TEST_MESSAGE_LOOP_H_
+
+#include "base/message_loop/message_loop.h"
+
+namespace base {
+
+// TestMessageLoop is a convenience class for unittests that need to create a
+// message loop without a real thread backing it. For most tests,
+// it is sufficient to just instantiate TestMessageLoop as a member variable.
+//
+// TestMessageLoop will attempt to drain the underlying MessageLoop on
+// destruction for clean teardown of tests.
+class TestMessageLoop {
+ public:
+  TestMessageLoop();
+  explicit TestMessageLoop(MessageLoop::Type type);
+  ~TestMessageLoop();
+
+  const scoped_refptr<SingleThreadTaskRunner>& task_runner() {
+    return loop_.task_runner();
+  }
+
+ private:
+  MessageLoop loop_;
+};
+
+}  // namespace base
+
+#endif  // BASE_TEST_TEST_MESSAGE_LOOP_H_
diff --git a/base/test/test_pending_task_unittest.cc b/base/test/test_pending_task_unittest.cc
new file mode 100644
index 0000000..6e01c8c
--- /dev/null
+++ b/base/test/test_pending_task_unittest.cc
@@ -0,0 +1,57 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/test_pending_task.h"
+
+#include "base/bind.h"
+#include "base/trace_event/trace_event.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest-spi.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+TEST(TestPendingTaskTest, TraceSupport) {
+  base::TestPendingTask task;
+
+  // Check that TestPendingTask can be sent to the trace subsystem.
+  TRACE_EVENT1("test", "TestPendingTask::TraceSupport", "task", task.AsValue());
+
+  // Just a basic check that the trace output has *something* in it.
+  std::unique_ptr<base::trace_event::ConvertableToTraceFormat> task_value(
+      task.AsValue());
+  EXPECT_THAT(task_value->ToString(), ::testing::HasSubstr("post_time"));
+}
+
+TEST(TestPendingTaskTest, ToString) {
+  base::TestPendingTask task;
+
+  // Just a basic check that ToString has *something* in it.
+  EXPECT_THAT(task.ToString(), ::testing::StartsWith("TestPendingTask("));
+}
+
+TEST(TestPendingTaskTest, GTestPrettyPrint) {
+  base::TestPendingTask task;
+
+  // Check that gtest is calling the TestPendingTask's PrintTo method.
+  EXPECT_THAT(::testing::PrintToString(task),
+              ::testing::StartsWith("TestPendingTask("));
+
+  // Check that pretty printing works with the gtest iostreams operator.
+  EXPECT_NONFATAL_FAILURE(EXPECT_TRUE(false) << task, "TestPendingTask(");
+}
+
+TEST(TestPendingTaskTest, ShouldRunBefore) {
+  base::TestPendingTask task_first;
+  task_first.delay = base::TimeDelta::FromMilliseconds(1);
+  base::TestPendingTask task_after;
+  task_after.delay = base::TimeDelta::FromMilliseconds(2);
+
+  EXPECT_FALSE(task_after.ShouldRunBefore(task_first))
+      << task_after << ".ShouldRunBefore(" << task_first << ")\n";
+  EXPECT_TRUE(task_first.ShouldRunBefore(task_after))
+      << task_first << ".ShouldRunBefore(" << task_after << ")\n";
+}
+
+}  // namespace base
diff --git a/base/test/test_shared_library.cc b/base/test/test_shared_library.cc
new file mode 100644
index 0000000..99c0467
--- /dev/null
+++ b/base/test/test_shared_library.cc
@@ -0,0 +1,30 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/native_library_test_utils.h"
+
+extern "C" {
+
+int NATIVE_LIBRARY_TEST_ALWAYS_EXPORT GetExportedValue() {
+  return g_native_library_exported_value;
+}
+
+void NATIVE_LIBRARY_TEST_ALWAYS_EXPORT SetExportedValue(int value) {
+  g_native_library_exported_value = value;
+}
+
+// A test function used only to verify basic dynamic symbol resolution.
+int NATIVE_LIBRARY_TEST_ALWAYS_EXPORT GetSimpleTestValue() {
+  return 5;
+}
+
+// When called by |NativeLibraryTest.LoadLibraryPreferOwnSymbols|, this should
+// forward to the local definition of NativeLibraryTestIncrement(), even though
+// the test module also links in the native_library_test_utils source library
+// which exports it.
+int NATIVE_LIBRARY_TEST_ALWAYS_EXPORT GetIncrementValue() {
+  return NativeLibraryTestIncrement();
+}
+
+}  // extern "C"
diff --git a/base/test/test_suite.cc b/base/test/test_suite.cc
new file mode 100644
index 0000000..1862940
--- /dev/null
+++ b/base/test/test_suite.cc
@@ -0,0 +1,486 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/test_suite.h"
+
+#include <signal.h>
+
+#include <memory>
+
+#include "base/at_exit.h"
+#include "base/base_paths.h"
+#include "base/base_switches.h"
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/debug/debugger.h"
+#include "base/debug/profiler.h"
+#include "base/debug/stack_trace.h"
+#include "base/feature_list.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/i18n/icu_util.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/path_service.h"
+#include "base/process/launch.h"
+#include "base/process/memory.h"
+#include "base/test/gtest_xml_unittest_result_printer.h"
+#include "base/test/gtest_xml_util.h"
+#include "base/test/icu_test_util.h"
+#include "base/test/launcher/unit_test_launcher.h"
+#include "base/test/multiprocess_test.h"
+#include "base/test/test_switches.h"
+#include "base/test/test_timeouts.h"
+#include "base/time/time.h"
+#include "build/build_config.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/multiprocess_func_list.h"
+
+#if defined(OS_MACOSX)
+#include "base/mac/scoped_nsautorelease_pool.h"
+#if defined(OS_IOS)
+#include "base/test/test_listener_ios.h"
+#endif  // OS_IOS
+#endif  // OS_MACOSX
+
+#if !defined(OS_WIN)
+#include "base/i18n/rtl.h"
+#if !defined(OS_IOS)
+#include "base/strings/string_util.h"
+#include "third_party/icu/source/common/unicode/uloc.h"
+#endif
+#endif
+
+#if defined(OS_ANDROID)
+#include "base/test/test_support_android.h"
+#endif
+
+#if defined(OS_IOS)
+#include "base/test/test_support_ios.h"
+#endif
+
+#if defined(OS_LINUX)
+#include "base/test/fontconfig_util_linux.h"
+#endif
+
+namespace base {
+
+namespace {
+
+class MaybeTestDisabler : public testing::EmptyTestEventListener {
+ public:
+  void OnTestStart(const testing::TestInfo& test_info) override {
+    ASSERT_FALSE(TestSuite::IsMarkedMaybe(test_info))
+        << "Probably the OS #ifdefs don't include all of the necessary "
+           "platforms.\nPlease ensure that no tests have the MAYBE_ prefix "
+           "after the code is preprocessed.";
+  }
+};
+
+class TestClientInitializer : public testing::EmptyTestEventListener {
+ public:
+  TestClientInitializer()
+      : old_command_line_(CommandLine::NO_PROGRAM) {
+  }
+
+  void OnTestStart(const testing::TestInfo& test_info) override {
+    old_command_line_ = *CommandLine::ForCurrentProcess();
+  }
+
+  void OnTestEnd(const testing::TestInfo& test_info) override {
+    *CommandLine::ForCurrentProcess() = old_command_line_;
+  }
+
+ private:
+  CommandLine old_command_line_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestClientInitializer);
+};
+
+std::string GetProfileName() {
+  static const char kDefaultProfileName[] = "test-profile-{pid}";
+  CR_DEFINE_STATIC_LOCAL(std::string, profile_name, ());
+  if (profile_name.empty()) {
+    const base::CommandLine& command_line =
+        *base::CommandLine::ForCurrentProcess();
+    if (command_line.HasSwitch(switches::kProfilingFile))
+      profile_name = command_line.GetSwitchValueASCII(switches::kProfilingFile);
+    else
+      profile_name = std::string(kDefaultProfileName);
+  }
+  return profile_name;
+}
+
+void InitializeLogging() {
+#if defined(OS_ANDROID)
+  InitAndroidTestLogging();
+#else
+  FilePath exe;
+  PathService::Get(FILE_EXE, &exe);
+  FilePath log_filename = exe.ReplaceExtension(FILE_PATH_LITERAL("log"));
+  logging::LoggingSettings settings;
+  settings.logging_dest = logging::LOG_TO_ALL;
+  settings.log_file = log_filename.value().c_str();
+  settings.delete_old = logging::DELETE_OLD_LOG_FILE;
+  logging::InitLogging(settings);
+  // We want process and thread IDs because we may have multiple processes.
+  // Note: temporarily enabled timestamps in an effort to catch bug 6361.
+  logging::SetLogItems(true, true, true, true);
+#endif  // !defined(OS_ANDROID)
+}
+
+}  // namespace
+
+int RunUnitTestsUsingBaseTestSuite(int argc, char **argv) {
+  TestSuite test_suite(argc, argv);
+  return LaunchUnitTests(argc, argv,
+                         Bind(&TestSuite::Run, Unretained(&test_suite)));
+}
+
+TestSuite::TestSuite(int argc, char** argv) : initialized_command_line_(false) {
+  PreInitialize();
+  InitializeFromCommandLine(argc, argv);
+  // Logging must be initialized before any thread has a chance to call logging
+  // functions.
+  InitializeLogging();
+}
+
+#if defined(OS_WIN)
+TestSuite::TestSuite(int argc, wchar_t** argv)
+    : initialized_command_line_(false) {
+  PreInitialize();
+  InitializeFromCommandLine(argc, argv);
+  // Logging must be initialized before any thread has a chance to call logging
+  // functions.
+  InitializeLogging();
+}
+#endif  // defined(OS_WIN)
+
+TestSuite::~TestSuite() {
+  if (initialized_command_line_)
+    CommandLine::Reset();
+}
+
+void TestSuite::InitializeFromCommandLine(int argc, char** argv) {
+  initialized_command_line_ = CommandLine::Init(argc, argv);
+  testing::InitGoogleTest(&argc, argv);
+  testing::InitGoogleMock(&argc, argv);
+
+#if defined(OS_IOS)
+  InitIOSRunHook(this, argc, argv);
+#endif
+}
+
+#if defined(OS_WIN)
+void TestSuite::InitializeFromCommandLine(int argc, wchar_t** argv) {
+  // Windows CommandLine::Init ignores argv anyway.
+  initialized_command_line_ = CommandLine::Init(argc, NULL);
+  testing::InitGoogleTest(&argc, argv);
+  testing::InitGoogleMock(&argc, argv);
+}
+#endif  // defined(OS_WIN)
+
+void TestSuite::PreInitialize() {
+#if defined(OS_WIN)
+  testing::GTEST_FLAG(catch_exceptions) = false;
+#endif
+  EnableTerminationOnHeapCorruption();
+#if defined(OS_LINUX) && defined(USE_AURA)
+  // When calling native char conversion functions (e.g wrctomb) we need to
+  // have the locale set. In the absence of such a call the "C" locale is the
+  // default. In the gtk code (below) gtk_init() implicitly sets a locale.
+  setlocale(LC_ALL, "");
+#endif  // defined(OS_LINUX) && defined(USE_AURA)
+
+  // On Android, AtExitManager is created in
+  // testing/android/native_test_wrapper.cc before main() is called.
+#if !defined(OS_ANDROID)
+  at_exit_manager_.reset(new AtExitManager);
+#endif
+
+  // Don't add additional code to this function.  Instead add it to
+  // Initialize().  See bug 6436.
+}
+
+
+// static
+bool TestSuite::IsMarkedMaybe(const testing::TestInfo& test) {
+  return strncmp(test.name(), "MAYBE_", 6) == 0;
+}
+
+void TestSuite::CatchMaybeTests() {
+  testing::TestEventListeners& listeners =
+      testing::UnitTest::GetInstance()->listeners();
+  listeners.Append(new MaybeTestDisabler);
+}
+
+void TestSuite::ResetCommandLine() {
+  testing::TestEventListeners& listeners =
+      testing::UnitTest::GetInstance()->listeners();
+  listeners.Append(new TestClientInitializer);
+}
+
+void TestSuite::AddTestLauncherResultPrinter() {
+  // Only add the custom printer if requested.
+  if (!CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kTestLauncherOutput)) {
+    return;
+  }
+
+  FilePath output_path(CommandLine::ForCurrentProcess()->GetSwitchValuePath(
+      switches::kTestLauncherOutput));
+
+  // Do not add the result printer if output path already exists. It's an
+  // indicator there is a process printing to that file, and we're likely
+  // its child. Do not clobber the results in that case.
+  if (PathExists(output_path)) {
+    LOG(WARNING) << "Test launcher output path " << output_path.AsUTF8Unsafe()
+                 << " exists. Not adding test launcher result printer.";
+    return;
+  }
+
+  printer_ = new XmlUnitTestResultPrinter;
+  CHECK(printer_->Initialize(output_path))
+      << "Output path is " << output_path.AsUTF8Unsafe()
+      << " and PathExists(output_path) is " << PathExists(output_path);
+  testing::TestEventListeners& listeners =
+      testing::UnitTest::GetInstance()->listeners();
+  listeners.Append(printer_);
+}
+
+// Don't add additional code to this method.  Instead add it to
+// Initialize().  See bug 6436.
+int TestSuite::Run() {
+#if defined(OS_IOS)
+  RunTestsFromIOSApp();
+#endif
+
+#if defined(OS_MACOSX)
+  mac::ScopedNSAutoreleasePool scoped_pool;
+#endif
+
+  Initialize();
+  std::string client_func =
+      CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+          switches::kTestChildProcess);
+
+  // Check to see if we are being run as a client process.
+  if (!client_func.empty())
+    return multi_process_function_list::InvokeChildProcessTest(client_func);
+#if defined(OS_IOS)
+  test_listener_ios::RegisterTestEndListener();
+#endif
+
+  int result = RUN_ALL_TESTS();
+
+#if defined(OS_MACOSX)
+  // This MUST happen before Shutdown() since Shutdown() tears down
+  // objects (such as NotificationService::current()) that Cocoa
+  // objects use to remove themselves as observers.
+  scoped_pool.Recycle();
+#endif
+
+  Shutdown();
+
+  return result;
+}
+
+void TestSuite::UnitTestAssertHandler(const char* file,
+                                      int line,
+                                      const base::StringPiece summary,
+                                      const base::StringPiece stack_trace) {
+#if defined(OS_ANDROID)
+  // Correlating test stdio with logcat can be difficult, so we emit this
+  // helpful little hint about what was running.  Only do this for Android
+  // because other platforms don't separate out the relevant logs in the same
+  // way.
+  const ::testing::TestInfo* const test_info =
+      ::testing::UnitTest::GetInstance()->current_test_info();
+  if (test_info) {
+    LOG(ERROR) << "Currently running: " << test_info->test_case_name() << "."
+               << test_info->name();
+    fflush(stderr);
+  }
+#endif  // defined(OS_ANDROID)
+
+  // XmlUnitTestResultPrinter inherits gtest format, where assert has summary
+  // and message. In GTest, summary is just a logged text, and message is a
+  // logged text, concatenated with stack trace of assert.
+  // Concatenate summary and stack_trace here, to pass it as a message.
+  if (printer_) {
+    const std::string summary_str = summary.as_string();
+    const std::string stack_trace_str = summary_str + stack_trace.as_string();
+    printer_->OnAssert(file, line, summary_str, stack_trace_str);
+  }
+
+  // The logging system actually prints the message before calling the assert
+  // handler. Just exit now to avoid printing too many stack traces.
+  _exit(1);
+}
+
+#if defined(OS_WIN)
+namespace {
+
+// Disable optimizations to prevent function folding or other transformations
+// that will make the call stacks on failures more confusing.
+#pragma optimize("", off)
+// Handlers for invalid parameter, pure call, and abort. They generate a
+// breakpoint to ensure that we get a call stack on these failures.
+void InvalidParameter(const wchar_t* expression,
+                      const wchar_t* function,
+                      const wchar_t* file,
+                      unsigned int line,
+                      uintptr_t reserved) {
+  // CRT printed message is sufficient.
+  __debugbreak();
+  _exit(1);
+}
+
+void PureCall() {
+  fprintf(stderr, "Pure-virtual function call. Terminating.\n");
+  __debugbreak();
+  _exit(1);
+}
+
+void AbortHandler(int signal) {
+  // Print EOL after the CRT abort message.
+  fprintf(stderr, "\n");
+  __debugbreak();
+}
+#pragma optimize("", on)
+
+}  // namespace
+#endif
+
+void TestSuite::SuppressErrorDialogs() {
+#if defined(OS_WIN)
+  UINT new_flags = SEM_FAILCRITICALERRORS |
+                   SEM_NOGPFAULTERRORBOX |
+                   SEM_NOOPENFILEERRORBOX;
+
+  // Preserve existing error mode, as discussed at
+  // http://blogs.msdn.com/oldnewthing/archive/2004/07/27/198410.aspx
+  UINT existing_flags = SetErrorMode(new_flags);
+  SetErrorMode(existing_flags | new_flags);
+
+#if defined(_DEBUG)
+  // Suppress the "Debug Assertion Failed" dialog.
+  // TODO(hbono): remove this code when gtest has it.
+  // http://groups.google.com/d/topic/googletestframework/OjuwNlXy5ac/discussion
+  _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
+  _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
+  _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
+  _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
+#endif  // defined(_DEBUG)
+
+  // See crbug.com/783040 for test code to trigger all of these failures.
+  _set_invalid_parameter_handler(InvalidParameter);
+  _set_purecall_handler(PureCall);
+  signal(SIGABRT, AbortHandler);
+#endif  // defined(OS_WIN)
+}
+
+void TestSuite::Initialize() {
+  const CommandLine* command_line = CommandLine::ForCurrentProcess();
+#if !defined(OS_IOS)
+  if (command_line->HasSwitch(switches::kWaitForDebugger)) {
+    debug::WaitForDebugger(60, true);
+  }
+#endif
+  // Set up a FeatureList instance, so that code using that API will not hit a
+  // an error that it's not set. It will be cleared automatically.
+  // TestFeatureForBrowserTest1 and TestFeatureForBrowserTest2 used in
+  // ContentBrowserTestScopedFeatureListTest to ensure ScopedFeatureList keeps
+  // features from command line.
+  std::string enabled =
+      command_line->GetSwitchValueASCII(switches::kEnableFeatures);
+  std::string disabled =
+      command_line->GetSwitchValueASCII(switches::kDisableFeatures);
+  enabled += ",TestFeatureForBrowserTest1";
+  disabled += ",TestFeatureForBrowserTest2";
+  scoped_feature_list_.InitFromCommandLine(enabled, disabled);
+
+  // The enable-features and disable-features flags were just slurped into a
+  // FeatureList, so remove them from the command line. Tests should enable and
+  // disable features via the ScopedFeatureList API rather than command-line
+  // flags.
+  CommandLine new_command_line(command_line->GetProgram());
+  CommandLine::SwitchMap switches = command_line->GetSwitches();
+
+  switches.erase(switches::kEnableFeatures);
+  switches.erase(switches::kDisableFeatures);
+
+  for (const auto& iter : switches)
+    new_command_line.AppendSwitchNative(iter.first, iter.second);
+
+  *CommandLine::ForCurrentProcess() = new_command_line;
+
+#if defined(OS_IOS)
+  InitIOSTestMessageLoop();
+#endif  // OS_IOS
+
+#if defined(OS_ANDROID)
+  InitAndroidTestMessageLoop();
+#endif  // else defined(OS_ANDROID)
+
+  CHECK(debug::EnableInProcessStackDumping());
+#if defined(OS_WIN)
+  RouteStdioToConsole(true);
+  // Make sure we run with high resolution timer to minimize differences
+  // between production code and test code.
+  Time::EnableHighResolutionTimer(true);
+#endif  // defined(OS_WIN)
+
+  // In some cases, we do not want to see standard error dialogs.
+  if (!debug::BeingDebugged() &&
+      !command_line->HasSwitch("show-error-dialogs")) {
+    SuppressErrorDialogs();
+    debug::SetSuppressDebugUI(true);
+    assert_handler_ = std::make_unique<logging::ScopedLogAssertHandler>(
+        base::Bind(&TestSuite::UnitTestAssertHandler, base::Unretained(this)));
+  }
+
+  base::test::InitializeICUForTesting();
+
+  // On the Mac OS X command line, the default locale is *_POSIX. In Chromium,
+  // the locale is set via an OS X locale API and is never *_POSIX.
+  // Some tests (such as those involving word break iterator) will behave
+  // differently and fail if we use *POSIX locale. Setting it to en_US here
+  // does not affect tests that explicitly overrides the locale for testing.
+  // This can be an issue on all platforms other than Windows.
+  // TODO(jshin): Should we set the locale via an OS X locale API here?
+#if !defined(OS_WIN)
+#if defined(OS_IOS)
+  i18n::SetICUDefaultLocale("en_US");
+#else
+  std::string default_locale(uloc_getDefault());
+  if (EndsWith(default_locale, "POSIX", CompareCase::INSENSITIVE_ASCII))
+    i18n::SetICUDefaultLocale("en_US");
+#endif
+#endif
+
+#if defined(OS_LINUX)
+  // TODO(thomasanderson): Call TearDownFontconfig() in Shutdown().  It would
+  // currently crash because of leaked FcFontSet's in font_fallback_linux.cc.
+  SetUpFontconfig();
+#endif
+
+  CatchMaybeTests();
+  ResetCommandLine();
+  AddTestLauncherResultPrinter();
+
+  TestTimeouts::Initialize();
+
+  trace_to_file_.BeginTracingFromCommandLineOptions();
+
+  base::debug::StartProfiling(GetProfileName());
+}
+
+void TestSuite::Shutdown() {
+  base::debug::StopProfiling();
+}
+
+}  // namespace base
diff --git a/base/test/test_suite.h b/base/test/test_suite.h
new file mode 100644
index 0000000..6d852ba
--- /dev/null
+++ b/base/test/test_suite.h
@@ -0,0 +1,103 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_TEST_SUITE_H_
+#define BASE_TEST_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 <memory>
+#include <string>
+
+#include "base/at_exit.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/test/scoped_feature_list.h"
+#include "base/test/trace_to_file.h"
+#include "build/build_config.h"
+
+namespace testing {
+class TestInfo;
+}
+
+namespace base {
+
+class XmlUnitTestResultPrinter;
+
+// Instantiates TestSuite, runs it and returns exit code.
+int RunUnitTestsUsingBaseTestSuite(int argc, char **argv);
+
+class TestSuite {
+ public:
+  // Match function used by the GetTestCount method.
+  typedef bool (*TestMatch)(const testing::TestInfo&);
+
+  TestSuite(int argc, char** argv);
+#if defined(OS_WIN)
+  TestSuite(int argc, wchar_t** argv);
+#endif  // defined(OS_WIN)
+  virtual ~TestSuite();
+
+  // Returns true if the test is marked as "MAYBE_".
+  // When using different prefixes depending on platform, we use MAYBE_ and
+  // preprocessor directives to replace MAYBE_ with the target prefix.
+  static bool IsMarkedMaybe(const testing::TestInfo& test);
+
+  void CatchMaybeTests();
+
+  void ResetCommandLine();
+
+  void AddTestLauncherResultPrinter();
+
+  int Run();
+
+ protected:
+  // By default fatal log messages (e.g. from DCHECKs) result in error dialogs
+  // which gum up buildbots. Use a minimalistic assert handler which just
+  // terminates the process.
+  void UnitTestAssertHandler(const char* file,
+                             int line,
+                             const base::StringPiece summary,
+                             const base::StringPiece stack_trace);
+
+  // Disable crash dialogs so that it doesn't gum up the buildbot
+  virtual void SuppressErrorDialogs();
+
+  // Override these for custom initialization and shutdown handling.  Use these
+  // instead of putting complex code in your constructor/destructor.
+
+  virtual void Initialize();
+  virtual void Shutdown();
+
+  // Make sure that we setup an AtExitManager so Singleton objects will be
+  // destroyed.
+  std::unique_ptr<base::AtExitManager> at_exit_manager_;
+
+ private:
+  void InitializeFromCommandLine(int argc, char** argv);
+#if defined(OS_WIN)
+  void InitializeFromCommandLine(int argc, wchar_t** argv);
+#endif  // defined(OS_WIN)
+
+  // Basic initialization for the test suite happens here.
+  void PreInitialize();
+
+  test::TraceToFile trace_to_file_;
+
+  bool initialized_command_line_;
+
+  test::ScopedFeatureList scoped_feature_list_;
+
+  XmlUnitTestResultPrinter* printer_ = nullptr;
+
+  std::unique_ptr<logging::ScopedLogAssertHandler> assert_handler_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestSuite);
+};
+
+}  // namespace base
+
+#endif  // BASE_TEST_TEST_SUITE_H_
diff --git a/base/test/test_support_android.cc b/base/test/test_support_android.cc
new file mode 100644
index 0000000..d5b656a
--- /dev/null
+++ b/base/test/test_support_android.cc
@@ -0,0 +1,225 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdarg.h>
+#include <string.h>
+
+#include "base/android/path_utils.h"
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/singleton.h"
+#include "base/message_loop/message_loop.h"
+#include "base/message_loop/message_pump_android.h"
+#include "base/path_service.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/test/multiprocess_test.h"
+
+namespace {
+
+base::FilePath* g_test_data_dir = nullptr;
+
+struct RunState {
+  RunState(base::MessagePump::Delegate* delegate, int run_depth)
+      : delegate(delegate),
+        run_depth(run_depth),
+        should_quit(false) {
+  }
+
+  base::MessagePump::Delegate* delegate;
+
+  // Used to count how many Run() invocations are on the stack.
+  int run_depth;
+
+  // Used to flag that the current Run() invocation should return ASAP.
+  bool should_quit;
+};
+
+RunState* g_state = nullptr;
+
+// A singleton WaitableEvent wrapper so we avoid a busy loop in
+// MessagePumpForUIStub. Other platforms use the native event loop which blocks
+// when there are no pending messages.
+class Waitable {
+ public:
+  static Waitable* GetInstance() {
+    return base::Singleton<Waitable,
+                           base::LeakySingletonTraits<Waitable>>::get();
+  }
+
+  // Signals that there are more work to do.
+  void Signal() { waitable_event_.Signal(); }
+
+  // Blocks until more work is scheduled.
+  void Block() { waitable_event_.Wait(); }
+
+  void Quit() {
+    g_state->should_quit = true;
+    Signal();
+  }
+
+ private:
+  friend struct base::DefaultSingletonTraits<Waitable>;
+
+  Waitable()
+      : waitable_event_(base::WaitableEvent::ResetPolicy::AUTOMATIC,
+                        base::WaitableEvent::InitialState::NOT_SIGNALED) {}
+
+  base::WaitableEvent waitable_event_;
+
+  DISALLOW_COPY_AND_ASSIGN(Waitable);
+};
+
+// The MessagePumpForUI implementation for test purpose.
+class MessagePumpForUIStub : public base::MessagePumpForUI {
+ public:
+  MessagePumpForUIStub() : base::MessagePumpForUI() { Waitable::GetInstance(); }
+  ~MessagePumpForUIStub() override {}
+
+  bool IsTestImplementation() const override { return true; }
+
+  // In tests, there isn't a native thread, as such RunLoop::Run() should be
+  // used to run the loop instead of attaching and delegating to the native
+  // loop. As such, this override ignores the Attach() request.
+  void Attach(base::MessagePump::Delegate* delegate) override {}
+
+  void Run(base::MessagePump::Delegate* delegate) override {
+    // The following was based on message_pump_glib.cc, except we're using a
+    // WaitableEvent since there are no native message loop to use.
+    RunState state(delegate, g_state ? g_state->run_depth + 1 : 1);
+
+    RunState* previous_state = g_state;
+    g_state = &state;
+
+    // When not nested we can use the real implementation, otherwise fall back
+    // to the stub implementation.
+    if (g_state->run_depth > 1) {
+      RunNested(delegate);
+    } else {
+      MessagePumpForUI::Run(delegate);
+    }
+
+    g_state = previous_state;
+  }
+
+  void RunNested(base::MessagePump::Delegate* delegate) {
+    bool more_work_is_plausible = true;
+
+    for (;;) {
+      if (!more_work_is_plausible) {
+        Waitable::GetInstance()->Block();
+        if (g_state->should_quit)
+          break;
+      }
+
+      more_work_is_plausible = g_state->delegate->DoWork();
+      if (g_state->should_quit)
+        break;
+
+      base::TimeTicks delayed_work_time;
+      more_work_is_plausible |=
+          g_state->delegate->DoDelayedWork(&delayed_work_time);
+      if (g_state->should_quit)
+        break;
+
+      if (more_work_is_plausible)
+        continue;
+
+      more_work_is_plausible = g_state->delegate->DoIdleWork();
+      if (g_state->should_quit)
+        break;
+
+      more_work_is_plausible |= !delayed_work_time.is_null();
+    }
+  }
+
+  void Quit() override {
+    CHECK(g_state);
+    if (g_state->run_depth > 1) {
+      Waitable::GetInstance()->Quit();
+    } else {
+      MessagePumpForUI::Quit();
+    }
+  }
+
+  void ScheduleWork() override {
+    if (g_state && g_state->run_depth > 1) {
+      Waitable::GetInstance()->Signal();
+    } else {
+      MessagePumpForUI::ScheduleWork();
+    }
+  }
+
+  void ScheduleDelayedWork(const base::TimeTicks& delayed_work_time) override {
+    if (g_state && g_state->run_depth > 1) {
+      Waitable::GetInstance()->Signal();
+    } else {
+      MessagePumpForUI::ScheduleDelayedWork(delayed_work_time);
+    }
+  }
+};
+
+std::unique_ptr<base::MessagePump> CreateMessagePumpForUIStub() {
+  return std::unique_ptr<base::MessagePump>(new MessagePumpForUIStub());
+};
+
+// Provides the test path for DIR_SOURCE_ROOT and DIR_ANDROID_APP_DATA.
+bool GetTestProviderPath(int key, base::FilePath* result) {
+  switch (key) {
+    // TODO(agrieve): Stop overriding DIR_ANDROID_APP_DATA.
+    // https://crbug.com/617734
+    // Instead DIR_ASSETS should be used to discover assets file location in
+    // tests.
+    case base::DIR_ANDROID_APP_DATA:
+    case base::DIR_ASSETS:
+    case base::DIR_SOURCE_ROOT:
+      CHECK(g_test_data_dir != nullptr);
+      *result = *g_test_data_dir;
+      return true;
+    default:
+      return false;
+  }
+}
+
+void InitPathProvider(int key) {
+  base::FilePath path;
+  // If failed to override the key, that means the way has not been registered.
+  if (GetTestProviderPath(key, &path) &&
+      !base::PathService::Override(key, path)) {
+    base::PathService::RegisterProvider(&GetTestProviderPath, key, key + 1);
+  }
+}
+
+}  // namespace
+
+namespace base {
+
+void InitAndroidTestLogging() {
+  logging::LoggingSettings settings;
+  settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
+  logging::InitLogging(settings);
+  // To view log output with IDs and timestamps use "adb logcat -v threadtime".
+  logging::SetLogItems(false,    // Process ID
+                       false,    // Thread ID
+                       false,    // Timestamp
+                       false);   // Tick count
+}
+
+void InitAndroidTestPaths(const FilePath& test_data_dir) {
+  if (g_test_data_dir) {
+    CHECK(test_data_dir == *g_test_data_dir);
+    return;
+  }
+  g_test_data_dir = new FilePath(test_data_dir);
+  InitPathProvider(DIR_SOURCE_ROOT);
+  InitPathProvider(DIR_ANDROID_APP_DATA);
+  InitPathProvider(DIR_ASSETS);
+}
+
+void InitAndroidTestMessageLoop() {
+  if (!MessageLoop::InitMessagePumpForUIFactory(&CreateMessagePumpForUIStub))
+    LOG(INFO) << "MessagePumpForUIFactory already set, unable to override.";
+}
+
+}  // namespace base
diff --git a/base/test/test_support_android.h b/base/test/test_support_android.h
new file mode 100644
index 0000000..4942e54
--- /dev/null
+++ b/base/test/test_support_android.h
@@ -0,0 +1,25 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_TEST_SUPPORT_ANDROID_H_
+#define BASE_TEST_TEST_SUPPORT_ANDROID_H_
+
+#include "base/base_export.h"
+
+namespace base {
+
+class FilePath;
+
+// Init logging for tests on Android. Logs will be output into Android's logcat.
+BASE_EXPORT void InitAndroidTestLogging();
+
+// Init path providers for tests on Android.
+BASE_EXPORT void InitAndroidTestPaths(const FilePath& test_data_dir);
+
+// Init the message loop for tests on Android.
+BASE_EXPORT void InitAndroidTestMessageLoop();
+
+}  // namespace base
+
+#endif  // BASE_TEST_TEST_SUPPORT_ANDROID_H_
diff --git a/base/test/test_ui_thread_android.cc b/base/test/test_ui_thread_android.cc
new file mode 100644
index 0000000..d19fefa
--- /dev/null
+++ b/base/test/test_ui_thread_android.cc
@@ -0,0 +1,14 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+#include "base/test/test_ui_thread_android.h"
+
+#include "jni/TestUiThread_jni.h"
+
+namespace base {
+
+void StartTestUiThreadLooper() {
+  Java_TestUiThread_loop(base::android::AttachCurrentThread());
+}
+
+}  // namespace base
diff --git a/base/test/test_ui_thread_android.h b/base/test/test_ui_thread_android.h
new file mode 100644
index 0000000..233911a
--- /dev/null
+++ b/base/test/test_ui_thread_android.h
@@ -0,0 +1,20 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_TEST_UI_THREAD_ANDROID_
+#define BASE_TEST_TEST_UI_THREAD_ANDROID_
+
+#include <jni.h>
+
+namespace base {
+
+// Set up a thread as the Chromium UI Thread, and run its looper. This is is
+// intended for C++ unit tests (e.g. the net unit tests) that don't run with the
+// UI thread as their main looper, but test code that, on Android, uses UI
+// thread events, so need a running UI thread.
+void StartTestUiThreadLooper();
+
+}  // namespace base
+
+#endif  //  BASE_TEST_TEST_UI_THREAD_ANDROID_
diff --git a/base/test/thread_test_helper.cc b/base/test/thread_test_helper.cc
new file mode 100644
index 0000000..f3ca780
--- /dev/null
+++ b/base/test/thread_test_helper.cc
@@ -0,0 +1,41 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/thread_test_helper.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/threading/thread_restrictions.h"
+
+namespace base {
+
+ThreadTestHelper::ThreadTestHelper(
+    scoped_refptr<SequencedTaskRunner> target_sequence)
+    : test_result_(false),
+      target_sequence_(std::move(target_sequence)),
+      done_event_(WaitableEvent::ResetPolicy::AUTOMATIC,
+                  WaitableEvent::InitialState::NOT_SIGNALED) {}
+
+bool ThreadTestHelper::Run() {
+  if (!target_sequence_->PostTask(
+          FROM_HERE, base::BindOnce(&ThreadTestHelper::RunOnSequence, this))) {
+    return false;
+  }
+  base::ThreadRestrictions::ScopedAllowWait allow_wait;
+  done_event_.Wait();
+  return test_result_;
+}
+
+void ThreadTestHelper::RunTest() { set_test_result(true); }
+
+ThreadTestHelper::~ThreadTestHelper() = default;
+
+void ThreadTestHelper::RunOnSequence() {
+  RunTest();
+  done_event_.Signal();
+}
+
+}  // namespace base
diff --git a/base/test/thread_test_helper.h b/base/test/thread_test_helper.h
new file mode 100644
index 0000000..935e7ef
--- /dev/null
+++ b/base/test/thread_test_helper.h
@@ -0,0 +1,50 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_THREAD_TEST_HELPER_H_
+#define BASE_TEST_THREAD_TEST_HELPER_H_
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/sequenced_task_runner.h"
+#include "base/synchronization/waitable_event.h"
+
+namespace base {
+
+// Helper class that executes code on a given target sequence/thread while
+// blocking on the invoking sequence/thread. To use, derive from this class and
+// overwrite RunTest. An alternative use of this class is to use it directly. It
+// will then block until all pending tasks on a given sequence/thread have been
+// executed.
+class ThreadTestHelper : public RefCountedThreadSafe<ThreadTestHelper> {
+ public:
+  explicit ThreadTestHelper(scoped_refptr<SequencedTaskRunner> target_sequence);
+
+  // True if RunTest() was successfully executed on the target sequence.
+  bool Run() WARN_UNUSED_RESULT;
+
+  virtual void RunTest();
+
+ protected:
+  friend class RefCountedThreadSafe<ThreadTestHelper>;
+
+  virtual ~ThreadTestHelper();
+
+  // Use this method to store the result of RunTest().
+  void set_test_result(bool test_result) { test_result_ = test_result; }
+
+ private:
+  void RunOnSequence();
+
+  bool test_result_;
+  scoped_refptr<SequencedTaskRunner> target_sequence_;
+  WaitableEvent done_event_;
+
+  DISALLOW_COPY_AND_ASSIGN(ThreadTestHelper);
+};
+
+}  // namespace base
+
+#endif  // BASE_TEST_THREAD_TEST_HELPER_H_
diff --git a/base/test/trace_event_analyzer.cc b/base/test/trace_event_analyzer.cc
new file mode 100644
index 0000000..cab2899
--- /dev/null
+++ b/base/test/trace_event_analyzer.cc
@@ -0,0 +1,1069 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/trace_event_analyzer.h"
+
+#include <math.h>
+
+#include <algorithm>
+#include <set>
+
+#include "base/json/json_reader.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/run_loop.h"
+#include "base/strings/pattern.h"
+#include "base/trace_event/trace_buffer.h"
+#include "base/trace_event/trace_config.h"
+#include "base/trace_event/trace_log.h"
+#include "base/values.h"
+
+namespace {
+void OnTraceDataCollected(base::OnceClosure quit_closure,
+                          base::trace_event::TraceResultBuffer* buffer,
+                          const scoped_refptr<base::RefCountedString>& json,
+                          bool has_more_events) {
+  buffer->AddFragment(json->data());
+  if (!has_more_events)
+    std::move(quit_closure).Run();
+}
+}  // namespace
+
+namespace trace_analyzer {
+
+// TraceEvent
+
+TraceEvent::TraceEvent()
+    : thread(0, 0),
+      timestamp(0),
+      duration(0),
+      phase(TRACE_EVENT_PHASE_BEGIN),
+      other_event(nullptr) {}
+
+TraceEvent::TraceEvent(TraceEvent&& other) = default;
+
+TraceEvent::~TraceEvent() = default;
+
+TraceEvent& TraceEvent::operator=(TraceEvent&& rhs) = default;
+
+bool TraceEvent::SetFromJSON(const base::Value* event_value) {
+  if (event_value->type() != base::Value::Type::DICTIONARY) {
+    LOG(ERROR) << "Value must be Type::DICTIONARY";
+    return false;
+  }
+  const base::DictionaryValue* dictionary =
+      static_cast<const base::DictionaryValue*>(event_value);
+
+  std::string phase_str;
+  const base::DictionaryValue* args = nullptr;
+
+  if (!dictionary->GetString("ph", &phase_str)) {
+    LOG(ERROR) << "ph is missing from TraceEvent JSON";
+    return false;
+  }
+
+  phase = *phase_str.data();
+
+  bool may_have_duration = (phase == TRACE_EVENT_PHASE_COMPLETE);
+  bool require_origin = (phase != TRACE_EVENT_PHASE_METADATA);
+  bool require_id = (phase == TRACE_EVENT_PHASE_ASYNC_BEGIN ||
+                     phase == TRACE_EVENT_PHASE_ASYNC_STEP_INTO ||
+                     phase == TRACE_EVENT_PHASE_ASYNC_STEP_PAST ||
+                     phase == TRACE_EVENT_PHASE_MEMORY_DUMP ||
+                     phase == TRACE_EVENT_PHASE_ENTER_CONTEXT ||
+                     phase == TRACE_EVENT_PHASE_LEAVE_CONTEXT ||
+                     phase == TRACE_EVENT_PHASE_CREATE_OBJECT ||
+                     phase == TRACE_EVENT_PHASE_DELETE_OBJECT ||
+                     phase == TRACE_EVENT_PHASE_SNAPSHOT_OBJECT ||
+                     phase == TRACE_EVENT_PHASE_ASYNC_END);
+
+  if (require_origin && !dictionary->GetInteger("pid", &thread.process_id)) {
+    LOG(ERROR) << "pid is missing from TraceEvent JSON";
+    return false;
+  }
+  if (require_origin && !dictionary->GetInteger("tid", &thread.thread_id)) {
+    LOG(ERROR) << "tid is missing from TraceEvent JSON";
+    return false;
+  }
+  if (require_origin && !dictionary->GetDouble("ts", &timestamp)) {
+    LOG(ERROR) << "ts is missing from TraceEvent JSON";
+    return false;
+  }
+  if (may_have_duration) {
+    dictionary->GetDouble("dur", &duration);
+  }
+  if (!dictionary->GetString("cat", &category)) {
+    LOG(ERROR) << "cat is missing from TraceEvent JSON";
+    return false;
+  }
+  if (!dictionary->GetString("name", &name)) {
+    LOG(ERROR) << "name is missing from TraceEvent JSON";
+    return false;
+  }
+  if (!dictionary->GetDictionary("args", &args)) {
+    LOG(ERROR) << "args is missing from TraceEvent JSON";
+    return false;
+  }
+  if (require_id && !dictionary->GetString("id", &id)) {
+    LOG(ERROR) << "id is missing from ASYNC_BEGIN/ASYNC_END TraceEvent JSON";
+    return false;
+  }
+
+  dictionary->GetDouble("tdur", &thread_duration);
+  dictionary->GetDouble("tts", &thread_timestamp);
+  dictionary->GetString("scope", &scope);
+  dictionary->GetString("bind_id", &bind_id);
+  dictionary->GetBoolean("flow_out", &flow_out);
+  dictionary->GetBoolean("flow_in", &flow_in);
+
+  const base::DictionaryValue* id2;
+  if (dictionary->GetDictionary("id2", &id2)) {
+    id2->GetString("global", &global_id2);
+    id2->GetString("local", &local_id2);
+  }
+
+  // For each argument, copy the type and create a trace_analyzer::TraceValue.
+  for (base::DictionaryValue::Iterator it(*args); !it.IsAtEnd();
+       it.Advance()) {
+    std::string str;
+    bool boolean = false;
+    int int_num = 0;
+    double double_num = 0.0;
+    if (it.value().GetAsString(&str)) {
+      arg_strings[it.key()] = str;
+    } else if (it.value().GetAsInteger(&int_num)) {
+      arg_numbers[it.key()] = static_cast<double>(int_num);
+    } else if (it.value().GetAsBoolean(&boolean)) {
+      arg_numbers[it.key()] = static_cast<double>(boolean ? 1 : 0);
+    } else if (it.value().GetAsDouble(&double_num)) {
+      arg_numbers[it.key()] = double_num;
+    }
+    // Record all arguments as values.
+    arg_values[it.key()] = it.value().CreateDeepCopy();
+  }
+
+  return true;
+}
+
+double TraceEvent::GetAbsTimeToOtherEvent() const {
+  return fabs(other_event->timestamp - timestamp);
+}
+
+bool TraceEvent::GetArgAsString(const std::string& name,
+                                std::string* arg) const {
+  const auto it = arg_strings.find(name);
+  if (it != arg_strings.end()) {
+    *arg = it->second;
+    return true;
+  }
+  return false;
+}
+
+bool TraceEvent::GetArgAsNumber(const std::string& name,
+                                double* arg) const {
+  const auto it = arg_numbers.find(name);
+  if (it != arg_numbers.end()) {
+    *arg = it->second;
+    return true;
+  }
+  return false;
+}
+
+bool TraceEvent::GetArgAsValue(const std::string& name,
+                               std::unique_ptr<base::Value>* arg) const {
+  const auto it = arg_values.find(name);
+  if (it != arg_values.end()) {
+    *arg = it->second->CreateDeepCopy();
+    return true;
+  }
+  return false;
+}
+
+bool TraceEvent::HasStringArg(const std::string& name) const {
+  return (arg_strings.find(name) != arg_strings.end());
+}
+
+bool TraceEvent::HasNumberArg(const std::string& name) const {
+  return (arg_numbers.find(name) != arg_numbers.end());
+}
+
+bool TraceEvent::HasArg(const std::string& name) const {
+  return (arg_values.find(name) != arg_values.end());
+}
+
+std::string TraceEvent::GetKnownArgAsString(const std::string& name) const {
+  std::string arg_string;
+  bool result = GetArgAsString(name, &arg_string);
+  DCHECK(result);
+  return arg_string;
+}
+
+double TraceEvent::GetKnownArgAsDouble(const std::string& name) const {
+  double arg_double = 0;
+  bool result = GetArgAsNumber(name, &arg_double);
+  DCHECK(result);
+  return arg_double;
+}
+
+int TraceEvent::GetKnownArgAsInt(const std::string& name) const {
+  double arg_double = 0;
+  bool result = GetArgAsNumber(name, &arg_double);
+  DCHECK(result);
+  return static_cast<int>(arg_double);
+}
+
+bool TraceEvent::GetKnownArgAsBool(const std::string& name) const {
+  double arg_double = 0;
+  bool result = GetArgAsNumber(name, &arg_double);
+  DCHECK(result);
+  return (arg_double != 0.0);
+}
+
+std::unique_ptr<base::Value> TraceEvent::GetKnownArgAsValue(
+    const std::string& name) const {
+  std::unique_ptr<base::Value> arg_value;
+  bool result = GetArgAsValue(name, &arg_value);
+  DCHECK(result);
+  return arg_value;
+}
+
+// QueryNode
+
+QueryNode::QueryNode(const Query& query) : query_(query) {
+}
+
+QueryNode::~QueryNode() = default;
+
+// Query
+
+Query::Query(TraceEventMember member)
+    : type_(QUERY_EVENT_MEMBER),
+      operator_(OP_INVALID),
+      member_(member),
+      number_(0),
+      is_pattern_(false) {
+}
+
+Query::Query(TraceEventMember member, const std::string& arg_name)
+    : type_(QUERY_EVENT_MEMBER),
+      operator_(OP_INVALID),
+      member_(member),
+      number_(0),
+      string_(arg_name),
+      is_pattern_(false) {
+}
+
+Query::Query(const Query& query) = default;
+
+Query::~Query() = default;
+
+Query Query::String(const std::string& str) {
+  return Query(str);
+}
+
+Query Query::Double(double num) {
+  return Query(num);
+}
+
+Query Query::Int(int32_t num) {
+  return Query(static_cast<double>(num));
+}
+
+Query Query::Uint(uint32_t num) {
+  return Query(static_cast<double>(num));
+}
+
+Query Query::Bool(bool boolean) {
+  return Query(boolean ? 1.0 : 0.0);
+}
+
+Query Query::Phase(char phase) {
+  return Query(static_cast<double>(phase));
+}
+
+Query Query::Pattern(const std::string& pattern) {
+  Query query(pattern);
+  query.is_pattern_ = true;
+  return query;
+}
+
+bool Query::Evaluate(const TraceEvent& event) const {
+  // First check for values that can convert to bool.
+
+  // double is true if != 0:
+  double bool_value = 0.0;
+  bool is_bool = GetAsDouble(event, &bool_value);
+  if (is_bool)
+    return (bool_value != 0.0);
+
+  // string is true if it is non-empty:
+  std::string str_value;
+  bool is_str = GetAsString(event, &str_value);
+  if (is_str)
+    return !str_value.empty();
+
+  DCHECK_EQ(QUERY_BOOLEAN_OPERATOR, type_)
+      << "Invalid query: missing boolean expression";
+  DCHECK(left_.get());
+  DCHECK(right_.get() || is_unary_operator());
+
+  if (is_comparison_operator()) {
+    DCHECK(left().is_value() && right().is_value())
+        << "Invalid query: comparison operator used between event member and "
+           "value.";
+    bool compare_result = false;
+    if (CompareAsDouble(event, &compare_result))
+      return compare_result;
+    if (CompareAsString(event, &compare_result))
+      return compare_result;
+    return false;
+  }
+  // It's a logical operator.
+  switch (operator_) {
+    case OP_AND:
+      return left().Evaluate(event) && right().Evaluate(event);
+    case OP_OR:
+      return left().Evaluate(event) || right().Evaluate(event);
+    case OP_NOT:
+      return !left().Evaluate(event);
+    default:
+      NOTREACHED();
+      return false;
+  }
+}
+
+bool Query::CompareAsDouble(const TraceEvent& event, bool* result) const {
+  double lhs, rhs;
+  if (!left().GetAsDouble(event, &lhs) || !right().GetAsDouble(event, &rhs))
+    return false;
+  switch (operator_) {
+    case OP_EQ:
+      *result = (lhs == rhs);
+      return true;
+    case OP_NE:
+      *result = (lhs != rhs);
+      return true;
+    case OP_LT:
+      *result = (lhs < rhs);
+      return true;
+    case OP_LE:
+      *result = (lhs <= rhs);
+      return true;
+    case OP_GT:
+      *result = (lhs > rhs);
+      return true;
+    case OP_GE:
+      *result = (lhs >= rhs);
+      return true;
+    default:
+      NOTREACHED();
+      return false;
+  }
+}
+
+bool Query::CompareAsString(const TraceEvent& event, bool* result) const {
+  std::string lhs, rhs;
+  if (!left().GetAsString(event, &lhs) || !right().GetAsString(event, &rhs))
+    return false;
+  switch (operator_) {
+    case OP_EQ:
+      if (right().is_pattern_)
+        *result = base::MatchPattern(lhs, rhs);
+      else if (left().is_pattern_)
+        *result = base::MatchPattern(rhs, lhs);
+      else
+        *result = (lhs == rhs);
+      return true;
+    case OP_NE:
+      if (right().is_pattern_)
+        *result = !base::MatchPattern(lhs, rhs);
+      else if (left().is_pattern_)
+        *result = !base::MatchPattern(rhs, lhs);
+      else
+        *result = (lhs != rhs);
+      return true;
+    case OP_LT:
+      *result = (lhs < rhs);
+      return true;
+    case OP_LE:
+      *result = (lhs <= rhs);
+      return true;
+    case OP_GT:
+      *result = (lhs > rhs);
+      return true;
+    case OP_GE:
+      *result = (lhs >= rhs);
+      return true;
+    default:
+      NOTREACHED();
+      return false;
+  }
+}
+
+bool Query::EvaluateArithmeticOperator(const TraceEvent& event,
+                                       double* num) const {
+  DCHECK_EQ(QUERY_ARITHMETIC_OPERATOR, type_);
+  DCHECK(left_.get());
+  DCHECK(right_.get() || is_unary_operator());
+
+  double lhs = 0, rhs = 0;
+  if (!left().GetAsDouble(event, &lhs))
+    return false;
+  if (!is_unary_operator() && !right().GetAsDouble(event, &rhs))
+    return false;
+
+  switch (operator_) {
+    case OP_ADD:
+      *num = lhs + rhs;
+      return true;
+    case OP_SUB:
+      *num = lhs - rhs;
+      return true;
+    case OP_MUL:
+      *num = lhs * rhs;
+      return true;
+    case OP_DIV:
+      *num = lhs / rhs;
+      return true;
+    case OP_MOD:
+      *num = static_cast<double>(static_cast<int64_t>(lhs) %
+                                 static_cast<int64_t>(rhs));
+      return true;
+    case OP_NEGATE:
+      *num = -lhs;
+      return true;
+    default:
+      NOTREACHED();
+      return false;
+  }
+}
+
+bool Query::GetAsDouble(const TraceEvent& event, double* num) const {
+  switch (type_) {
+    case QUERY_ARITHMETIC_OPERATOR:
+      return EvaluateArithmeticOperator(event, num);
+    case QUERY_EVENT_MEMBER:
+      return GetMemberValueAsDouble(event, num);
+    case QUERY_NUMBER:
+      *num = number_;
+      return true;
+    default:
+      return false;
+  }
+}
+
+bool Query::GetAsString(const TraceEvent& event, std::string* str) const {
+  switch (type_) {
+    case QUERY_EVENT_MEMBER:
+      return GetMemberValueAsString(event, str);
+    case QUERY_STRING:
+      *str = string_;
+      return true;
+    default:
+      return false;
+  }
+}
+
+const TraceEvent* Query::SelectTargetEvent(const TraceEvent* event,
+                                           TraceEventMember member) {
+  if (member >= OTHER_FIRST_MEMBER && member <= OTHER_LAST_MEMBER) {
+    return event->other_event;
+  } else if (member >= PREV_FIRST_MEMBER && member <= PREV_LAST_MEMBER) {
+    return event->prev_event;
+  } else {
+    return event;
+  }
+}
+
+bool Query::GetMemberValueAsDouble(const TraceEvent& event,
+                                   double* num) const {
+  DCHECK_EQ(QUERY_EVENT_MEMBER, type_);
+
+  // This could be a request for a member of |event| or a member of |event|'s
+  // associated previous or next event. Store the target event in the_event:
+  const TraceEvent* the_event = SelectTargetEvent(&event, member_);
+
+  // Request for member of associated event, but there is no associated event.
+  if (!the_event)
+    return false;
+
+  switch (member_) {
+    case EVENT_PID:
+    case OTHER_PID:
+    case PREV_PID:
+      *num = static_cast<double>(the_event->thread.process_id);
+      return true;
+    case EVENT_TID:
+    case OTHER_TID:
+    case PREV_TID:
+      *num = static_cast<double>(the_event->thread.thread_id);
+      return true;
+    case EVENT_TIME:
+    case OTHER_TIME:
+    case PREV_TIME:
+      *num = the_event->timestamp;
+      return true;
+    case EVENT_DURATION:
+      if (!the_event->has_other_event())
+        return false;
+      *num = the_event->GetAbsTimeToOtherEvent();
+      return true;
+    case EVENT_COMPLETE_DURATION:
+      if (the_event->phase != TRACE_EVENT_PHASE_COMPLETE)
+        return false;
+      *num = the_event->duration;
+      return true;
+    case EVENT_PHASE:
+    case OTHER_PHASE:
+    case PREV_PHASE:
+      *num = static_cast<double>(the_event->phase);
+      return true;
+    case EVENT_HAS_STRING_ARG:
+    case OTHER_HAS_STRING_ARG:
+    case PREV_HAS_STRING_ARG:
+      *num = (the_event->HasStringArg(string_) ? 1.0 : 0.0);
+      return true;
+    case EVENT_HAS_NUMBER_ARG:
+    case OTHER_HAS_NUMBER_ARG:
+    case PREV_HAS_NUMBER_ARG:
+      *num = (the_event->HasNumberArg(string_) ? 1.0 : 0.0);
+      return true;
+    case EVENT_ARG:
+    case OTHER_ARG:
+    case PREV_ARG: {
+      // Search for the argument name and return its value if found.
+      std::map<std::string, double>::const_iterator num_i =
+          the_event->arg_numbers.find(string_);
+      if (num_i == the_event->arg_numbers.end())
+        return false;
+      *num = num_i->second;
+      return true;
+    }
+    case EVENT_HAS_OTHER:
+      // return 1.0 (true) if the other event exists
+      *num = event.other_event ? 1.0 : 0.0;
+      return true;
+    case EVENT_HAS_PREV:
+      *num = event.prev_event ? 1.0 : 0.0;
+      return true;
+    default:
+      return false;
+  }
+}
+
+bool Query::GetMemberValueAsString(const TraceEvent& event,
+                                   std::string* str) const {
+  DCHECK_EQ(QUERY_EVENT_MEMBER, type_);
+
+  // This could be a request for a member of |event| or a member of |event|'s
+  // associated previous or next event. Store the target event in the_event:
+  const TraceEvent* the_event = SelectTargetEvent(&event, member_);
+
+  // Request for member of associated event, but there is no associated event.
+  if (!the_event)
+    return false;
+
+  switch (member_) {
+    case EVENT_CATEGORY:
+    case OTHER_CATEGORY:
+    case PREV_CATEGORY:
+      *str = the_event->category;
+      return true;
+    case EVENT_NAME:
+    case OTHER_NAME:
+    case PREV_NAME:
+      *str = the_event->name;
+      return true;
+    case EVENT_ID:
+    case OTHER_ID:
+    case PREV_ID:
+      *str = the_event->id;
+      return true;
+    case EVENT_ARG:
+    case OTHER_ARG:
+    case PREV_ARG: {
+      // Search for the argument name and return its value if found.
+      std::map<std::string, std::string>::const_iterator str_i =
+          the_event->arg_strings.find(string_);
+      if (str_i == the_event->arg_strings.end())
+        return false;
+      *str = str_i->second;
+      return true;
+    }
+    default:
+      return false;
+  }
+}
+
+Query::Query(const std::string& str)
+    : type_(QUERY_STRING),
+      operator_(OP_INVALID),
+      member_(EVENT_INVALID),
+      number_(0),
+      string_(str),
+      is_pattern_(false) {
+}
+
+Query::Query(double num)
+    : type_(QUERY_NUMBER),
+      operator_(OP_INVALID),
+      member_(EVENT_INVALID),
+      number_(num),
+      is_pattern_(false) {
+}
+const Query& Query::left() const {
+  return left_->query();
+}
+
+const Query& Query::right() const {
+  return right_->query();
+}
+
+Query Query::operator==(const Query& rhs) const {
+  return Query(*this, rhs, OP_EQ);
+}
+
+Query Query::operator!=(const Query& rhs) const {
+  return Query(*this, rhs, OP_NE);
+}
+
+Query Query::operator<(const Query& rhs) const {
+  return Query(*this, rhs, OP_LT);
+}
+
+Query Query::operator<=(const Query& rhs) const {
+  return Query(*this, rhs, OP_LE);
+}
+
+Query Query::operator>(const Query& rhs) const {
+  return Query(*this, rhs, OP_GT);
+}
+
+Query Query::operator>=(const Query& rhs) const {
+  return Query(*this, rhs, OP_GE);
+}
+
+Query Query::operator&&(const Query& rhs) const {
+  return Query(*this, rhs, OP_AND);
+}
+
+Query Query::operator||(const Query& rhs) const {
+  return Query(*this, rhs, OP_OR);
+}
+
+Query Query::operator!() const {
+  return Query(*this, OP_NOT);
+}
+
+Query Query::operator+(const Query& rhs) const {
+  return Query(*this, rhs, OP_ADD);
+}
+
+Query Query::operator-(const Query& rhs) const {
+  return Query(*this, rhs, OP_SUB);
+}
+
+Query Query::operator*(const Query& rhs) const {
+  return Query(*this, rhs, OP_MUL);
+}
+
+Query Query::operator/(const Query& rhs) const {
+  return Query(*this, rhs, OP_DIV);
+}
+
+Query Query::operator%(const Query& rhs) const {
+  return Query(*this, rhs, OP_MOD);
+}
+
+Query Query::operator-() const {
+  return Query(*this, OP_NEGATE);
+}
+
+
+Query::Query(const Query& left, const Query& right, Operator binary_op)
+    : operator_(binary_op),
+      left_(new QueryNode(left)),
+      right_(new QueryNode(right)),
+      member_(EVENT_INVALID),
+      number_(0) {
+  type_ = (binary_op < OP_ADD ?
+           QUERY_BOOLEAN_OPERATOR : QUERY_ARITHMETIC_OPERATOR);
+}
+
+Query::Query(const Query& left, Operator unary_op)
+    : operator_(unary_op),
+      left_(new QueryNode(left)),
+      member_(EVENT_INVALID),
+      number_(0) {
+  type_ = (unary_op < OP_ADD ?
+           QUERY_BOOLEAN_OPERATOR : QUERY_ARITHMETIC_OPERATOR);
+}
+
+namespace {
+
+// Search |events| for |query| and add matches to |output|.
+size_t FindMatchingEvents(const std::vector<TraceEvent>& events,
+                          const Query& query,
+                          TraceEventVector* output,
+                          bool ignore_metadata_events) {
+  for (size_t i = 0; i < events.size(); ++i) {
+    if (ignore_metadata_events && events[i].phase == TRACE_EVENT_PHASE_METADATA)
+      continue;
+    if (query.Evaluate(events[i]))
+      output->push_back(&events[i]);
+  }
+  return output->size();
+}
+
+bool ParseEventsFromJson(const std::string& json,
+                         std::vector<TraceEvent>* output) {
+  std::unique_ptr<base::Value> root = base::JSONReader::Read(json);
+
+  base::ListValue* root_list = nullptr;
+  if (!root.get() || !root->GetAsList(&root_list))
+    return false;
+
+  for (size_t i = 0; i < root_list->GetSize(); ++i) {
+    base::Value* item = nullptr;
+    if (root_list->Get(i, &item)) {
+      TraceEvent event;
+      if (event.SetFromJSON(item))
+        output->push_back(std::move(event));
+      else
+        return false;
+    }
+  }
+
+  return true;
+}
+
+}  // namespace
+
+// TraceAnalyzer
+
+TraceAnalyzer::TraceAnalyzer()
+    : ignore_metadata_events_(false), allow_association_changes_(true) {}
+
+TraceAnalyzer::~TraceAnalyzer() = default;
+
+// static
+TraceAnalyzer* TraceAnalyzer::Create(const std::string& json_events) {
+  std::unique_ptr<TraceAnalyzer> analyzer(new TraceAnalyzer());
+  if (analyzer->SetEvents(json_events))
+    return analyzer.release();
+  return nullptr;
+}
+
+bool TraceAnalyzer::SetEvents(const std::string& json_events) {
+  raw_events_.clear();
+  if (!ParseEventsFromJson(json_events, &raw_events_))
+    return false;
+  std::stable_sort(raw_events_.begin(), raw_events_.end());
+  ParseMetadata();
+  return true;
+}
+
+void TraceAnalyzer::AssociateBeginEndEvents() {
+  using trace_analyzer::Query;
+
+  Query begin(Query::EventPhaseIs(TRACE_EVENT_PHASE_BEGIN));
+  Query end(Query::EventPhaseIs(TRACE_EVENT_PHASE_END));
+  Query match(Query::EventName() == Query::OtherName() &&
+              Query::EventCategory() == Query::OtherCategory() &&
+              Query::EventTid() == Query::OtherTid() &&
+              Query::EventPid() == Query::OtherPid());
+
+  AssociateEvents(begin, end, match);
+}
+
+void TraceAnalyzer::AssociateAsyncBeginEndEvents(bool match_pid) {
+  using trace_analyzer::Query;
+
+  Query begin(
+      Query::EventPhaseIs(TRACE_EVENT_PHASE_ASYNC_BEGIN) ||
+      Query::EventPhaseIs(TRACE_EVENT_PHASE_ASYNC_STEP_INTO) ||
+      Query::EventPhaseIs(TRACE_EVENT_PHASE_ASYNC_STEP_PAST));
+  Query end(Query::EventPhaseIs(TRACE_EVENT_PHASE_ASYNC_END) ||
+            Query::EventPhaseIs(TRACE_EVENT_PHASE_ASYNC_STEP_INTO) ||
+            Query::EventPhaseIs(TRACE_EVENT_PHASE_ASYNC_STEP_PAST));
+  Query match(Query::EventCategory() == Query::OtherCategory() &&
+              Query::EventId() == Query::OtherId());
+
+  if (match_pid) {
+    match = match && Query::EventPid() == Query::OtherPid();
+  }
+
+  AssociateEvents(begin, end, match);
+}
+
+void TraceAnalyzer::AssociateEvents(const Query& first,
+                                    const Query& second,
+                                    const Query& match) {
+  DCHECK(allow_association_changes_)
+      << "AssociateEvents not allowed after FindEvents";
+
+  // Search for matching begin/end event pairs. When a matching end is found,
+  // it is associated with the begin event.
+  std::vector<TraceEvent*> begin_stack;
+  for (size_t event_index = 0; event_index < raw_events_.size();
+       ++event_index) {
+
+    TraceEvent& this_event = raw_events_[event_index];
+
+    if (second.Evaluate(this_event)) {
+      // Search stack for matching begin, starting from end.
+      for (int stack_index = static_cast<int>(begin_stack.size()) - 1;
+           stack_index >= 0; --stack_index) {
+        TraceEvent& begin_event = *begin_stack[stack_index];
+
+        // Temporarily set other to test against the match query.
+        const TraceEvent* other_backup = begin_event.other_event;
+        begin_event.other_event = &this_event;
+        if (match.Evaluate(begin_event)) {
+          // Found a matching begin/end pair.
+          // Set the associated previous event
+          this_event.prev_event = &begin_event;
+          // Erase the matching begin event index from the stack.
+          begin_stack.erase(begin_stack.begin() + stack_index);
+          break;
+        }
+
+        // Not a match, restore original other and continue.
+        begin_event.other_event = other_backup;
+      }
+    }
+    // Even if this_event is a |second| event that has matched an earlier
+    // |first| event, it can still also be a |first| event and be associated
+    // with a later |second| event.
+    if (first.Evaluate(this_event)) {
+      begin_stack.push_back(&this_event);
+    }
+  }
+}
+
+void TraceAnalyzer::MergeAssociatedEventArgs() {
+  for (size_t i = 0; i < raw_events_.size(); ++i) {
+    // Merge all associated events with the first event.
+    const TraceEvent* other = raw_events_[i].other_event;
+    // Avoid looping by keeping set of encountered TraceEvents.
+    std::set<const TraceEvent*> encounters;
+    encounters.insert(&raw_events_[i]);
+    while (other && encounters.find(other) == encounters.end()) {
+      encounters.insert(other);
+      raw_events_[i].arg_numbers.insert(
+          other->arg_numbers.begin(),
+          other->arg_numbers.end());
+      raw_events_[i].arg_strings.insert(
+          other->arg_strings.begin(),
+          other->arg_strings.end());
+      other = other->other_event;
+    }
+  }
+}
+
+size_t TraceAnalyzer::FindEvents(const Query& query, TraceEventVector* output) {
+  allow_association_changes_ = false;
+  output->clear();
+  return FindMatchingEvents(
+      raw_events_, query, output, ignore_metadata_events_);
+}
+
+const TraceEvent* TraceAnalyzer::FindFirstOf(const Query& query) {
+  TraceEventVector output;
+  if (FindEvents(query, &output) > 0)
+    return output.front();
+  return nullptr;
+}
+
+const TraceEvent* TraceAnalyzer::FindLastOf(const Query& query) {
+  TraceEventVector output;
+  if (FindEvents(query, &output) > 0)
+    return output.back();
+  return nullptr;
+}
+
+const std::string& TraceAnalyzer::GetThreadName(
+    const TraceEvent::ProcessThreadID& thread) {
+  // If thread is not found, just add and return empty string.
+  return thread_names_[thread];
+}
+
+void TraceAnalyzer::ParseMetadata() {
+  for (size_t i = 0; i < raw_events_.size(); ++i) {
+    TraceEvent& this_event = raw_events_[i];
+    // Check for thread name metadata.
+    if (this_event.phase != TRACE_EVENT_PHASE_METADATA ||
+        this_event.name != "thread_name")
+      continue;
+    std::map<std::string, std::string>::const_iterator string_it =
+        this_event.arg_strings.find("name");
+    if (string_it != this_event.arg_strings.end())
+      thread_names_[this_event.thread] = string_it->second;
+  }
+}
+
+// Utility functions for collecting process-local traces and creating a
+// |TraceAnalyzer| from the result.
+
+void Start(const std::string& category_filter_string) {
+  DCHECK(!base::trace_event::TraceLog::GetInstance()->IsEnabled());
+  base::trace_event::TraceLog::GetInstance()->SetEnabled(
+      base::trace_event::TraceConfig(category_filter_string, ""),
+      base::trace_event::TraceLog::RECORDING_MODE);
+}
+
+std::unique_ptr<TraceAnalyzer> Stop() {
+  DCHECK(base::trace_event::TraceLog::GetInstance()->IsEnabled());
+  base::trace_event::TraceLog::GetInstance()->SetDisabled();
+
+  base::trace_event::TraceResultBuffer buffer;
+  base::trace_event::TraceResultBuffer::SimpleOutput trace_output;
+  buffer.SetOutputCallback(trace_output.GetCallback());
+  base::RunLoop run_loop;
+  buffer.Start();
+  base::trace_event::TraceLog::GetInstance()->Flush(
+      base::BindRepeating(&OnTraceDataCollected, run_loop.QuitClosure(),
+                          base::Unretained(&buffer)));
+  run_loop.Run();
+  buffer.Finish();
+
+  return base::WrapUnique(TraceAnalyzer::Create(trace_output.json_output));
+}
+
+// TraceEventVector utility functions.
+
+bool GetRateStats(const TraceEventVector& events,
+                  RateStats* stats,
+                  const RateStatsOptions* options) {
+  DCHECK(stats);
+  // Need at least 3 events to calculate rate stats.
+  const size_t kMinEvents = 3;
+  if (events.size() < kMinEvents) {
+    LOG(ERROR) << "Not enough events: " << events.size();
+    return false;
+  }
+
+  std::vector<double> deltas;
+  size_t num_deltas = events.size() - 1;
+  for (size_t i = 0; i < num_deltas; ++i) {
+    double delta = events.at(i + 1)->timestamp - events.at(i)->timestamp;
+    if (delta < 0.0) {
+      LOG(ERROR) << "Events are out of order";
+      return false;
+    }
+    deltas.push_back(delta);
+  }
+
+  std::sort(deltas.begin(), deltas.end());
+
+  if (options) {
+    if (options->trim_min + options->trim_max > events.size() - kMinEvents) {
+      LOG(ERROR) << "Attempt to trim too many events";
+      return false;
+    }
+    deltas.erase(deltas.begin(), deltas.begin() + options->trim_min);
+    deltas.erase(deltas.end() - options->trim_max, deltas.end());
+  }
+
+  num_deltas = deltas.size();
+  double delta_sum = 0.0;
+  for (size_t i = 0; i < num_deltas; ++i)
+    delta_sum += deltas[i];
+
+  stats->min_us = *std::min_element(deltas.begin(), deltas.end());
+  stats->max_us = *std::max_element(deltas.begin(), deltas.end());
+  stats->mean_us = delta_sum / static_cast<double>(num_deltas);
+
+  double sum_mean_offsets_squared = 0.0;
+  for (size_t i = 0; i < num_deltas; ++i) {
+    double offset = fabs(deltas[i] - stats->mean_us);
+    sum_mean_offsets_squared += offset * offset;
+  }
+  stats->standard_deviation_us =
+      sqrt(sum_mean_offsets_squared / static_cast<double>(num_deltas - 1));
+
+  return true;
+}
+
+bool FindFirstOf(const TraceEventVector& events,
+                 const Query& query,
+                 size_t position,
+                 size_t* return_index) {
+  DCHECK(return_index);
+  for (size_t i = position; i < events.size(); ++i) {
+    if (query.Evaluate(*events[i])) {
+      *return_index = i;
+      return true;
+    }
+  }
+  return false;
+}
+
+bool FindLastOf(const TraceEventVector& events,
+                const Query& query,
+                size_t position,
+                size_t* return_index) {
+  DCHECK(return_index);
+  for (size_t i = std::min(position + 1, events.size()); i != 0; --i) {
+    if (query.Evaluate(*events[i - 1])) {
+      *return_index = i - 1;
+      return true;
+    }
+  }
+  return false;
+}
+
+bool FindClosest(const TraceEventVector& events,
+                 const Query& query,
+                 size_t position,
+                 size_t* return_closest,
+                 size_t* return_second_closest) {
+  DCHECK(return_closest);
+  if (events.empty() || position >= events.size())
+    return false;
+  size_t closest = events.size();
+  size_t second_closest = events.size();
+  for (size_t i = 0; i < events.size(); ++i) {
+    if (!query.Evaluate(*events.at(i)))
+      continue;
+    if (closest == events.size()) {
+      closest = i;
+      continue;
+    }
+    if (fabs(events.at(i)->timestamp - events.at(position)->timestamp) <
+        fabs(events.at(closest)->timestamp - events.at(position)->timestamp)) {
+      second_closest = closest;
+      closest = i;
+    } else if (second_closest == events.size()) {
+      second_closest = i;
+    }
+  }
+
+  if (closest < events.size() &&
+      (!return_second_closest || second_closest < events.size())) {
+    *return_closest = closest;
+    if (return_second_closest)
+      *return_second_closest = second_closest;
+    return true;
+  }
+
+  return false;
+}
+
+size_t CountMatches(const TraceEventVector& events,
+                    const Query& query,
+                    size_t begin_position,
+                    size_t end_position) {
+  if (begin_position >= events.size())
+    return 0u;
+  end_position = (end_position < events.size()) ? end_position : events.size();
+  size_t count = 0u;
+  for (size_t i = begin_position; i < end_position; ++i) {
+    if (query.Evaluate(*events.at(i)))
+      ++count;
+  }
+  return count;
+}
+
+}  // namespace trace_analyzer
diff --git a/base/test/trace_event_analyzer.h b/base/test/trace_event_analyzer.h
new file mode 100644
index 0000000..dcdd2e4
--- /dev/null
+++ b/base/test/trace_event_analyzer.h
@@ -0,0 +1,842 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Use trace_analyzer::Query and trace_analyzer::TraceAnalyzer to search for
+// specific trace events that were generated by the trace_event.h API.
+//
+// Basic procedure:
+// - Get trace events JSON string from base::trace_event::TraceLog.
+// - Create TraceAnalyzer with JSON string.
+// - Call TraceAnalyzer::AssociateBeginEndEvents (optional).
+// - Call TraceAnalyzer::AssociateEvents (zero or more times).
+// - Call TraceAnalyzer::FindEvents with queries to find specific events.
+//
+// A Query is a boolean expression tree that evaluates to true or false for a
+// given trace event. Queries can be combined into a tree using boolean,
+// arithmetic and comparison operators that refer to data of an individual trace
+// event.
+//
+// The events are returned as trace_analyzer::TraceEvent objects.
+// TraceEvent contains a single trace event's data, as well as a pointer to
+// a related trace event. The related trace event is typically the matching end
+// of a begin event or the matching begin of an end event.
+//
+// The following examples use this basic setup code to construct TraceAnalyzer
+// with the json trace string retrieved from TraceLog and construct an event
+// vector for retrieving events:
+//
+// TraceAnalyzer analyzer(json_events);
+// TraceEventVector events;
+//
+// EXAMPLE 1: Find events named "my_event".
+//
+// analyzer.FindEvents(Query(EVENT_NAME) == "my_event", &events);
+//
+// EXAMPLE 2: Find begin events named "my_event" with duration > 1 second.
+//
+// Query q = (Query(EVENT_NAME) == Query::String("my_event") &&
+//            Query(EVENT_PHASE) == Query::Phase(TRACE_EVENT_PHASE_BEGIN) &&
+//            Query(EVENT_DURATION) > Query::Double(1000000.0));
+// analyzer.FindEvents(q, &events);
+//
+// EXAMPLE 3: Associating event pairs across threads.
+//
+// If the test needs to analyze something that starts and ends on different
+// threads, the test needs to use INSTANT events. The typical procedure is to
+// specify the same unique ID as a TRACE_EVENT argument on both the start and
+// finish INSTANT events. Then use the following procedure to associate those
+// events.
+//
+// Step 1: instrument code with custom begin/end trace events.
+//   [Thread 1 tracing code]
+//   TRACE_EVENT_INSTANT1("test_latency", "timing1_begin", "id", 3);
+//   [Thread 2 tracing code]
+//   TRACE_EVENT_INSTANT1("test_latency", "timing1_end", "id", 3);
+//
+// Step 2: associate these custom begin/end pairs.
+//   Query begin(Query(EVENT_NAME) == Query::String("timing1_begin"));
+//   Query end(Query(EVENT_NAME) == Query::String("timing1_end"));
+//   Query match(Query(EVENT_ARG, "id") == Query(OTHER_ARG, "id"));
+//   analyzer.AssociateEvents(begin, end, match);
+//
+// Step 3: search for "timing1_begin" events with existing other event.
+//   Query q = (Query(EVENT_NAME) == Query::String("timing1_begin") &&
+//              Query(EVENT_HAS_OTHER));
+//   analyzer.FindEvents(q, &events);
+//
+// Step 4: analyze events, such as checking durations.
+//   for (size_t i = 0; i < events.size(); ++i) {
+//     double duration;
+//     EXPECT_TRUE(events[i].GetAbsTimeToOtherEvent(&duration));
+//     EXPECT_LT(duration, 1000000.0/60.0); // expect less than 1/60 second.
+//   }
+//
+// There are two helper functions, Start(category_filter_string) and Stop(), for
+// facilitating the collection of process-local traces and building a
+// TraceAnalyzer from them. A typical test, that uses the helper functions,
+// looks like the following:
+//
+// TEST_F(...) {
+//   Start("*");
+//   [Invoke the functions you want to test their traces]
+//   auto analyzer = Stop();
+//
+//   [Use the analyzer to verify produced traces, as explained above]
+// }
+//
+// Note: The Stop() function needs a SingleThreadTaskRunner.
+
+#ifndef BASE_TEST_TRACE_EVENT_ANALYZER_H_
+#define BASE_TEST_TRACE_EVENT_ANALYZER_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/trace_event/trace_event.h"
+
+namespace base {
+class Value;
+}
+
+namespace trace_analyzer {
+class QueryNode;
+
+// trace_analyzer::TraceEvent is a more convenient form of the
+// base::trace_event::TraceEvent class to make tracing-based tests easier to
+// write.
+struct TraceEvent {
+  // ProcessThreadID contains a Process ID and Thread ID.
+  struct ProcessThreadID {
+    ProcessThreadID() : process_id(0), thread_id(0) {}
+    ProcessThreadID(int process_id, int thread_id)
+        : process_id(process_id), thread_id(thread_id) {}
+    bool operator< (const ProcessThreadID& rhs) const {
+      if (process_id != rhs.process_id)
+        return process_id < rhs.process_id;
+      return thread_id < rhs.thread_id;
+    }
+    int process_id;
+    int thread_id;
+  };
+
+  TraceEvent();
+  TraceEvent(TraceEvent&& other);
+  ~TraceEvent();
+
+  bool SetFromJSON(const base::Value* event_value) WARN_UNUSED_RESULT;
+
+  bool operator< (const TraceEvent& rhs) const {
+    return timestamp < rhs.timestamp;
+  }
+
+  TraceEvent& operator=(TraceEvent&& rhs);
+
+  bool has_other_event() const { return other_event; }
+
+  // Returns absolute duration in microseconds between this event and other
+  // event. Must have already verified that other_event exists by
+  // Query(EVENT_HAS_OTHER) or by calling has_other_event().
+  double GetAbsTimeToOtherEvent() const;
+
+  // Return the argument value if it exists and it is a string.
+  bool GetArgAsString(const std::string& name, std::string* arg) const;
+  // Return the argument value if it exists and it is a number.
+  bool GetArgAsNumber(const std::string& name, double* arg) const;
+  // Return the argument value if it exists.
+  bool GetArgAsValue(const std::string& name,
+                     std::unique_ptr<base::Value>* arg) const;
+
+  // Check if argument exists and is string.
+  bool HasStringArg(const std::string& name) const;
+  // Check if argument exists and is number (double, int or bool).
+  bool HasNumberArg(const std::string& name) const;
+  // Check if argument exists.
+  bool HasArg(const std::string& name) const;
+
+  // Get known existing arguments as specific types.
+  // Useful when you have already queried the argument with
+  // Query(HAS_NUMBER_ARG) or Query(HAS_STRING_ARG).
+  std::string GetKnownArgAsString(const std::string& name) const;
+  double GetKnownArgAsDouble(const std::string& name) const;
+  int GetKnownArgAsInt(const std::string& name) const;
+  bool GetKnownArgAsBool(const std::string& name) const;
+  std::unique_ptr<base::Value> GetKnownArgAsValue(
+      const std::string& name) const;
+
+  // Process ID and Thread ID.
+  ProcessThreadID thread;
+
+  // Time since epoch in microseconds.
+  // Stored as double to match its JSON representation.
+  double timestamp;
+  double duration;
+  char phase;
+  std::string category;
+  std::string name;
+  std::string id;
+  double thread_duration = 0.0;
+  double thread_timestamp = 0.0;
+  std::string scope;
+  std::string bind_id;
+  bool flow_out = false;
+  bool flow_in = false;
+  std::string global_id2;
+  std::string local_id2;
+
+  // All numbers and bool values from TraceEvent args are cast to double.
+  // bool becomes 1.0 (true) or 0.0 (false).
+  std::map<std::string, double> arg_numbers;
+  std::map<std::string, std::string> arg_strings;
+  std::map<std::string, std::unique_ptr<base::Value>> arg_values;
+
+  // The other event associated with this event (or NULL).
+  const TraceEvent* other_event;
+
+  // A back-link for |other_event|. That is, if other_event is not null, then
+  // |event->other_event->prev_event == event| is always true.
+  const TraceEvent* prev_event;
+};
+
+typedef std::vector<const TraceEvent*> TraceEventVector;
+
+class Query {
+ public:
+  Query(const Query& query);
+
+  ~Query();
+
+  ////////////////////////////////////////////////////////////////
+  // Query literal values
+
+  // Compare with the given string.
+  static Query String(const std::string& str);
+
+  // Compare with the given number.
+  static Query Double(double num);
+  static Query Int(int32_t num);
+  static Query Uint(uint32_t num);
+
+  // Compare with the given bool.
+  static Query Bool(bool boolean);
+
+  // Compare with the given phase.
+  static Query Phase(char phase);
+
+  // Compare with the given string pattern. Only works with == and != operators.
+  // Example: Query(EVENT_NAME) == Query::Pattern("MyEvent*")
+  static Query Pattern(const std::string& pattern);
+
+  ////////////////////////////////////////////////////////////////
+  // Query event members
+
+  static Query EventPid() { return Query(EVENT_PID); }
+
+  static Query EventTid() { return Query(EVENT_TID); }
+
+  // Return the timestamp of the event in microseconds since epoch.
+  static Query EventTime() { return Query(EVENT_TIME); }
+
+  // Return the absolute time between event and other event in microseconds.
+  // Only works if Query::EventHasOther() == true.
+  static Query EventDuration() { return Query(EVENT_DURATION); }
+
+  // Return the duration of a COMPLETE event.
+  static Query EventCompleteDuration() {
+    return Query(EVENT_COMPLETE_DURATION);
+  }
+
+  static Query EventPhase() { return Query(EVENT_PHASE); }
+
+  static Query EventCategory() { return Query(EVENT_CATEGORY); }
+
+  static Query EventName() { return Query(EVENT_NAME); }
+
+  static Query EventId() { return Query(EVENT_ID); }
+
+  static Query EventPidIs(int process_id) {
+    return Query(EVENT_PID) == Query::Int(process_id);
+  }
+
+  static Query EventTidIs(int thread_id) {
+    return Query(EVENT_TID) == Query::Int(thread_id);
+  }
+
+  static Query EventThreadIs(const TraceEvent::ProcessThreadID& thread) {
+    return EventPidIs(thread.process_id) && EventTidIs(thread.thread_id);
+  }
+
+  static Query EventTimeIs(double timestamp) {
+    return Query(EVENT_TIME) == Query::Double(timestamp);
+  }
+
+  static Query EventDurationIs(double duration) {
+    return Query(EVENT_DURATION) == Query::Double(duration);
+  }
+
+  static Query EventPhaseIs(char phase) {
+    return Query(EVENT_PHASE) == Query::Phase(phase);
+  }
+
+  static Query EventCategoryIs(const std::string& category) {
+    return Query(EVENT_CATEGORY) == Query::String(category);
+  }
+
+  static Query EventNameIs(const std::string& name) {
+    return Query(EVENT_NAME) == Query::String(name);
+  }
+
+  static Query EventIdIs(const std::string& id) {
+    return Query(EVENT_ID) == Query::String(id);
+  }
+
+  // Evaluates to true if arg exists and is a string.
+  static Query EventHasStringArg(const std::string& arg_name) {
+    return Query(EVENT_HAS_STRING_ARG, arg_name);
+  }
+
+  // Evaluates to true if arg exists and is a number.
+  // Number arguments include types double, int and bool.
+  static Query EventHasNumberArg(const std::string& arg_name) {
+    return Query(EVENT_HAS_NUMBER_ARG, arg_name);
+  }
+
+  // Evaluates to arg value (string or number).
+  static Query EventArg(const std::string& arg_name) {
+    return Query(EVENT_ARG, arg_name);
+  }
+
+  // Return true if associated event exists.
+  static Query EventHasOther() { return Query(EVENT_HAS_OTHER); }
+
+  // Access the associated other_event's members:
+
+  static Query OtherPid() { return Query(OTHER_PID); }
+
+  static Query OtherTid() { return Query(OTHER_TID); }
+
+  static Query OtherTime() { return Query(OTHER_TIME); }
+
+  static Query OtherPhase() { return Query(OTHER_PHASE); }
+
+  static Query OtherCategory() { return Query(OTHER_CATEGORY); }
+
+  static Query OtherName() { return Query(OTHER_NAME); }
+
+  static Query OtherId() { return Query(OTHER_ID); }
+
+  static Query OtherPidIs(int process_id) {
+    return Query(OTHER_PID) == Query::Int(process_id);
+  }
+
+  static Query OtherTidIs(int thread_id) {
+    return Query(OTHER_TID) == Query::Int(thread_id);
+  }
+
+  static Query OtherThreadIs(const TraceEvent::ProcessThreadID& thread) {
+    return OtherPidIs(thread.process_id) && OtherTidIs(thread.thread_id);
+  }
+
+  static Query OtherTimeIs(double timestamp) {
+    return Query(OTHER_TIME) == Query::Double(timestamp);
+  }
+
+  static Query OtherPhaseIs(char phase) {
+    return Query(OTHER_PHASE) == Query::Phase(phase);
+  }
+
+  static Query OtherCategoryIs(const std::string& category) {
+    return Query(OTHER_CATEGORY) == Query::String(category);
+  }
+
+  static Query OtherNameIs(const std::string& name) {
+    return Query(OTHER_NAME) == Query::String(name);
+  }
+
+  static Query OtherIdIs(const std::string& id) {
+    return Query(OTHER_ID) == Query::String(id);
+  }
+
+  // Evaluates to true if arg exists and is a string.
+  static Query OtherHasStringArg(const std::string& arg_name) {
+    return Query(OTHER_HAS_STRING_ARG, arg_name);
+  }
+
+  // Evaluates to true if arg exists and is a number.
+  // Number arguments include types double, int and bool.
+  static Query OtherHasNumberArg(const std::string& arg_name) {
+    return Query(OTHER_HAS_NUMBER_ARG, arg_name);
+  }
+
+  // Evaluates to arg value (string or number).
+  static Query OtherArg(const std::string& arg_name) {
+    return Query(OTHER_ARG, arg_name);
+  }
+
+  // Access the associated prev_event's members:
+
+  static Query PrevPid() { return Query(PREV_PID); }
+
+  static Query PrevTid() { return Query(PREV_TID); }
+
+  static Query PrevTime() { return Query(PREV_TIME); }
+
+  static Query PrevPhase() { return Query(PREV_PHASE); }
+
+  static Query PrevCategory() { return Query(PREV_CATEGORY); }
+
+  static Query PrevName() { return Query(PREV_NAME); }
+
+  static Query PrevId() { return Query(PREV_ID); }
+
+  static Query PrevPidIs(int process_id) {
+    return Query(PREV_PID) == Query::Int(process_id);
+  }
+
+  static Query PrevTidIs(int thread_id) {
+    return Query(PREV_TID) == Query::Int(thread_id);
+  }
+
+  static Query PrevThreadIs(const TraceEvent::ProcessThreadID& thread) {
+    return PrevPidIs(thread.process_id) && PrevTidIs(thread.thread_id);
+  }
+
+  static Query PrevTimeIs(double timestamp) {
+    return Query(PREV_TIME) == Query::Double(timestamp);
+  }
+
+  static Query PrevPhaseIs(char phase) {
+    return Query(PREV_PHASE) == Query::Phase(phase);
+  }
+
+  static Query PrevCategoryIs(const std::string& category) {
+    return Query(PREV_CATEGORY) == Query::String(category);
+  }
+
+  static Query PrevNameIs(const std::string& name) {
+    return Query(PREV_NAME) == Query::String(name);
+  }
+
+  static Query PrevIdIs(const std::string& id) {
+    return Query(PREV_ID) == Query::String(id);
+  }
+
+  // Evaluates to true if arg exists and is a string.
+  static Query PrevHasStringArg(const std::string& arg_name) {
+    return Query(PREV_HAS_STRING_ARG, arg_name);
+  }
+
+  // Evaluates to true if arg exists and is a number.
+  // Number arguments include types double, int and bool.
+  static Query PrevHasNumberArg(const std::string& arg_name) {
+    return Query(PREV_HAS_NUMBER_ARG, arg_name);
+  }
+
+  // Evaluates to arg value (string or number).
+  static Query PrevArg(const std::string& arg_name) {
+    return Query(PREV_ARG, arg_name);
+  }
+
+  ////////////////////////////////////////////////////////////////
+  // Common queries:
+
+  // Find BEGIN events that have a corresponding END event.
+  static Query MatchBeginWithEnd() {
+    return (Query(EVENT_PHASE) == Query::Phase(TRACE_EVENT_PHASE_BEGIN)) &&
+           Query(EVENT_HAS_OTHER);
+  }
+
+  // Find COMPLETE events.
+  static Query MatchComplete() {
+    return (Query(EVENT_PHASE) == Query::Phase(TRACE_EVENT_PHASE_COMPLETE));
+  }
+
+  // Find ASYNC_BEGIN events that have a corresponding ASYNC_END event.
+  static Query MatchAsyncBeginWithNext() {
+    return (Query(EVENT_PHASE) ==
+            Query::Phase(TRACE_EVENT_PHASE_ASYNC_BEGIN)) &&
+           Query(EVENT_HAS_OTHER);
+  }
+
+  // Find BEGIN events of given |name| which also have associated END events.
+  static Query MatchBeginName(const std::string& name) {
+    return (Query(EVENT_NAME) == Query(name)) && MatchBeginWithEnd();
+  }
+
+  // Find COMPLETE events of given |name|.
+  static Query MatchCompleteName(const std::string& name) {
+    return (Query(EVENT_NAME) == Query(name)) && MatchComplete();
+  }
+
+  // Match given Process ID and Thread ID.
+  static Query MatchThread(const TraceEvent::ProcessThreadID& thread) {
+    return (Query(EVENT_PID) == Query::Int(thread.process_id)) &&
+           (Query(EVENT_TID) == Query::Int(thread.thread_id));
+  }
+
+  // Match event pair that spans multiple threads.
+  static Query MatchCrossThread() {
+    return (Query(EVENT_PID) != Query(OTHER_PID)) ||
+           (Query(EVENT_TID) != Query(OTHER_TID));
+  }
+
+  ////////////////////////////////////////////////////////////////
+  // Operators:
+
+  // Boolean operators:
+  Query operator==(const Query& rhs) const;
+  Query operator!=(const Query& rhs) const;
+  Query operator< (const Query& rhs) const;
+  Query operator<=(const Query& rhs) const;
+  Query operator> (const Query& rhs) const;
+  Query operator>=(const Query& rhs) const;
+  Query operator&&(const Query& rhs) const;
+  Query operator||(const Query& rhs) const;
+  Query operator!() const;
+
+  // Arithmetic operators:
+  // Following operators are applied to double arguments:
+  Query operator+(const Query& rhs) const;
+  Query operator-(const Query& rhs) const;
+  Query operator*(const Query& rhs) const;
+  Query operator/(const Query& rhs) const;
+  Query operator-() const;
+  // Mod operates on int64_t args (doubles are casted to int64_t beforehand):
+  Query operator%(const Query& rhs) const;
+
+  // Return true if the given event matches this query tree.
+  // This is a recursive method that walks the query tree.
+  bool Evaluate(const TraceEvent& event) const;
+
+  enum TraceEventMember {
+    EVENT_INVALID,
+    EVENT_PID,
+    EVENT_TID,
+    EVENT_TIME,
+    EVENT_DURATION,
+    EVENT_COMPLETE_DURATION,
+    EVENT_PHASE,
+    EVENT_CATEGORY,
+    EVENT_NAME,
+    EVENT_ID,
+    EVENT_HAS_STRING_ARG,
+    EVENT_HAS_NUMBER_ARG,
+    EVENT_ARG,
+    EVENT_HAS_OTHER,
+    EVENT_HAS_PREV,
+
+    OTHER_PID,
+    OTHER_TID,
+    OTHER_TIME,
+    OTHER_PHASE,
+    OTHER_CATEGORY,
+    OTHER_NAME,
+    OTHER_ID,
+    OTHER_HAS_STRING_ARG,
+    OTHER_HAS_NUMBER_ARG,
+    OTHER_ARG,
+
+    PREV_PID,
+    PREV_TID,
+    PREV_TIME,
+    PREV_PHASE,
+    PREV_CATEGORY,
+    PREV_NAME,
+    PREV_ID,
+    PREV_HAS_STRING_ARG,
+    PREV_HAS_NUMBER_ARG,
+    PREV_ARG,
+
+    OTHER_FIRST_MEMBER = OTHER_PID,
+    OTHER_LAST_MEMBER = OTHER_ARG,
+
+    PREV_FIRST_MEMBER = PREV_PID,
+    PREV_LAST_MEMBER = PREV_ARG,
+  };
+
+  enum Operator {
+    OP_INVALID,
+    // Boolean operators:
+    OP_EQ,
+    OP_NE,
+    OP_LT,
+    OP_LE,
+    OP_GT,
+    OP_GE,
+    OP_AND,
+    OP_OR,
+    OP_NOT,
+    // Arithmetic operators:
+    OP_ADD,
+    OP_SUB,
+    OP_MUL,
+    OP_DIV,
+    OP_MOD,
+    OP_NEGATE
+  };
+
+  enum QueryType {
+    QUERY_BOOLEAN_OPERATOR,
+    QUERY_ARITHMETIC_OPERATOR,
+    QUERY_EVENT_MEMBER,
+    QUERY_NUMBER,
+    QUERY_STRING
+  };
+
+  // Compare with the given member.
+  explicit Query(TraceEventMember member);
+
+  // Compare with the given member argument value.
+  Query(TraceEventMember member, const std::string& arg_name);
+
+  // Compare with the given string.
+  explicit Query(const std::string& str);
+
+  // Compare with the given number.
+  explicit Query(double num);
+
+  // Construct a boolean Query that returns (left <binary_op> right).
+  Query(const Query& left, const Query& right, Operator binary_op);
+
+  // Construct a boolean Query that returns (<binary_op> left).
+  Query(const Query& left, Operator unary_op);
+
+  // Try to compare left_ against right_ based on operator_.
+  // If either left or right does not convert to double, false is returned.
+  // Otherwise, true is returned and |result| is set to the comparison result.
+  bool CompareAsDouble(const TraceEvent& event, bool* result) const;
+
+  // Try to compare left_ against right_ based on operator_.
+  // If either left or right does not convert to string, false is returned.
+  // Otherwise, true is returned and |result| is set to the comparison result.
+  bool CompareAsString(const TraceEvent& event, bool* result) const;
+
+  // Attempt to convert this Query to a double. On success, true is returned
+  // and the double value is stored in |num|.
+  bool GetAsDouble(const TraceEvent& event, double* num) const;
+
+  // Attempt to convert this Query to a string. On success, true is returned
+  // and the string value is stored in |str|.
+  bool GetAsString(const TraceEvent& event, std::string* str) const;
+
+  // Evaluate this Query as an arithmetic operator on left_ and right_.
+  bool EvaluateArithmeticOperator(const TraceEvent& event,
+                                  double* num) const;
+
+  // For QUERY_EVENT_MEMBER Query: attempt to get the double value of the Query.
+  bool GetMemberValueAsDouble(const TraceEvent& event, double* num) const;
+
+  // For QUERY_EVENT_MEMBER Query: attempt to get the string value of the Query.
+  bool GetMemberValueAsString(const TraceEvent& event, std::string* num) const;
+
+  // Does this Query represent a value?
+  bool is_value() const { return type_ != QUERY_BOOLEAN_OPERATOR; }
+
+  bool is_unary_operator() const {
+    return operator_ == OP_NOT || operator_ == OP_NEGATE;
+  }
+
+  bool is_comparison_operator() const {
+    return operator_ != OP_INVALID && operator_ < OP_AND;
+  }
+
+  static const TraceEvent* SelectTargetEvent(const TraceEvent* ev,
+                                             TraceEventMember member);
+
+  const Query& left() const;
+  const Query& right() const;
+
+ private:
+  QueryType type_;
+  Operator operator_;
+  scoped_refptr<QueryNode> left_;
+  scoped_refptr<QueryNode> right_;
+  TraceEventMember member_;
+  double number_;
+  std::string string_;
+  bool is_pattern_;
+};
+
+// Implementation detail:
+// QueryNode allows Query to store a ref-counted query tree.
+class QueryNode : public base::RefCounted<QueryNode> {
+ public:
+  explicit QueryNode(const Query& query);
+  const Query& query() const { return query_; }
+
+ private:
+  friend class base::RefCounted<QueryNode>;
+  ~QueryNode();
+
+  Query query_;
+};
+
+// TraceAnalyzer helps tests search for trace events.
+class TraceAnalyzer {
+ public:
+  ~TraceAnalyzer();
+
+  // Use trace events from JSON string generated by tracing API.
+  // Returns non-NULL if the JSON is successfully parsed.
+  static TraceAnalyzer* Create(const std::string& json_events)
+                               WARN_UNUSED_RESULT;
+
+  void SetIgnoreMetadataEvents(bool ignore) {
+    ignore_metadata_events_ = ignore;
+  }
+
+  // Associate BEGIN and END events with each other. This allows Query(OTHER_*)
+  // to access the associated event and enables Query(EVENT_DURATION).
+  // An end event will match the most recent begin event with the same name,
+  // category, process ID and thread ID. This matches what is shown in
+  // about:tracing. After association, the BEGIN event will point to the
+  // matching END event, but the END event will not point to the BEGIN event.
+  void AssociateBeginEndEvents();
+
+  // Associate ASYNC_BEGIN, ASYNC_STEP and ASYNC_END events with each other.
+  // An ASYNC_END event will match the most recent ASYNC_BEGIN or ASYNC_STEP
+  // event with the same name, category, and ID. This creates a singly linked
+  // list of ASYNC_BEGIN->ASYNC_STEP...->ASYNC_END.
+  // |match_pid| - If true, will only match async events which are running
+  //               under the same process ID, otherwise will allow linking
+  //               async events from different processes.
+  void AssociateAsyncBeginEndEvents(bool match_pid = true);
+
+  // AssociateEvents can be used to customize event associations by setting the
+  // other_event member of TraceEvent. This should be used to associate two
+  // INSTANT events.
+  //
+  // The assumptions are:
+  // - |first| events occur before |second| events.
+  // - the closest matching |second| event is the correct match.
+  //
+  // |first|  - Eligible |first| events match this query.
+  // |second| - Eligible |second| events match this query.
+  // |match|  - This query is run on the |first| event. The OTHER_* EventMember
+  //            queries will point to an eligible |second| event. The query
+  //            should evaluate to true if the |first|/|second| pair is a match.
+  //
+  // When a match is found, the pair will be associated by having the first
+  // event's other_event member point to the other. AssociateEvents does not
+  // clear previous associations, so it is possible to associate multiple pairs
+  // of events by calling AssociateEvents more than once with different queries.
+  //
+  // NOTE: AssociateEvents will overwrite existing other_event associations if
+  // the queries pass for events that already had a previous association.
+  //
+  // After calling any Find* method, it is not allowed to call AssociateEvents
+  // again.
+  void AssociateEvents(const Query& first,
+                       const Query& second,
+                       const Query& match);
+
+  // For each event, copy its arguments to the other_event argument map. If
+  // argument name already exists, it will not be overwritten.
+  void MergeAssociatedEventArgs();
+
+  // Find all events that match query and replace output vector.
+  size_t FindEvents(const Query& query, TraceEventVector* output);
+
+  // Find first event that matches query or NULL if not found.
+  const TraceEvent* FindFirstOf(const Query& query);
+
+  // Find last event that matches query or NULL if not found.
+  const TraceEvent* FindLastOf(const Query& query);
+
+  const std::string& GetThreadName(const TraceEvent::ProcessThreadID& thread);
+
+ private:
+  TraceAnalyzer();
+
+  bool SetEvents(const std::string& json_events) WARN_UNUSED_RESULT;
+
+  // Read metadata (thread names, etc) from events.
+  void ParseMetadata();
+
+  std::map<TraceEvent::ProcessThreadID, std::string> thread_names_;
+  std::vector<TraceEvent> raw_events_;
+  bool ignore_metadata_events_;
+  bool allow_association_changes_;
+
+  DISALLOW_COPY_AND_ASSIGN(TraceAnalyzer);
+};
+
+// Utility functions for collecting process-local traces and creating a
+// |TraceAnalyzer| from the result. Please see comments in trace_config.h to
+// understand how the |category_filter_string| works. Use "*" to enable all
+// default categories.
+void Start(const std::string& category_filter_string);
+std::unique_ptr<TraceAnalyzer> Stop();
+
+// Utility functions for TraceEventVector.
+
+struct RateStats {
+  double min_us;
+  double max_us;
+  double mean_us;
+  double standard_deviation_us;
+};
+
+struct RateStatsOptions {
+  RateStatsOptions() : trim_min(0u), trim_max(0u) {}
+  // After the times between events are sorted, the number of specified elements
+  // will be trimmed before calculating the RateStats. This is useful in cases
+  // where extreme outliers are tolerable and should not skew the overall
+  // average.
+  size_t trim_min;  // Trim this many minimum times.
+  size_t trim_max;  // Trim this many maximum times.
+};
+
+// Calculate min/max/mean and standard deviation from the times between
+// adjacent events.
+bool GetRateStats(const TraceEventVector& events,
+                  RateStats* stats,
+                  const RateStatsOptions* options);
+
+// Starting from |position|, find the first event that matches |query|.
+// Returns true if found, false otherwise.
+bool FindFirstOf(const TraceEventVector& events,
+                 const Query& query,
+                 size_t position,
+                 size_t* return_index);
+
+// Starting from |position|, find the last event that matches |query|.
+// Returns true if found, false otherwise.
+bool FindLastOf(const TraceEventVector& events,
+                const Query& query,
+                size_t position,
+                size_t* return_index);
+
+// Find the closest events to |position| in time that match |query|.
+// return_second_closest may be NULL. Closeness is determined by comparing
+// with the event timestamp.
+// Returns true if found, false otherwise. If both return parameters are
+// requested, both must be found for a successful result.
+bool FindClosest(const TraceEventVector& events,
+                 const Query& query,
+                 size_t position,
+                 size_t* return_closest,
+                 size_t* return_second_closest);
+
+// Count matches, inclusive of |begin_position|, exclusive of |end_position|.
+size_t CountMatches(const TraceEventVector& events,
+                    const Query& query,
+                    size_t begin_position,
+                    size_t end_position);
+
+// Count all matches.
+static inline size_t CountMatches(const TraceEventVector& events,
+                                  const Query& query) {
+  return CountMatches(events, query, 0u, events.size());
+}
+
+}  // namespace trace_analyzer
+
+#endif  // BASE_TEST_TRACE_EVENT_ANALYZER_H_
diff --git a/base/test/trace_event_analyzer_unittest.cc b/base/test/trace_event_analyzer_unittest.cc
new file mode 100644
index 0000000..6461b0f
--- /dev/null
+++ b/base/test/trace_event_analyzer_unittest.cc
@@ -0,0 +1,961 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/trace_event_analyzer.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "base/bind.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/platform_thread.h"
+#include "base/trace_event/trace_buffer.h"
+#include "base/trace_event/trace_event_argument.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace trace_analyzer {
+
+namespace {
+
+class TraceEventAnalyzerTest : public testing::Test {
+ public:
+  void ManualSetUp();
+  void OnTraceDataCollected(
+      base::WaitableEvent* flush_complete_event,
+      const scoped_refptr<base::RefCountedString>& json_events_str,
+      bool has_more_events);
+  void BeginTracing();
+  void EndTracing();
+
+  base::trace_event::TraceResultBuffer::SimpleOutput output_;
+  base::trace_event::TraceResultBuffer buffer_;
+};
+
+void TraceEventAnalyzerTest::ManualSetUp() {
+  ASSERT_TRUE(base::trace_event::TraceLog::GetInstance());
+  buffer_.SetOutputCallback(output_.GetCallback());
+  output_.json_output.clear();
+}
+
+void TraceEventAnalyzerTest::OnTraceDataCollected(
+    base::WaitableEvent* flush_complete_event,
+    const scoped_refptr<base::RefCountedString>& json_events_str,
+    bool has_more_events) {
+  buffer_.AddFragment(json_events_str->data());
+  if (!has_more_events)
+    flush_complete_event->Signal();
+}
+
+void TraceEventAnalyzerTest::BeginTracing() {
+  output_.json_output.clear();
+  buffer_.Start();
+  base::trace_event::TraceLog::GetInstance()->SetEnabled(
+      base::trace_event::TraceConfig("*", ""),
+      base::trace_event::TraceLog::RECORDING_MODE);
+}
+
+void TraceEventAnalyzerTest::EndTracing() {
+  base::trace_event::TraceLog::GetInstance()->SetDisabled();
+  base::WaitableEvent flush_complete_event(
+      base::WaitableEvent::ResetPolicy::AUTOMATIC,
+      base::WaitableEvent::InitialState::NOT_SIGNALED);
+  base::trace_event::TraceLog::GetInstance()->Flush(
+      base::Bind(&TraceEventAnalyzerTest::OnTraceDataCollected,
+                 base::Unretained(this),
+                 base::Unretained(&flush_complete_event)));
+  flush_complete_event.Wait();
+  buffer_.Finish();
+}
+
+}  // namespace
+
+TEST_F(TraceEventAnalyzerTest, NoEvents) {
+  ManualSetUp();
+
+  // Create an empty JSON event string:
+  buffer_.Start();
+  buffer_.Finish();
+
+  std::unique_ptr<TraceAnalyzer> analyzer(
+      TraceAnalyzer::Create(output_.json_output));
+  ASSERT_TRUE(analyzer.get());
+
+  // Search for all events and verify that nothing is returned.
+  TraceEventVector found;
+  analyzer->FindEvents(Query::Bool(true), &found);
+  EXPECT_EQ(0u, found.size());
+}
+
+TEST_F(TraceEventAnalyzerTest, TraceEvent) {
+  ManualSetUp();
+
+  int int_num = 2;
+  double double_num = 3.5;
+  const char str[] = "the string";
+
+  TraceEvent event;
+  event.arg_numbers["false"] = 0.0;
+  event.arg_numbers["true"] = 1.0;
+  event.arg_numbers["int"] = static_cast<double>(int_num);
+  event.arg_numbers["double"] = double_num;
+  event.arg_strings["string"] = str;
+  event.arg_values["dict"] = WrapUnique(new base::DictionaryValue());
+
+  ASSERT_TRUE(event.HasNumberArg("false"));
+  ASSERT_TRUE(event.HasNumberArg("true"));
+  ASSERT_TRUE(event.HasNumberArg("int"));
+  ASSERT_TRUE(event.HasNumberArg("double"));
+  ASSERT_TRUE(event.HasStringArg("string"));
+  ASSERT_FALSE(event.HasNumberArg("notfound"));
+  ASSERT_FALSE(event.HasStringArg("notfound"));
+  ASSERT_TRUE(event.HasArg("dict"));
+  ASSERT_FALSE(event.HasArg("notfound"));
+
+  EXPECT_FALSE(event.GetKnownArgAsBool("false"));
+  EXPECT_TRUE(event.GetKnownArgAsBool("true"));
+  EXPECT_EQ(int_num, event.GetKnownArgAsInt("int"));
+  EXPECT_EQ(double_num, event.GetKnownArgAsDouble("double"));
+  EXPECT_STREQ(str, event.GetKnownArgAsString("string").c_str());
+
+  std::unique_ptr<base::Value> arg;
+  EXPECT_TRUE(event.GetArgAsValue("dict", &arg));
+  EXPECT_EQ(base::Value::Type::DICTIONARY, arg->type());
+}
+
+TEST_F(TraceEventAnalyzerTest, QueryEventMember) {
+  ManualSetUp();
+
+  TraceEvent event;
+  event.thread.process_id = 3;
+  event.thread.thread_id = 4;
+  event.timestamp = 1.5;
+  event.phase = TRACE_EVENT_PHASE_BEGIN;
+  event.category = "category";
+  event.name = "name";
+  event.id = "1";
+  event.arg_numbers["num"] = 7.0;
+  event.arg_strings["str"] = "the string";
+
+  // Other event with all different members:
+  TraceEvent other;
+  other.thread.process_id = 5;
+  other.thread.thread_id = 6;
+  other.timestamp = 2.5;
+  other.phase = TRACE_EVENT_PHASE_END;
+  other.category = "category2";
+  other.name = "name2";
+  other.id = "2";
+  other.arg_numbers["num2"] = 8.0;
+  other.arg_strings["str2"] = "the string 2";
+
+  event.other_event = &other;
+  ASSERT_TRUE(event.has_other_event());
+  double duration = event.GetAbsTimeToOtherEvent();
+
+  Query event_pid = Query::EventPidIs(event.thread.process_id);
+  Query event_tid = Query::EventTidIs(event.thread.thread_id);
+  Query event_time = Query::EventTimeIs(event.timestamp);
+  Query event_duration = Query::EventDurationIs(duration);
+  Query event_phase = Query::EventPhaseIs(event.phase);
+  Query event_category = Query::EventCategoryIs(event.category);
+  Query event_name = Query::EventNameIs(event.name);
+  Query event_id = Query::EventIdIs(event.id);
+  Query event_has_arg1 = Query::EventHasNumberArg("num");
+  Query event_has_arg2 = Query::EventHasStringArg("str");
+  Query event_arg1 =
+      (Query::EventArg("num") == Query::Double(event.arg_numbers["num"]));
+  Query event_arg2 =
+      (Query::EventArg("str") == Query::String(event.arg_strings["str"]));
+  Query event_has_other = Query::EventHasOther();
+  Query other_pid = Query::OtherPidIs(other.thread.process_id);
+  Query other_tid = Query::OtherTidIs(other.thread.thread_id);
+  Query other_time = Query::OtherTimeIs(other.timestamp);
+  Query other_phase = Query::OtherPhaseIs(other.phase);
+  Query other_category = Query::OtherCategoryIs(other.category);
+  Query other_name = Query::OtherNameIs(other.name);
+  Query other_id = Query::OtherIdIs(other.id);
+  Query other_has_arg1 = Query::OtherHasNumberArg("num2");
+  Query other_has_arg2 = Query::OtherHasStringArg("str2");
+  Query other_arg1 =
+      (Query::OtherArg("num2") == Query::Double(other.arg_numbers["num2"]));
+  Query other_arg2 =
+      (Query::OtherArg("str2") == Query::String(other.arg_strings["str2"]));
+
+  EXPECT_TRUE(event_pid.Evaluate(event));
+  EXPECT_TRUE(event_tid.Evaluate(event));
+  EXPECT_TRUE(event_time.Evaluate(event));
+  EXPECT_TRUE(event_duration.Evaluate(event));
+  EXPECT_TRUE(event_phase.Evaluate(event));
+  EXPECT_TRUE(event_category.Evaluate(event));
+  EXPECT_TRUE(event_name.Evaluate(event));
+  EXPECT_TRUE(event_id.Evaluate(event));
+  EXPECT_TRUE(event_has_arg1.Evaluate(event));
+  EXPECT_TRUE(event_has_arg2.Evaluate(event));
+  EXPECT_TRUE(event_arg1.Evaluate(event));
+  EXPECT_TRUE(event_arg2.Evaluate(event));
+  EXPECT_TRUE(event_has_other.Evaluate(event));
+  EXPECT_TRUE(other_pid.Evaluate(event));
+  EXPECT_TRUE(other_tid.Evaluate(event));
+  EXPECT_TRUE(other_time.Evaluate(event));
+  EXPECT_TRUE(other_phase.Evaluate(event));
+  EXPECT_TRUE(other_category.Evaluate(event));
+  EXPECT_TRUE(other_name.Evaluate(event));
+  EXPECT_TRUE(other_id.Evaluate(event));
+  EXPECT_TRUE(other_has_arg1.Evaluate(event));
+  EXPECT_TRUE(other_has_arg2.Evaluate(event));
+  EXPECT_TRUE(other_arg1.Evaluate(event));
+  EXPECT_TRUE(other_arg2.Evaluate(event));
+
+  // Evaluate event queries against other to verify the queries fail when the
+  // event members are wrong.
+  EXPECT_FALSE(event_pid.Evaluate(other));
+  EXPECT_FALSE(event_tid.Evaluate(other));
+  EXPECT_FALSE(event_time.Evaluate(other));
+  EXPECT_FALSE(event_duration.Evaluate(other));
+  EXPECT_FALSE(event_phase.Evaluate(other));
+  EXPECT_FALSE(event_category.Evaluate(other));
+  EXPECT_FALSE(event_name.Evaluate(other));
+  EXPECT_FALSE(event_id.Evaluate(other));
+  EXPECT_FALSE(event_has_arg1.Evaluate(other));
+  EXPECT_FALSE(event_has_arg2.Evaluate(other));
+  EXPECT_FALSE(event_arg1.Evaluate(other));
+  EXPECT_FALSE(event_arg2.Evaluate(other));
+  EXPECT_FALSE(event_has_other.Evaluate(other));
+}
+
+TEST_F(TraceEventAnalyzerTest, BooleanOperators) {
+  ManualSetUp();
+
+  BeginTracing();
+  {
+    TRACE_EVENT_INSTANT1("cat1", "name1", TRACE_EVENT_SCOPE_THREAD, "num", 1);
+    TRACE_EVENT_INSTANT1("cat1", "name2", TRACE_EVENT_SCOPE_THREAD, "num", 2);
+    TRACE_EVENT_INSTANT1("cat2", "name3", TRACE_EVENT_SCOPE_THREAD, "num", 3);
+    TRACE_EVENT_INSTANT1("cat2", "name4", TRACE_EVENT_SCOPE_THREAD, "num", 4);
+  }
+  EndTracing();
+
+  std::unique_ptr<TraceAnalyzer> analyzer(
+      TraceAnalyzer::Create(output_.json_output));
+  ASSERT_TRUE(analyzer);
+  analyzer->SetIgnoreMetadataEvents(true);
+
+  TraceEventVector found;
+
+  // ==
+
+  analyzer->FindEvents(Query::EventCategory() == Query::String("cat1"), &found);
+  ASSERT_EQ(2u, found.size());
+  EXPECT_STREQ("name1", found[0]->name.c_str());
+  EXPECT_STREQ("name2", found[1]->name.c_str());
+
+  analyzer->FindEvents(Query::EventArg("num") == Query::Int(2), &found);
+  ASSERT_EQ(1u, found.size());
+  EXPECT_STREQ("name2", found[0]->name.c_str());
+
+  // !=
+
+  analyzer->FindEvents(Query::EventCategory() != Query::String("cat1"), &found);
+  ASSERT_EQ(2u, found.size());
+  EXPECT_STREQ("name3", found[0]->name.c_str());
+  EXPECT_STREQ("name4", found[1]->name.c_str());
+
+  analyzer->FindEvents(Query::EventArg("num") != Query::Int(2), &found);
+  ASSERT_EQ(3u, found.size());
+  EXPECT_STREQ("name1", found[0]->name.c_str());
+  EXPECT_STREQ("name3", found[1]->name.c_str());
+  EXPECT_STREQ("name4", found[2]->name.c_str());
+
+  // <
+  analyzer->FindEvents(Query::EventArg("num") < Query::Int(2), &found);
+  ASSERT_EQ(1u, found.size());
+  EXPECT_STREQ("name1", found[0]->name.c_str());
+
+  // <=
+  analyzer->FindEvents(Query::EventArg("num") <= Query::Int(2), &found);
+  ASSERT_EQ(2u, found.size());
+  EXPECT_STREQ("name1", found[0]->name.c_str());
+  EXPECT_STREQ("name2", found[1]->name.c_str());
+
+  // >
+  analyzer->FindEvents(Query::EventArg("num") > Query::Int(3), &found);
+  ASSERT_EQ(1u, found.size());
+  EXPECT_STREQ("name4", found[0]->name.c_str());
+
+  // >=
+  analyzer->FindEvents(Query::EventArg("num") >= Query::Int(4), &found);
+  ASSERT_EQ(1u, found.size());
+  EXPECT_STREQ("name4", found[0]->name.c_str());
+
+  // &&
+  analyzer->FindEvents(Query::EventName() != Query::String("name1") &&
+                       Query::EventArg("num") < Query::Int(3), &found);
+  ASSERT_EQ(1u, found.size());
+  EXPECT_STREQ("name2", found[0]->name.c_str());
+
+  // ||
+  analyzer->FindEvents(Query::EventName() == Query::String("name1") ||
+                       Query::EventArg("num") == Query::Int(3), &found);
+  ASSERT_EQ(2u, found.size());
+  EXPECT_STREQ("name1", found[0]->name.c_str());
+  EXPECT_STREQ("name3", found[1]->name.c_str());
+
+  // !
+  analyzer->FindEvents(!(Query::EventName() == Query::String("name1") ||
+                         Query::EventArg("num") == Query::Int(3)), &found);
+  ASSERT_EQ(2u, found.size());
+  EXPECT_STREQ("name2", found[0]->name.c_str());
+  EXPECT_STREQ("name4", found[1]->name.c_str());
+}
+
+TEST_F(TraceEventAnalyzerTest, ArithmeticOperators) {
+  ManualSetUp();
+
+  BeginTracing();
+  {
+    // These events are searched for:
+    TRACE_EVENT_INSTANT2("cat1", "math1", TRACE_EVENT_SCOPE_THREAD,
+                         "a", 10, "b", 5);
+    TRACE_EVENT_INSTANT2("cat1", "math2", TRACE_EVENT_SCOPE_THREAD,
+                         "a", 10, "b", 10);
+    // Extra events that never match, for noise:
+    TRACE_EVENT_INSTANT2("noise", "math3", TRACE_EVENT_SCOPE_THREAD,
+                         "a", 1,  "b", 3);
+    TRACE_EVENT_INSTANT2("noise", "math4", TRACE_EVENT_SCOPE_THREAD,
+                         "c", 10, "d", 5);
+  }
+  EndTracing();
+
+  std::unique_ptr<TraceAnalyzer> analyzer(
+      TraceAnalyzer::Create(output_.json_output));
+  ASSERT_TRUE(analyzer.get());
+
+  TraceEventVector found;
+
+  // Verify that arithmetic operators function:
+
+  // +
+  analyzer->FindEvents(Query::EventArg("a") + Query::EventArg("b") ==
+                       Query::Int(20), &found);
+  EXPECT_EQ(1u, found.size());
+  EXPECT_STREQ("math2", found.front()->name.c_str());
+
+  // -
+  analyzer->FindEvents(Query::EventArg("a") - Query::EventArg("b") ==
+                       Query::Int(5), &found);
+  EXPECT_EQ(1u, found.size());
+  EXPECT_STREQ("math1", found.front()->name.c_str());
+
+  // *
+  analyzer->FindEvents(Query::EventArg("a") * Query::EventArg("b") ==
+                       Query::Int(50), &found);
+  EXPECT_EQ(1u, found.size());
+  EXPECT_STREQ("math1", found.front()->name.c_str());
+
+  // /
+  analyzer->FindEvents(Query::EventArg("a") / Query::EventArg("b") ==
+                       Query::Int(2), &found);
+  EXPECT_EQ(1u, found.size());
+  EXPECT_STREQ("math1", found.front()->name.c_str());
+
+  // %
+  analyzer->FindEvents(Query::EventArg("a") % Query::EventArg("b") ==
+                       Query::Int(0), &found);
+  EXPECT_EQ(2u, found.size());
+
+  // - (negate)
+  analyzer->FindEvents(-Query::EventArg("b") == Query::Int(-10), &found);
+  EXPECT_EQ(1u, found.size());
+  EXPECT_STREQ("math2", found.front()->name.c_str());
+}
+
+TEST_F(TraceEventAnalyzerTest, StringPattern) {
+  ManualSetUp();
+
+  BeginTracing();
+  {
+    TRACE_EVENT_INSTANT0("cat1", "name1", TRACE_EVENT_SCOPE_THREAD);
+    TRACE_EVENT_INSTANT0("cat1", "name2", TRACE_EVENT_SCOPE_THREAD);
+    TRACE_EVENT_INSTANT0("cat1", "no match", TRACE_EVENT_SCOPE_THREAD);
+    TRACE_EVENT_INSTANT0("cat1", "name3x", TRACE_EVENT_SCOPE_THREAD);
+  }
+  EndTracing();
+
+  std::unique_ptr<TraceAnalyzer> analyzer(
+      TraceAnalyzer::Create(output_.json_output));
+  ASSERT_TRUE(analyzer.get());
+  analyzer->SetIgnoreMetadataEvents(true);
+
+  TraceEventVector found;
+
+  analyzer->FindEvents(Query::EventName() == Query::Pattern("name?"), &found);
+  ASSERT_EQ(2u, found.size());
+  EXPECT_STREQ("name1", found[0]->name.c_str());
+  EXPECT_STREQ("name2", found[1]->name.c_str());
+
+  analyzer->FindEvents(Query::EventName() == Query::Pattern("name*"), &found);
+  ASSERT_EQ(3u, found.size());
+  EXPECT_STREQ("name1", found[0]->name.c_str());
+  EXPECT_STREQ("name2", found[1]->name.c_str());
+  EXPECT_STREQ("name3x", found[2]->name.c_str());
+
+  analyzer->FindEvents(Query::EventName() != Query::Pattern("name*"), &found);
+  ASSERT_EQ(1u, found.size());
+  EXPECT_STREQ("no match", found[0]->name.c_str());
+}
+
+// Test that duration queries work.
+TEST_F(TraceEventAnalyzerTest, BeginEndDuration) {
+  ManualSetUp();
+
+  const base::TimeDelta kSleepTime = base::TimeDelta::FromMilliseconds(200);
+  // We will search for events that have a duration of greater than 90% of the
+  // sleep time, so that there is no flakiness.
+  int64_t duration_cutoff_us = (kSleepTime.InMicroseconds() * 9) / 10;
+
+  BeginTracing();
+  {
+    TRACE_EVENT_BEGIN0("cat1", "name1"); // found by duration query
+    TRACE_EVENT_BEGIN0("noise", "name2"); // not searched for, just noise
+    {
+      TRACE_EVENT_BEGIN0("cat2", "name3"); // found by duration query
+      // next event not searched for, just noise
+      TRACE_EVENT_INSTANT0("noise", "name4", TRACE_EVENT_SCOPE_THREAD);
+      base::PlatformThread::Sleep(kSleepTime);
+      TRACE_EVENT_BEGIN0("cat2", "name5"); // not found (duration too short)
+      TRACE_EVENT_END0("cat2", "name5"); // not found (duration too short)
+      TRACE_EVENT_END0("cat2", "name3"); // found by duration query
+    }
+    TRACE_EVENT_END0("noise", "name2"); // not searched for, just noise
+    TRACE_EVENT_END0("cat1", "name1"); // found by duration query
+  }
+  EndTracing();
+
+  std::unique_ptr<TraceAnalyzer> analyzer(
+      TraceAnalyzer::Create(output_.json_output));
+  ASSERT_TRUE(analyzer.get());
+  analyzer->AssociateBeginEndEvents();
+
+  TraceEventVector found;
+  analyzer->FindEvents(
+      Query::MatchBeginWithEnd() &&
+      Query::EventDuration() >
+          Query::Int(static_cast<int>(duration_cutoff_us)) &&
+      (Query::EventCategory() == Query::String("cat1") ||
+       Query::EventCategory() == Query::String("cat2") ||
+       Query::EventCategory() == Query::String("cat3")),
+      &found);
+  ASSERT_EQ(2u, found.size());
+  EXPECT_STREQ("name1", found[0]->name.c_str());
+  EXPECT_STREQ("name3", found[1]->name.c_str());
+}
+
+// Test that duration queries work.
+TEST_F(TraceEventAnalyzerTest, CompleteDuration) {
+  ManualSetUp();
+
+  const base::TimeDelta kSleepTime = base::TimeDelta::FromMilliseconds(200);
+  // We will search for events that have a duration of greater than 90% of the
+  // sleep time, so that there is no flakiness.
+  int64_t duration_cutoff_us = (kSleepTime.InMicroseconds() * 9) / 10;
+
+  BeginTracing();
+  {
+    TRACE_EVENT0("cat1", "name1"); // found by duration query
+    TRACE_EVENT0("noise", "name2"); // not searched for, just noise
+    {
+      TRACE_EVENT0("cat2", "name3"); // found by duration query
+      // next event not searched for, just noise
+      TRACE_EVENT_INSTANT0("noise", "name4", TRACE_EVENT_SCOPE_THREAD);
+      base::PlatformThread::Sleep(kSleepTime);
+      TRACE_EVENT0("cat2", "name5"); // not found (duration too short)
+    }
+  }
+  EndTracing();
+
+  std::unique_ptr<TraceAnalyzer> analyzer(
+      TraceAnalyzer::Create(output_.json_output));
+  ASSERT_TRUE(analyzer.get());
+  analyzer->AssociateBeginEndEvents();
+
+  TraceEventVector found;
+  analyzer->FindEvents(
+      Query::EventCompleteDuration() >
+          Query::Int(static_cast<int>(duration_cutoff_us)) &&
+      (Query::EventCategory() == Query::String("cat1") ||
+       Query::EventCategory() == Query::String("cat2") ||
+       Query::EventCategory() == Query::String("cat3")),
+      &found);
+  ASSERT_EQ(2u, found.size());
+  EXPECT_STREQ("name1", found[0]->name.c_str());
+  EXPECT_STREQ("name3", found[1]->name.c_str());
+}
+
+// Test AssociateBeginEndEvents
+TEST_F(TraceEventAnalyzerTest, BeginEndAssocations) {
+  ManualSetUp();
+
+  BeginTracing();
+  {
+    TRACE_EVENT_END0("cat1", "name1"); // does not match out of order begin
+    TRACE_EVENT_BEGIN0("cat1", "name2");
+    TRACE_EVENT_INSTANT0("cat1", "name3", TRACE_EVENT_SCOPE_THREAD);
+    TRACE_EVENT_BEGIN0("cat1", "name1");
+    TRACE_EVENT_END0("cat1", "name2");
+  }
+  EndTracing();
+
+  std::unique_ptr<TraceAnalyzer> analyzer(
+      TraceAnalyzer::Create(output_.json_output));
+  ASSERT_TRUE(analyzer.get());
+  analyzer->AssociateBeginEndEvents();
+
+  TraceEventVector found;
+  analyzer->FindEvents(Query::MatchBeginWithEnd(), &found);
+  ASSERT_EQ(1u, found.size());
+  EXPECT_STREQ("name2", found[0]->name.c_str());
+}
+
+// Test MergeAssociatedEventArgs
+TEST_F(TraceEventAnalyzerTest, MergeAssociatedEventArgs) {
+  ManualSetUp();
+
+  const char arg_string[] = "arg_string";
+  BeginTracing();
+  {
+    TRACE_EVENT_BEGIN0("cat1", "name1");
+    TRACE_EVENT_END1("cat1", "name1", "arg", arg_string);
+  }
+  EndTracing();
+
+  std::unique_ptr<TraceAnalyzer> analyzer(
+      TraceAnalyzer::Create(output_.json_output));
+  ASSERT_TRUE(analyzer.get());
+  analyzer->AssociateBeginEndEvents();
+
+  TraceEventVector found;
+  analyzer->FindEvents(Query::MatchBeginName("name1"), &found);
+  ASSERT_EQ(1u, found.size());
+  std::string arg_actual;
+  EXPECT_FALSE(found[0]->GetArgAsString("arg", &arg_actual));
+
+  analyzer->MergeAssociatedEventArgs();
+  EXPECT_TRUE(found[0]->GetArgAsString("arg", &arg_actual));
+  EXPECT_STREQ(arg_string, arg_actual.c_str());
+}
+
+// Test AssociateAsyncBeginEndEvents
+TEST_F(TraceEventAnalyzerTest, AsyncBeginEndAssocations) {
+  ManualSetUp();
+
+  BeginTracing();
+  {
+    TRACE_EVENT_ASYNC_END0("cat1", "name1", 0xA); // no match / out of order
+    TRACE_EVENT_ASYNC_BEGIN0("cat1", "name1", 0xB);
+    TRACE_EVENT_ASYNC_BEGIN0("cat1", "name1", 0xC);
+    TRACE_EVENT_INSTANT0("cat1", "name1", TRACE_EVENT_SCOPE_THREAD); // noise
+    TRACE_EVENT0("cat1", "name1"); // noise
+    TRACE_EVENT_ASYNC_END0("cat1", "name1", 0xB);
+    TRACE_EVENT_ASYNC_END0("cat1", "name1", 0xC);
+    TRACE_EVENT_ASYNC_BEGIN0("cat1", "name1", 0xA); // no match / out of order
+  }
+  EndTracing();
+
+  std::unique_ptr<TraceAnalyzer> analyzer(
+      TraceAnalyzer::Create(output_.json_output));
+  ASSERT_TRUE(analyzer.get());
+  analyzer->AssociateAsyncBeginEndEvents();
+
+  TraceEventVector found;
+  analyzer->FindEvents(Query::MatchAsyncBeginWithNext(), &found);
+  ASSERT_EQ(2u, found.size());
+  EXPECT_STRCASEEQ("0xb", found[0]->id.c_str());
+  EXPECT_STRCASEEQ("0xc", found[1]->id.c_str());
+}
+
+// Test AssociateAsyncBeginEndEvents
+TEST_F(TraceEventAnalyzerTest, AsyncBeginEndAssocationsWithSteps) {
+  ManualSetUp();
+
+  BeginTracing();
+  {
+    TRACE_EVENT_ASYNC_STEP_INTO0("c", "n", 0xA, "s1");
+    TRACE_EVENT_ASYNC_END0("c", "n", 0xA);
+    TRACE_EVENT_ASYNC_BEGIN0("c", "n", 0xB);
+    TRACE_EVENT_ASYNC_BEGIN0("c", "n", 0xC);
+    TRACE_EVENT_ASYNC_STEP_PAST0("c", "n", 0xB, "s1");
+    TRACE_EVENT_ASYNC_STEP_INTO0("c", "n", 0xC, "s1");
+    TRACE_EVENT_ASYNC_STEP_INTO1("c", "n", 0xC, "s2", "a", 1);
+    TRACE_EVENT_ASYNC_END0("c", "n", 0xB);
+    TRACE_EVENT_ASYNC_END0("c", "n", 0xC);
+    TRACE_EVENT_ASYNC_BEGIN0("c", "n", 0xA);
+    TRACE_EVENT_ASYNC_STEP_INTO0("c", "n", 0xA, "s2");
+  }
+  EndTracing();
+
+  std::unique_ptr<TraceAnalyzer> analyzer(
+      TraceAnalyzer::Create(output_.json_output));
+  ASSERT_TRUE(analyzer.get());
+  analyzer->AssociateAsyncBeginEndEvents();
+
+  TraceEventVector found;
+  analyzer->FindEvents(Query::MatchAsyncBeginWithNext(), &found);
+  ASSERT_EQ(3u, found.size());
+
+  EXPECT_STRCASEEQ("0xb", found[0]->id.c_str());
+  EXPECT_EQ(TRACE_EVENT_PHASE_ASYNC_STEP_PAST, found[0]->other_event->phase);
+  EXPECT_EQ(found[0], found[0]->other_event->prev_event);
+  EXPECT_TRUE(found[0]->other_event->other_event);
+  EXPECT_EQ(TRACE_EVENT_PHASE_ASYNC_END,
+            found[0]->other_event->other_event->phase);
+  EXPECT_EQ(found[0]->other_event,
+            found[0]->other_event->other_event->prev_event);
+
+  EXPECT_STRCASEEQ("0xc", found[1]->id.c_str());
+  EXPECT_EQ(TRACE_EVENT_PHASE_ASYNC_STEP_INTO, found[1]->other_event->phase);
+  EXPECT_EQ(found[1], found[1]->other_event->prev_event);
+  EXPECT_TRUE(found[1]->other_event->other_event);
+  EXPECT_EQ(TRACE_EVENT_PHASE_ASYNC_STEP_INTO,
+            found[1]->other_event->other_event->phase);
+  EXPECT_EQ(found[1]->other_event,
+            found[1]->other_event->other_event->prev_event);
+  double arg_actual = 0;
+  EXPECT_TRUE(found[1]->other_event->other_event->GetArgAsNumber(
+                  "a", &arg_actual));
+  EXPECT_EQ(1.0, arg_actual);
+  EXPECT_TRUE(found[1]->other_event->other_event->other_event);
+  EXPECT_EQ(TRACE_EVENT_PHASE_ASYNC_END,
+            found[1]->other_event->other_event->other_event->phase);
+
+  EXPECT_STRCASEEQ("0xa", found[2]->id.c_str());
+  EXPECT_EQ(TRACE_EVENT_PHASE_ASYNC_STEP_INTO, found[2]->other_event->phase);
+}
+
+// Test that the TraceAnalyzer custom associations work.
+TEST_F(TraceEventAnalyzerTest, CustomAssociations) {
+  ManualSetUp();
+
+  // Add events that begin/end in pipelined ordering with unique ID parameter
+  // to match up the begin/end pairs.
+  BeginTracing();
+  {
+    // no begin match
+    TRACE_EVENT_INSTANT1("cat1", "end", TRACE_EVENT_SCOPE_THREAD, "id", 1);
+    // end is cat4
+    TRACE_EVENT_INSTANT1("cat2", "begin", TRACE_EVENT_SCOPE_THREAD, "id", 2);
+    // end is cat5
+    TRACE_EVENT_INSTANT1("cat3", "begin", TRACE_EVENT_SCOPE_THREAD, "id", 3);
+    TRACE_EVENT_INSTANT1("cat4", "end", TRACE_EVENT_SCOPE_THREAD, "id", 2);
+    TRACE_EVENT_INSTANT1("cat5", "end", TRACE_EVENT_SCOPE_THREAD, "id", 3);
+    // no end match
+    TRACE_EVENT_INSTANT1("cat6", "begin", TRACE_EVENT_SCOPE_THREAD, "id", 1);
+  }
+  EndTracing();
+
+  std::unique_ptr<TraceAnalyzer> analyzer(
+      TraceAnalyzer::Create(output_.json_output));
+  ASSERT_TRUE(analyzer.get());
+
+  // begin, end, and match queries to find proper begin/end pairs.
+  Query begin(Query::EventName() == Query::String("begin"));
+  Query end(Query::EventName() == Query::String("end"));
+  Query match(Query::EventArg("id") == Query::OtherArg("id"));
+  analyzer->AssociateEvents(begin, end, match);
+
+  TraceEventVector found;
+
+  // cat1 has no other_event.
+  analyzer->FindEvents(Query::EventCategory() == Query::String("cat1") &&
+                       Query::EventHasOther(), &found);
+  EXPECT_EQ(0u, found.size());
+
+  // cat1 has no other_event.
+  analyzer->FindEvents(Query::EventCategory() == Query::String("cat1") &&
+                       !Query::EventHasOther(), &found);
+  EXPECT_EQ(1u, found.size());
+
+  // cat6 has no other_event.
+  analyzer->FindEvents(Query::EventCategory() == Query::String("cat6") &&
+                       !Query::EventHasOther(), &found);
+  EXPECT_EQ(1u, found.size());
+
+  // cat2 and cat4 are associated.
+  analyzer->FindEvents(Query::EventCategory() == Query::String("cat2") &&
+                       Query::OtherCategory() == Query::String("cat4"), &found);
+  EXPECT_EQ(1u, found.size());
+
+  // cat4 and cat2 are not associated.
+  analyzer->FindEvents(Query::EventCategory() == Query::String("cat4") &&
+                       Query::OtherCategory() == Query::String("cat2"), &found);
+  EXPECT_EQ(0u, found.size());
+
+  // cat3 and cat5 are associated.
+  analyzer->FindEvents(Query::EventCategory() == Query::String("cat3") &&
+                       Query::OtherCategory() == Query::String("cat5"), &found);
+  EXPECT_EQ(1u, found.size());
+
+  // cat5 and cat3 are not associated.
+  analyzer->FindEvents(Query::EventCategory() == Query::String("cat5") &&
+                       Query::OtherCategory() == Query::String("cat3"), &found);
+  EXPECT_EQ(0u, found.size());
+}
+
+// Verify that Query literals and types are properly casted.
+TEST_F(TraceEventAnalyzerTest, Literals) {
+  ManualSetUp();
+
+  // Since these queries don't refer to the event data, the dummy event below
+  // will never be accessed.
+  TraceEvent dummy;
+  char char_num = 5;
+  short short_num = -5;
+  EXPECT_TRUE((Query::Double(5.0) == Query::Int(char_num)).Evaluate(dummy));
+  EXPECT_TRUE((Query::Double(-5.0) == Query::Int(short_num)).Evaluate(dummy));
+  EXPECT_TRUE((Query::Double(1.0) == Query::Uint(1u)).Evaluate(dummy));
+  EXPECT_TRUE((Query::Double(1.0) == Query::Int(1)).Evaluate(dummy));
+  EXPECT_TRUE((Query::Double(-1.0) == Query::Int(-1)).Evaluate(dummy));
+  EXPECT_TRUE((Query::Double(1.0) == Query::Double(1.0f)).Evaluate(dummy));
+  EXPECT_TRUE((Query::Bool(true) == Query::Int(1)).Evaluate(dummy));
+  EXPECT_TRUE((Query::Bool(false) == Query::Int(0)).Evaluate(dummy));
+  EXPECT_TRUE((Query::Bool(true) == Query::Double(1.0f)).Evaluate(dummy));
+  EXPECT_TRUE((Query::Bool(false) == Query::Double(0.0f)).Evaluate(dummy));
+}
+
+// Test GetRateStats.
+TEST_F(TraceEventAnalyzerTest, RateStats) {
+  std::vector<TraceEvent> events;
+  events.reserve(100);
+  TraceEventVector event_ptrs;
+  double timestamp = 0.0;
+  double little_delta = 1.0;
+  double big_delta = 10.0;
+  double tiny_delta = 0.1;
+  RateStats stats;
+  RateStatsOptions options;
+
+  // Insert 10 events, each apart by little_delta.
+  for (int i = 0; i < 10; ++i) {
+    timestamp += little_delta;
+    TraceEvent event;
+    event.timestamp = timestamp;
+    events.push_back(std::move(event));
+    event_ptrs.push_back(&events.back());
+  }
+
+  ASSERT_TRUE(GetRateStats(event_ptrs, &stats, nullptr));
+  EXPECT_EQ(little_delta, stats.mean_us);
+  EXPECT_EQ(little_delta, stats.min_us);
+  EXPECT_EQ(little_delta, stats.max_us);
+  EXPECT_EQ(0.0, stats.standard_deviation_us);
+
+  // Add an event apart by big_delta.
+  {
+    timestamp += big_delta;
+    TraceEvent event;
+    event.timestamp = timestamp;
+    events.push_back(std::move(event));
+    event_ptrs.push_back(&events.back());
+  }
+
+  ASSERT_TRUE(GetRateStats(event_ptrs, &stats, nullptr));
+  EXPECT_LT(little_delta, stats.mean_us);
+  EXPECT_EQ(little_delta, stats.min_us);
+  EXPECT_EQ(big_delta, stats.max_us);
+  EXPECT_LT(0.0, stats.standard_deviation_us);
+
+  // Trim off the biggest delta and verify stats.
+  options.trim_min = 0;
+  options.trim_max = 1;
+  ASSERT_TRUE(GetRateStats(event_ptrs, &stats, &options));
+  EXPECT_EQ(little_delta, stats.mean_us);
+  EXPECT_EQ(little_delta, stats.min_us);
+  EXPECT_EQ(little_delta, stats.max_us);
+  EXPECT_EQ(0.0, stats.standard_deviation_us);
+
+  // Add an event apart by tiny_delta.
+  {
+    timestamp += tiny_delta;
+    TraceEvent event;
+    event.timestamp = timestamp;
+    events.push_back(std::move(event));
+    event_ptrs.push_back(&events.back());
+  }
+
+  // Trim off both the biggest and tiniest delta and verify stats.
+  options.trim_min = 1;
+  options.trim_max = 1;
+  ASSERT_TRUE(GetRateStats(event_ptrs, &stats, &options));
+  EXPECT_EQ(little_delta, stats.mean_us);
+  EXPECT_EQ(little_delta, stats.min_us);
+  EXPECT_EQ(little_delta, stats.max_us);
+  EXPECT_EQ(0.0, stats.standard_deviation_us);
+
+  // Verify smallest allowed number of events.
+  {
+    TraceEvent event;
+    TraceEventVector few_event_ptrs;
+    few_event_ptrs.push_back(&event);
+    few_event_ptrs.push_back(&event);
+    ASSERT_FALSE(GetRateStats(few_event_ptrs, &stats, nullptr));
+    few_event_ptrs.push_back(&event);
+    ASSERT_TRUE(GetRateStats(few_event_ptrs, &stats, nullptr));
+
+    // Trim off more than allowed and verify failure.
+    options.trim_min = 0;
+    options.trim_max = 1;
+    ASSERT_FALSE(GetRateStats(few_event_ptrs, &stats, &options));
+  }
+}
+
+// Test FindFirstOf and FindLastOf.
+TEST_F(TraceEventAnalyzerTest, FindOf) {
+  size_t num_events = 100;
+  size_t index = 0;
+  TraceEventVector event_ptrs;
+  EXPECT_FALSE(FindFirstOf(event_ptrs, Query::Bool(true), 0, &index));
+  EXPECT_FALSE(FindFirstOf(event_ptrs, Query::Bool(true), 10, &index));
+  EXPECT_FALSE(FindLastOf(event_ptrs, Query::Bool(true), 0, &index));
+  EXPECT_FALSE(FindLastOf(event_ptrs, Query::Bool(true), 10, &index));
+
+  std::vector<TraceEvent> events;
+  events.resize(num_events);
+  for (size_t i = 0; i < events.size(); ++i)
+    event_ptrs.push_back(&events[i]);
+  size_t bam_index = num_events/2;
+  events[bam_index].name = "bam";
+  Query query_bam = Query::EventName() == Query::String(events[bam_index].name);
+
+  // FindFirstOf
+  EXPECT_FALSE(FindFirstOf(event_ptrs, Query::Bool(false), 0, &index));
+  EXPECT_TRUE(FindFirstOf(event_ptrs, Query::Bool(true), 0, &index));
+  EXPECT_EQ(0u, index);
+  EXPECT_TRUE(FindFirstOf(event_ptrs, Query::Bool(true), 5, &index));
+  EXPECT_EQ(5u, index);
+
+  EXPECT_FALSE(FindFirstOf(event_ptrs, query_bam, bam_index + 1, &index));
+  EXPECT_TRUE(FindFirstOf(event_ptrs, query_bam, 0, &index));
+  EXPECT_EQ(bam_index, index);
+  EXPECT_TRUE(FindFirstOf(event_ptrs, query_bam, bam_index, &index));
+  EXPECT_EQ(bam_index, index);
+
+  // FindLastOf
+  EXPECT_FALSE(FindLastOf(event_ptrs, Query::Bool(false), 1000, &index));
+  EXPECT_TRUE(FindLastOf(event_ptrs, Query::Bool(true), 1000, &index));
+  EXPECT_EQ(num_events - 1, index);
+  EXPECT_TRUE(FindLastOf(event_ptrs, Query::Bool(true), num_events - 5,
+                         &index));
+  EXPECT_EQ(num_events - 5, index);
+
+  EXPECT_FALSE(FindLastOf(event_ptrs, query_bam, bam_index - 1, &index));
+  EXPECT_TRUE(FindLastOf(event_ptrs, query_bam, num_events, &index));
+  EXPECT_EQ(bam_index, index);
+  EXPECT_TRUE(FindLastOf(event_ptrs, query_bam, bam_index, &index));
+  EXPECT_EQ(bam_index, index);
+}
+
+// Test FindClosest.
+TEST_F(TraceEventAnalyzerTest, FindClosest) {
+  size_t index_1 = 0;
+  size_t index_2 = 0;
+  TraceEventVector event_ptrs;
+  EXPECT_FALSE(FindClosest(event_ptrs, Query::Bool(true), 0,
+                           &index_1, &index_2));
+
+  size_t num_events = 5;
+  std::vector<TraceEvent> events;
+  events.resize(num_events);
+  for (size_t i = 0; i < events.size(); ++i) {
+    // timestamps go up exponentially so the lower index is always closer in
+    // time than the higher index.
+    events[i].timestamp = static_cast<double>(i) * static_cast<double>(i);
+    event_ptrs.push_back(&events[i]);
+  }
+  events[0].name = "one";
+  events[2].name = "two";
+  events[4].name = "three";
+  Query query_named = Query::EventName() != Query::String(std::string());
+  Query query_one = Query::EventName() == Query::String("one");
+
+  // Only one event matches query_one, so two closest can't be found.
+  EXPECT_FALSE(FindClosest(event_ptrs, query_one, 0, &index_1, &index_2));
+
+  EXPECT_TRUE(FindClosest(event_ptrs, query_one, 3, &index_1, nullptr));
+  EXPECT_EQ(0u, index_1);
+
+  EXPECT_TRUE(FindClosest(event_ptrs, query_named, 1, &index_1, &index_2));
+  EXPECT_EQ(0u, index_1);
+  EXPECT_EQ(2u, index_2);
+
+  EXPECT_TRUE(FindClosest(event_ptrs, query_named, 4, &index_1, &index_2));
+  EXPECT_EQ(4u, index_1);
+  EXPECT_EQ(2u, index_2);
+
+  EXPECT_TRUE(FindClosest(event_ptrs, query_named, 3, &index_1, &index_2));
+  EXPECT_EQ(2u, index_1);
+  EXPECT_EQ(0u, index_2);
+}
+
+// Test CountMatches.
+TEST_F(TraceEventAnalyzerTest, CountMatches) {
+  TraceEventVector event_ptrs;
+  EXPECT_EQ(0u, CountMatches(event_ptrs, Query::Bool(true), 0, 10));
+
+  size_t num_events = 5;
+  size_t num_named = 3;
+  std::vector<TraceEvent> events;
+  events.resize(num_events);
+  for (size_t i = 0; i < events.size(); ++i)
+    event_ptrs.push_back(&events[i]);
+  events[0].name = "one";
+  events[2].name = "two";
+  events[4].name = "three";
+  Query query_named = Query::EventName() != Query::String(std::string());
+  Query query_one = Query::EventName() == Query::String("one");
+
+  EXPECT_EQ(0u, CountMatches(event_ptrs, Query::Bool(false)));
+  EXPECT_EQ(num_events, CountMatches(event_ptrs, Query::Bool(true)));
+  EXPECT_EQ(num_events - 1, CountMatches(event_ptrs, Query::Bool(true),
+                                         1, num_events));
+  EXPECT_EQ(1u, CountMatches(event_ptrs, query_one));
+  EXPECT_EQ(num_events - 1, CountMatches(event_ptrs, !query_one));
+  EXPECT_EQ(num_named, CountMatches(event_ptrs, query_named));
+}
+
+TEST_F(TraceEventAnalyzerTest, ComplexArgument) {
+  ManualSetUp();
+
+  BeginTracing();
+  {
+    std::unique_ptr<base::trace_event::TracedValue> value(
+        new base::trace_event::TracedValue);
+    value->SetString("property", "value");
+    TRACE_EVENT1("cat", "name", "arg", std::move(value));
+  }
+  EndTracing();
+
+  std::unique_ptr<TraceAnalyzer> analyzer(
+      TraceAnalyzer::Create(output_.json_output));
+  ASSERT_TRUE(analyzer.get());
+
+  TraceEventVector events;
+  analyzer->FindEvents(Query::EventName() == Query::String("name"), &events);
+
+  EXPECT_EQ(1u, events.size());
+  EXPECT_EQ("cat", events[0]->category);
+  EXPECT_EQ("name", events[0]->name);
+  EXPECT_TRUE(events[0]->HasArg("arg"));
+
+  std::unique_ptr<base::Value> arg;
+  events[0]->GetArgAsValue("arg", &arg);
+  base::DictionaryValue* arg_dict;
+  EXPECT_TRUE(arg->GetAsDictionary(&arg_dict));
+  std::string property;
+  EXPECT_TRUE(arg_dict->GetString("property", &property));
+  EXPECT_EQ("value", property);
+}
+
+}  // namespace trace_analyzer
diff --git a/base/test/trace_to_file.cc b/base/test/trace_to_file.cc
new file mode 100644
index 0000000..17aa80b
--- /dev/null
+++ b/base/test/trace_to_file.cc
@@ -0,0 +1,106 @@
+// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/trace_to_file.h"
+
+#include "base/base_switches.h"
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/files/file_util.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/run_loop.h"
+#include "base/trace_event/trace_buffer.h"
+#include "base/trace_event/trace_log.h"
+
+namespace base {
+namespace test {
+
+TraceToFile::TraceToFile() : started_(false) {
+}
+
+TraceToFile::~TraceToFile() {
+  EndTracingIfNeeded();
+}
+
+void TraceToFile::BeginTracingFromCommandLineOptions() {
+  DCHECK(CommandLine::InitializedForCurrentProcess());
+  DCHECK(!started_);
+
+  if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kTraceToFile))
+    return;
+
+  // Empty filter (i.e. just --trace-to-file) turns into default categories in
+  // TraceEventImpl
+  std::string filter = CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+      switches::kTraceToFile);
+
+  FilePath path;
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kTraceToFileName)) {
+    path = FilePath(CommandLine::ForCurrentProcess()
+                        ->GetSwitchValuePath(switches::kTraceToFileName));
+  } else {
+    path = FilePath(FILE_PATH_LITERAL("trace.json"));
+  }
+
+  BeginTracing(path, filter);
+}
+
+void TraceToFile::BeginTracing(const FilePath& path,
+                               const std::string& categories) {
+  DCHECK(!started_);
+  started_ = true;
+  path_ = path;
+  WriteFileHeader();
+
+  trace_event::TraceLog::GetInstance()->SetEnabled(
+      trace_event::TraceConfig(categories, trace_event::RECORD_UNTIL_FULL),
+      trace_event::TraceLog::RECORDING_MODE);
+}
+
+void TraceToFile::WriteFileHeader() {
+  const char str[] = "{\"traceEvents\": [";
+  WriteFile(path_, str, static_cast<int>(strlen(str)));
+}
+
+void TraceToFile::AppendFileFooter() {
+  const char str[] = "]}";
+  AppendToFile(path_, str, static_cast<int>(strlen(str)));
+}
+
+void TraceToFile::TraceOutputCallback(const std::string& data) {
+  bool ret = AppendToFile(path_, data.c_str(), static_cast<int>(data.size()));
+  DCHECK(ret);
+}
+
+static void OnTraceDataCollected(
+    Closure quit_closure,
+    trace_event::TraceResultBuffer* buffer,
+    const scoped_refptr<RefCountedString>& json_events_str,
+    bool has_more_events) {
+  buffer->AddFragment(json_events_str->data());
+  if (!has_more_events)
+    quit_closure.Run();
+}
+
+void TraceToFile::EndTracingIfNeeded() {
+  if (!started_)
+    return;
+  started_ = false;
+
+  trace_event::TraceLog::GetInstance()->SetDisabled();
+
+  trace_event::TraceResultBuffer buffer;
+  buffer.SetOutputCallback(
+      Bind(&TraceToFile::TraceOutputCallback, Unretained(this)));
+
+  RunLoop run_loop;
+  trace_event::TraceLog::GetInstance()->Flush(
+      Bind(&OnTraceDataCollected, run_loop.QuitClosure(), Unretained(&buffer)));
+  run_loop.Run();
+
+  AppendFileFooter();
+}
+
+}  // namespace test
+}  // namespace base
diff --git a/base/test/trace_to_file.h b/base/test/trace_to_file.h
new file mode 100644
index 0000000..4308736
--- /dev/null
+++ b/base/test/trace_to_file.h
@@ -0,0 +1,35 @@
+// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_TRACE_TO_FILE_H_
+#define BASE_TEST_TRACE_TO_FILE_H_
+
+#include "base/files/file_path.h"
+
+namespace base {
+namespace test {
+
+class TraceToFile {
+ public:
+  TraceToFile();
+  ~TraceToFile();
+
+  void BeginTracingFromCommandLineOptions();
+  void BeginTracing(const base::FilePath& path, const std::string& categories);
+  void EndTracingIfNeeded();
+
+ private:
+  void WriteFileHeader();
+  void AppendFileFooter();
+
+  void TraceOutputCallback(const std::string& data);
+
+  base::FilePath path_;
+  bool started_;
+};
+
+}  // namespace test
+}  // namespace base
+
+#endif  // BASE_TEST_TRACE_TO_FILE_H_
diff --git a/base/test/values_test_util.cc b/base/test/values_test_util.cc
new file mode 100644
index 0000000..a65c2c0
--- /dev/null
+++ b/base/test/values_test_util.cc
@@ -0,0 +1,76 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/values_test_util.h"
+
+#include <memory>
+
+#include "base/json/json_reader.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/values.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+void ExpectDictBooleanValue(bool expected_value,
+                            const DictionaryValue& value,
+                            const std::string& key) {
+  bool boolean_value = false;
+  EXPECT_TRUE(value.GetBoolean(key, &boolean_value)) << key;
+  EXPECT_EQ(expected_value, boolean_value) << key;
+}
+
+void ExpectDictDictionaryValue(const DictionaryValue& expected_value,
+                               const DictionaryValue& value,
+                               const std::string& key) {
+  const DictionaryValue* dict_value = nullptr;
+  EXPECT_TRUE(value.GetDictionary(key, &dict_value)) << key;
+  EXPECT_EQ(expected_value, *dict_value) << key;
+}
+
+void ExpectDictIntegerValue(int expected_value,
+                            const DictionaryValue& value,
+                            const std::string& key) {
+  int integer_value = 0;
+  EXPECT_TRUE(value.GetInteger(key, &integer_value)) << key;
+  EXPECT_EQ(expected_value, integer_value) << key;
+}
+
+void ExpectDictListValue(const ListValue& expected_value,
+                         const DictionaryValue& value,
+                         const std::string& key) {
+  const ListValue* list_value = nullptr;
+  EXPECT_TRUE(value.GetList(key, &list_value)) << key;
+  EXPECT_EQ(expected_value, *list_value) << key;
+}
+
+void ExpectDictStringValue(const std::string& expected_value,
+                           const DictionaryValue& value,
+                           const std::string& key) {
+  std::string string_value;
+  EXPECT_TRUE(value.GetString(key, &string_value)) << key;
+  EXPECT_EQ(expected_value, string_value) << key;
+}
+
+void ExpectStringValue(const std::string& expected_str, const Value& actual) {
+  EXPECT_EQ(Value::Type::STRING, actual.type());
+  EXPECT_EQ(expected_str, actual.GetString());
+}
+
+namespace test {
+
+std::unique_ptr<Value> ParseJson(base::StringPiece json) {
+  std::string error_msg;
+  std::unique_ptr<Value> result = base::JSONReader::ReadAndReturnError(
+      json, base::JSON_ALLOW_TRAILING_COMMAS, nullptr, &error_msg);
+  if (!result) {
+    ADD_FAILURE() << "Failed to parse \"" << json << "\": " << error_msg;
+    result = std::make_unique<Value>();
+  }
+  return result;
+}
+
+}  // namespace test
+}  // namespace base
diff --git a/base/test/values_test_util.h b/base/test/values_test_util.h
new file mode 100644
index 0000000..02ebca1
--- /dev/null
+++ b/base/test/values_test_util.h
@@ -0,0 +1,53 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_VALUES_TEST_UTIL_H_
+#define BASE_TEST_VALUES_TEST_UTIL_H_
+
+#include <memory>
+#include <string>
+
+#include "base/strings/string_piece.h"
+
+namespace base {
+class DictionaryValue;
+class ListValue;
+class Value;
+
+// All the functions below expect that the value for the given key in
+// the given dictionary equals the given expected value.
+
+void ExpectDictBooleanValue(bool expected_value,
+                            const DictionaryValue& value,
+                            const std::string& key);
+
+void ExpectDictDictionaryValue(const DictionaryValue& expected_value,
+                               const DictionaryValue& value,
+                               const std::string& key);
+
+void ExpectDictIntegerValue(int expected_value,
+                            const DictionaryValue& value,
+                            const std::string& key);
+
+void ExpectDictListValue(const ListValue& expected_value,
+                         const DictionaryValue& value,
+                         const std::string& key);
+
+void ExpectDictStringValue(const std::string& expected_value,
+                           const DictionaryValue& value,
+                           const std::string& key);
+
+void ExpectStringValue(const std::string& expected_str, const Value& actual);
+
+namespace test {
+
+// Parses |json| as JSON, allowing trailing commas, and returns the
+// resulting value.  If the json fails to parse, causes an EXPECT
+// failure and returns the Null Value (but never a NULL pointer).
+std::unique_ptr<Value> ParseJson(base::StringPiece json);
+
+}  // namespace test
+}  // namespace base
+
+#endif  // BASE_TEST_VALUES_TEST_UTIL_H_
diff --git a/base/third_party/dynamic_annotations/LICENSE b/base/third_party/dynamic_annotations/LICENSE
new file mode 100644
index 0000000..5c581a9
--- /dev/null
+++ b/base/third_party/dynamic_annotations/LICENSE
@@ -0,0 +1,28 @@
+/* Copyright (c) 2008-2009, 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.
+ *     * 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.
+ *
+ * ---
+ * Author: Kostya Serebryany
+ */
diff --git a/base/third_party/dynamic_annotations/README.chromium b/base/third_party/dynamic_annotations/README.chromium
new file mode 100644
index 0000000..c029f8e
--- /dev/null
+++ b/base/third_party/dynamic_annotations/README.chromium
@@ -0,0 +1,23 @@
+Name: dynamic annotations
+URL: http://code.google.com/p/data-race-test/wiki/DynamicAnnotations
+Version: 4384
+License: BSD
+
+ATTENTION: please avoid using these annotations in Chromium code.
+They were mainly intended to instruct the Valgrind-based version of
+ThreadSanitizer to handle atomic operations. The new version of ThreadSanitizer
+based on compiler instrumentation understands atomic operations out of the box,
+so normally you don't need the annotations.
+If you still think you do, please consider writing a comment at http://crbug.com/349861
+
+One header and one source file (dynamic_annotations.h and dynamic_annotations.c)
+in this directory define runtime macros useful for annotating synchronization
+utilities and benign data races so data race detectors can handle Chromium code
+with better precision.
+
+These files were taken from
+http://code.google.com/p/data-race-test/source/browse/?#svn/trunk/dynamic_annotations
+The files are covered under BSD license as described within the files.
+
+Local modifications:
+* made lineno an unsigned short (for -Wconstant-conversion warning fixes)
diff --git a/base/third_party/valgrind/LICENSE b/base/third_party/valgrind/LICENSE
new file mode 100644
index 0000000..41f677b
--- /dev/null
+++ b/base/third_party/valgrind/LICENSE
@@ -0,0 +1,39 @@
+   Notice that the following BSD-style license applies to the Valgrind header
+   files used by Chromium (valgrind.h and memcheck.h). However, the rest of
+   Valgrind is licensed under the terms of the GNU General Public License,
+   version 2, unless otherwise indicated.
+
+   ----------------------------------------------------------------
+
+   Copyright (C) 2000-2008 Julian Seward.  All rights reserved.
+
+   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. The origin of this software must not be misrepresented; you must 
+      not claim that you wrote the original software.  If you use this 
+      software in a product, an acknowledgment in the product 
+      documentation would be appreciated but is not required.
+
+   3. Altered source versions must be plainly marked as such, and must
+      not be misrepresented as being the original software.
+
+   4. The name of the author may not 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.
diff --git a/base/third_party/valgrind/README.chromium b/base/third_party/valgrind/README.chromium
new file mode 100644
index 0000000..56a1cbb
--- /dev/null
+++ b/base/third_party/valgrind/README.chromium
@@ -0,0 +1,11 @@
+Name: valgrind
+URL: http://valgrind.org
+License: BSD
+
+Header files in this directory define runtime macros that determine whether the
+current process is running under Valgrind and tell Memcheck tool about custom
+memory allocators.
+
+These header files were taken from Valgrind source code
+(svn://svn.valgrind.org/valgrind/trunk@11504, dated 21 Jan 2011). The files are
+covered under BSD license as described within.
diff --git a/base/threading/platform_thread_android.cc b/base/threading/platform_thread_android.cc
new file mode 100644
index 0000000..fd90d35
--- /dev/null
+++ b/base/threading/platform_thread_android.cc
@@ -0,0 +1,96 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/threading/platform_thread.h"
+
+#include <errno.h>
+#include <stddef.h>
+#include <sys/prctl.h>
+#include <sys/resource.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "base/android/jni_android.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/threading/platform_thread_internal_posix.h"
+#include "base/threading/thread_id_name_manager.h"
+#include "jni/ThreadUtils_jni.h"
+
+namespace base {
+
+namespace internal {
+
+// - BACKGROUND corresponds to Android's PRIORITY_BACKGROUND = 10 value and can
+// result in heavy throttling and force the thread onto a little core on
+// big.LITTLE devices.
+// - DISPLAY corresponds to Android's PRIORITY_DISPLAY = -4 value.
+// - REALTIME_AUDIO corresponds to Android's PRIORITY_AUDIO = -16 value.
+const ThreadPriorityToNiceValuePair kThreadPriorityToNiceValueMap[4] = {
+    {ThreadPriority::BACKGROUND, 10},
+    {ThreadPriority::NORMAL, 0},
+    {ThreadPriority::DISPLAY, -4},
+    {ThreadPriority::REALTIME_AUDIO, -16},
+};
+
+bool SetCurrentThreadPriorityForPlatform(ThreadPriority priority) {
+  // On Android, we set the Audio priority through JNI as Audio priority
+  // will also allow the process to run while it is backgrounded.
+  if (priority == ThreadPriority::REALTIME_AUDIO) {
+    JNIEnv* env = base::android::AttachCurrentThread();
+    Java_ThreadUtils_setThreadPriorityAudio(env, PlatformThread::CurrentId());
+    return true;
+  }
+  return false;
+}
+
+bool GetCurrentThreadPriorityForPlatform(ThreadPriority* priority) {
+  DCHECK(priority);
+  *priority = ThreadPriority::NORMAL;
+  JNIEnv* env = base::android::AttachCurrentThread();
+  if (Java_ThreadUtils_isThreadPriorityAudio(
+      env, PlatformThread::CurrentId())) {
+    *priority = ThreadPriority::REALTIME_AUDIO;
+    return true;
+  }
+  return false;
+}
+
+}  // namespace internal
+
+void PlatformThread::SetName(const std::string& name) {
+  ThreadIdNameManager::GetInstance()->SetName(name);
+
+  // Like linux, on android we can get the thread names to show up in the
+  // debugger by setting the process name for the LWP.
+  // We don't want to do this for the main thread because that would rename
+  // the process, causing tools like killall to stop working.
+  if (PlatformThread::CurrentId() == getpid())
+    return;
+
+  // Set the name for the LWP (which gets truncated to 15 characters).
+  int err = prctl(PR_SET_NAME, name.c_str());
+  if (err < 0 && errno != EPERM)
+    DPLOG(ERROR) << "prctl(PR_SET_NAME)";
+}
+
+
+void InitThreading() {
+}
+
+void TerminateOnThread() {
+  base::android::DetachFromVM();
+}
+
+size_t GetDefaultThreadStackSize(const pthread_attr_t& attributes) {
+#if !defined(ADDRESS_SANITIZER)
+  return 0;
+#else
+  // AddressSanitizer bloats the stack approximately 2x. Default stack size of
+  // 1Mb is not enough for some tests (see http://crbug.com/263749 for example).
+  return 2 * (1 << 20);  // 2Mb
+#endif
+}
+
+}  // namespace base
diff --git a/base/threading/post_task_and_reply_impl_unittest.cc b/base/threading/post_task_and_reply_impl_unittest.cc
new file mode 100644
index 0000000..319327d
--- /dev/null
+++ b/base/threading/post_task_and_reply_impl_unittest.cc
@@ -0,0 +1,198 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/threading/post_task_and_reply_impl.h"
+
+#include <utility>
+
+#include "base/auto_reset.h"
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/test/test_mock_time_task_runner.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::_;
+
+namespace base {
+namespace internal {
+
+namespace {
+
+class PostTaskAndReplyTaskRunner : public internal::PostTaskAndReplyImpl {
+ public:
+  explicit PostTaskAndReplyTaskRunner(TaskRunner* destination)
+      : destination_(destination) {}
+
+ private:
+  bool PostTask(const Location& from_here, OnceClosure task) override {
+    return destination_->PostTask(from_here, std::move(task));
+  }
+
+  // Non-owning.
+  TaskRunner* const destination_;
+};
+
+class ObjectToDelete : public RefCounted<ObjectToDelete> {
+ public:
+  // |delete_flag| is set to true when this object is deleted
+  ObjectToDelete(bool* delete_flag) : delete_flag_(delete_flag) {
+    EXPECT_FALSE(*delete_flag_);
+  }
+
+ private:
+  friend class RefCounted<ObjectToDelete>;
+  ~ObjectToDelete() { *delete_flag_ = true; }
+
+  bool* const delete_flag_;
+
+  DISALLOW_COPY_AND_ASSIGN(ObjectToDelete);
+};
+
+class MockObject {
+ public:
+  MockObject() = default;
+
+  MOCK_METHOD1(Task, void(scoped_refptr<ObjectToDelete>));
+  MOCK_METHOD1(Reply, void(scoped_refptr<ObjectToDelete>));
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockObject);
+};
+
+class MockRunsTasksInCurrentSequenceTaskRunner : public TestMockTimeTaskRunner {
+ public:
+  MockRunsTasksInCurrentSequenceTaskRunner(
+      TestMockTimeTaskRunner::Type type =
+          TestMockTimeTaskRunner::Type::kStandalone)
+      : TestMockTimeTaskRunner(type) {}
+
+  void RunUntilIdleWithRunsTasksInCurrentSequence() {
+    AutoReset<bool> reset(&runs_tasks_in_current_sequence_, true);
+    RunUntilIdle();
+  }
+
+  void ClearPendingTasksWithRunsTasksInCurrentSequence() {
+    AutoReset<bool> reset(&runs_tasks_in_current_sequence_, true);
+    ClearPendingTasks();
+  }
+
+  // TestMockTimeTaskRunner:
+  bool RunsTasksInCurrentSequence() const override {
+    return runs_tasks_in_current_sequence_;
+  }
+
+ private:
+  ~MockRunsTasksInCurrentSequenceTaskRunner() override = default;
+
+  bool runs_tasks_in_current_sequence_ = false;
+
+  DISALLOW_COPY_AND_ASSIGN(MockRunsTasksInCurrentSequenceTaskRunner);
+};
+
+class PostTaskAndReplyImplTest : public testing::Test {
+ protected:
+  PostTaskAndReplyImplTest() = default;
+
+  void PostTaskAndReplyToMockObject() {
+    // Expect the post to succeed.
+    EXPECT_TRUE(
+        PostTaskAndReplyTaskRunner(post_runner_.get())
+            .PostTaskAndReply(
+                FROM_HERE,
+                BindOnce(&MockObject::Task, Unretained(&mock_object_),
+                         MakeRefCounted<ObjectToDelete>(&delete_task_flag_)),
+                BindOnce(&MockObject::Reply, Unretained(&mock_object_),
+                         MakeRefCounted<ObjectToDelete>(&delete_reply_flag_))));
+
+    // Expect the first task to be posted to |post_runner_|.
+    EXPECT_TRUE(post_runner_->HasPendingTask());
+    EXPECT_FALSE(reply_runner_->HasPendingTask());
+    EXPECT_FALSE(delete_task_flag_);
+    EXPECT_FALSE(delete_reply_flag_);
+  }
+
+  scoped_refptr<MockRunsTasksInCurrentSequenceTaskRunner> post_runner_ =
+      MakeRefCounted<MockRunsTasksInCurrentSequenceTaskRunner>();
+  scoped_refptr<MockRunsTasksInCurrentSequenceTaskRunner> reply_runner_ =
+      MakeRefCounted<MockRunsTasksInCurrentSequenceTaskRunner>(
+          TestMockTimeTaskRunner::Type::kBoundToThread);
+  testing::StrictMock<MockObject> mock_object_;
+  bool delete_task_flag_ = false;
+  bool delete_reply_flag_ = false;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(PostTaskAndReplyImplTest);
+};
+
+}  // namespace
+
+TEST_F(PostTaskAndReplyImplTest, PostTaskAndReply) {
+  PostTaskAndReplyToMockObject();
+
+  EXPECT_CALL(mock_object_, Task(_));
+  post_runner_->RunUntilIdleWithRunsTasksInCurrentSequence();
+  testing::Mock::VerifyAndClear(&mock_object_);
+  // The task should have been deleted right after being run.
+  EXPECT_TRUE(delete_task_flag_);
+  EXPECT_FALSE(delete_reply_flag_);
+
+  // Expect the reply to be posted to |reply_runner_|.
+  EXPECT_FALSE(post_runner_->HasPendingTask());
+  EXPECT_TRUE(reply_runner_->HasPendingTask());
+
+  EXPECT_CALL(mock_object_, Reply(_));
+  reply_runner_->RunUntilIdleWithRunsTasksInCurrentSequence();
+  testing::Mock::VerifyAndClear(&mock_object_);
+  EXPECT_TRUE(delete_task_flag_);
+  // The reply should have been deleted right after being run.
+  EXPECT_TRUE(delete_reply_flag_);
+
+  // Expect no pending task in |post_runner_| and |reply_runner_|.
+  EXPECT_FALSE(post_runner_->HasPendingTask());
+  EXPECT_FALSE(reply_runner_->HasPendingTask());
+}
+
+TEST_F(PostTaskAndReplyImplTest, TaskDoesNotRun) {
+  PostTaskAndReplyToMockObject();
+
+  // Clear the |post_runner_|. Both callbacks should be scheduled for deletion
+  // on the |reply_runner_|.
+  post_runner_->ClearPendingTasksWithRunsTasksInCurrentSequence();
+  EXPECT_FALSE(post_runner_->HasPendingTask());
+  EXPECT_TRUE(reply_runner_->HasPendingTask());
+  EXPECT_FALSE(delete_task_flag_);
+  EXPECT_FALSE(delete_reply_flag_);
+
+  // Run the |reply_runner_|. Both callbacks should be deleted.
+  reply_runner_->RunUntilIdleWithRunsTasksInCurrentSequence();
+  EXPECT_TRUE(delete_task_flag_);
+  EXPECT_TRUE(delete_reply_flag_);
+}
+
+TEST_F(PostTaskAndReplyImplTest, ReplyDoesNotRun) {
+  PostTaskAndReplyToMockObject();
+
+  EXPECT_CALL(mock_object_, Task(_));
+  post_runner_->RunUntilIdleWithRunsTasksInCurrentSequence();
+  testing::Mock::VerifyAndClear(&mock_object_);
+  // The task should have been deleted right after being run.
+  EXPECT_TRUE(delete_task_flag_);
+  EXPECT_FALSE(delete_reply_flag_);
+
+  // Expect the reply to be posted to |reply_runner_|.
+  EXPECT_FALSE(post_runner_->HasPendingTask());
+  EXPECT_TRUE(reply_runner_->HasPendingTask());
+
+  // Clear the |reply_runner_| queue without running tasks. The reply callback
+  // should be deleted.
+  reply_runner_->ClearPendingTasksWithRunsTasksInCurrentSequence();
+  EXPECT_TRUE(delete_task_flag_);
+  EXPECT_TRUE(delete_reply_flag_);
+}
+
+}  // namespace internal
+}  // namespace base
diff --git a/base/threading/sequenced_task_runner_handle_unittest.cc b/base/threading/sequenced_task_runner_handle_unittest.cc
new file mode 100644
index 0000000..48394da
--- /dev/null
+++ b/base/threading/sequenced_task_runner_handle_unittest.cc
@@ -0,0 +1,90 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/threading/sequenced_task_runner_handle.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/location.h"
+#include "base/memory/ref_counted.h"
+#include "base/run_loop.h"
+#include "base/sequence_checker_impl.h"
+#include "base/sequenced_task_runner.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/task_scheduler/post_task.h"
+#include "base/test/scoped_task_environment.h"
+#include "base/test/test_simple_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace {
+
+class SequencedTaskRunnerHandleTest : public ::testing::Test {
+ protected:
+  // Verifies that the context it runs on has a SequencedTaskRunnerHandle
+  // and that posting to it results in the posted task running in that same
+  // context (sequence).
+  static void VerifyCurrentSequencedTaskRunner() {
+    ASSERT_TRUE(SequencedTaskRunnerHandle::IsSet());
+    scoped_refptr<SequencedTaskRunner> task_runner =
+        SequencedTaskRunnerHandle::Get();
+    ASSERT_TRUE(task_runner);
+
+    // Use SequenceCheckerImpl to make sure it's not a no-op in Release builds.
+    std::unique_ptr<SequenceCheckerImpl> sequence_checker(
+        new SequenceCheckerImpl);
+    task_runner->PostTask(
+        FROM_HERE,
+        base::BindOnce(&SequencedTaskRunnerHandleTest::CheckValidSequence,
+                       std::move(sequence_checker)));
+  }
+
+  static void CheckValidSequence(
+      std::unique_ptr<SequenceCheckerImpl> sequence_checker) {
+    EXPECT_TRUE(sequence_checker->CalledOnValidSequence());
+  }
+
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+};
+
+TEST_F(SequencedTaskRunnerHandleTest, FromMessageLoop) {
+  VerifyCurrentSequencedTaskRunner();
+  RunLoop().RunUntilIdle();
+}
+
+TEST_F(SequencedTaskRunnerHandleTest, FromTaskSchedulerSequencedTask) {
+  base::CreateSequencedTaskRunnerWithTraits({})->PostTask(
+      FROM_HERE,
+      base::BindOnce(
+          &SequencedTaskRunnerHandleTest::VerifyCurrentSequencedTaskRunner));
+  scoped_task_environment_.RunUntilIdle();
+}
+
+TEST_F(SequencedTaskRunnerHandleTest, NoHandleFromUnsequencedTask) {
+  base::PostTask(FROM_HERE, base::BindOnce([]() {
+                   EXPECT_FALSE(SequencedTaskRunnerHandle::IsSet());
+                 }));
+  scoped_task_environment_.RunUntilIdle();
+}
+
+TEST(SequencedTaskRunnerHandleTestWithoutMessageLoop, FromHandleInScope) {
+  scoped_refptr<SequencedTaskRunner> test_task_runner(new TestSimpleTaskRunner);
+  EXPECT_FALSE(SequencedTaskRunnerHandle::IsSet());
+  EXPECT_FALSE(ThreadTaskRunnerHandle::IsSet());
+  {
+    SequencedTaskRunnerHandle handle(test_task_runner);
+    EXPECT_TRUE(SequencedTaskRunnerHandle::IsSet());
+    EXPECT_FALSE(ThreadTaskRunnerHandle::IsSet());
+    EXPECT_EQ(test_task_runner, SequencedTaskRunnerHandle::Get());
+  }
+  EXPECT_FALSE(SequencedTaskRunnerHandle::IsSet());
+  EXPECT_FALSE(ThreadTaskRunnerHandle::IsSet());
+}
+
+}  // namespace
+}  // namespace base
diff --git a/base/threading/thread_perftest.cc b/base/threading/thread_perftest.cc
new file mode 100644
index 0000000..d3fad97
--- /dev/null
+++ b/base/threading/thread_perftest.cc
@@ -0,0 +1,321 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+
+#include <memory>
+#include <vector>
+
+#include "base/base_switches.h"
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/location.h"
+#include "base/memory/ptr_util.h"
+#include "base/message_loop/message_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "base/strings/stringprintf.h"
+#include "base/synchronization/condition_variable.h"
+#include "base/synchronization/lock.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread.h"
+#include "base/time/time.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/perf/perf_test.h"
+
+#if defined(OS_POSIX)
+#include <pthread.h>
+#endif
+
+namespace base {
+
+namespace {
+
+const int kNumRuns = 100000;
+
+// Base class for a threading perf-test. This sets up some threads for the
+// test and measures the clock-time in addition to time spent on each thread.
+class ThreadPerfTest : public testing::Test {
+ public:
+  ThreadPerfTest()
+      : done_(WaitableEvent::ResetPolicy::AUTOMATIC,
+              WaitableEvent::InitialState::NOT_SIGNALED) {}
+
+  // To be implemented by each test. Subclass must uses threads_ such that
+  // their cpu-time can be measured. Test must return from PingPong() _and_
+  // call FinishMeasurement from any thread to complete the test.
+  virtual void Init() {
+    if (ThreadTicks::IsSupported())
+      ThreadTicks::WaitUntilInitialized();
+  }
+  virtual void PingPong(int hops) = 0;
+  virtual void Reset() {}
+
+  void TimeOnThread(base::ThreadTicks* ticks, base::WaitableEvent* done) {
+    *ticks = base::ThreadTicks::Now();
+    done->Signal();
+  }
+
+  base::ThreadTicks ThreadNow(const base::Thread& thread) {
+    base::WaitableEvent done(WaitableEvent::ResetPolicy::AUTOMATIC,
+                             WaitableEvent::InitialState::NOT_SIGNALED);
+    base::ThreadTicks ticks;
+    thread.task_runner()->PostTask(
+        FROM_HERE, base::BindOnce(&ThreadPerfTest::TimeOnThread,
+                                  base::Unretained(this), &ticks, &done));
+    done.Wait();
+    return ticks;
+  }
+
+  void RunPingPongTest(const std::string& name, unsigned num_threads) {
+    // Create threads and collect starting cpu-time for each thread.
+    std::vector<base::ThreadTicks> thread_starts;
+    while (threads_.size() < num_threads) {
+      threads_.push_back(std::make_unique<base::Thread>("PingPonger"));
+      threads_.back()->Start();
+      if (base::ThreadTicks::IsSupported())
+        thread_starts.push_back(ThreadNow(*threads_.back()));
+    }
+
+    Init();
+
+    base::TimeTicks start = base::TimeTicks::Now();
+    PingPong(kNumRuns);
+    done_.Wait();
+    base::TimeTicks end = base::TimeTicks::Now();
+
+    // Gather the cpu-time spent on each thread. This does one extra tasks,
+    // but that should be in the noise given enough runs.
+    base::TimeDelta thread_time;
+    while (threads_.size()) {
+      if (base::ThreadTicks::IsSupported()) {
+        thread_time += ThreadNow(*threads_.back()) - thread_starts.back();
+        thread_starts.pop_back();
+      }
+      threads_.pop_back();
+    }
+
+    Reset();
+
+    double num_runs = static_cast<double>(kNumRuns);
+    double us_per_task_clock = (end - start).InMicroseconds() / num_runs;
+    double us_per_task_cpu = thread_time.InMicroseconds() / num_runs;
+
+    // Clock time per task.
+    perf_test::PrintResult(
+        "task", "", name + "_time ", us_per_task_clock, "us/hop", true);
+
+    // Total utilization across threads if available (likely higher).
+    if (base::ThreadTicks::IsSupported()) {
+      perf_test::PrintResult(
+          "task", "", name + "_cpu ", us_per_task_cpu, "us/hop", true);
+    }
+  }
+
+ protected:
+  void FinishMeasurement() { done_.Signal(); }
+  std::vector<std::unique_ptr<base::Thread>> threads_;
+
+ private:
+  base::WaitableEvent done_;
+};
+
+// Class to test task performance by posting empty tasks back and forth.
+class TaskPerfTest : public ThreadPerfTest {
+  base::Thread* NextThread(int count) {
+    return threads_[count % threads_.size()].get();
+  }
+
+  void PingPong(int hops) override {
+    if (!hops) {
+      FinishMeasurement();
+      return;
+    }
+    NextThread(hops)->task_runner()->PostTask(
+        FROM_HERE, base::BindOnce(&ThreadPerfTest::PingPong,
+                                  base::Unretained(this), hops - 1));
+  }
+};
+
+// This tries to test the 'best-case' as well as the 'worst-case' task posting
+// performance. The best-case keeps one thread alive such that it never yeilds,
+// while the worse-case forces a context switch for every task. Four threads are
+// used to ensure the threads do yeild (with just two it might be possible for
+// both threads to stay awake if they can signal each other fast enough).
+TEST_F(TaskPerfTest, TaskPingPong) {
+  RunPingPongTest("1_Task_Threads", 1);
+  RunPingPongTest("4_Task_Threads", 4);
+}
+
+
+// Same as above, but add observers to test their perf impact.
+class MessageLoopObserver : public base::MessageLoop::TaskObserver {
+ public:
+  void WillProcessTask(const base::PendingTask& pending_task) override {}
+  void DidProcessTask(const base::PendingTask& pending_task) override {}
+};
+MessageLoopObserver message_loop_observer;
+
+class TaskObserverPerfTest : public TaskPerfTest {
+ public:
+  void Init() override {
+    TaskPerfTest::Init();
+    for (size_t i = 0; i < threads_.size(); i++) {
+      threads_[i]->message_loop()->task_runner()->PostTask(
+          FROM_HERE, BindOnce(&MessageLoop::AddTaskObserver,
+                              Unretained(threads_[i]->message_loop()),
+                              Unretained(&message_loop_observer)));
+    }
+  }
+};
+
+TEST_F(TaskObserverPerfTest, TaskPingPong) {
+  RunPingPongTest("1_Task_Threads_With_Observer", 1);
+  RunPingPongTest("4_Task_Threads_With_Observer", 4);
+}
+
+// Class to test our WaitableEvent performance by signaling back and fort.
+// WaitableEvent is templated so we can also compare with other versions.
+template <typename WaitableEventType>
+class EventPerfTest : public ThreadPerfTest {
+ public:
+  void Init() override {
+    for (size_t i = 0; i < threads_.size(); i++) {
+      events_.push_back(std::make_unique<WaitableEventType>(
+          WaitableEvent::ResetPolicy::AUTOMATIC,
+          WaitableEvent::InitialState::NOT_SIGNALED));
+    }
+  }
+
+  void Reset() override { events_.clear(); }
+
+  void WaitAndSignalOnThread(size_t event) {
+    size_t next_event = (event + 1) % events_.size();
+    int my_hops = 0;
+    do {
+      events_[event]->Wait();
+      my_hops = --remaining_hops_;  // We own 'hops' between Wait and Signal.
+      events_[next_event]->Signal();
+    } while (my_hops > 0);
+    // Once we are done, all threads will signal as hops passes zero.
+    // We only signal completion once, on the thread that reaches zero.
+    if (!my_hops)
+      FinishMeasurement();
+  }
+
+  void PingPong(int hops) override {
+    remaining_hops_ = hops;
+    for (size_t i = 0; i < threads_.size(); i++) {
+      threads_[i]->task_runner()->PostTask(
+          FROM_HERE, base::BindOnce(&EventPerfTest::WaitAndSignalOnThread,
+                                    base::Unretained(this), i));
+    }
+
+    // Kick off the Signal ping-ponging.
+    events_.front()->Signal();
+  }
+
+  int remaining_hops_;
+  std::vector<std::unique_ptr<WaitableEventType>> events_;
+};
+
+// Similar to the task posting test, this just tests similar functionality
+// using WaitableEvents. We only test four threads (worst-case), but we
+// might want to craft a way to test the best-case (where the thread doesn't
+// end up blocking because the event is already signalled).
+typedef EventPerfTest<base::WaitableEvent> WaitableEventThreadPerfTest;
+TEST_F(WaitableEventThreadPerfTest, EventPingPong) {
+  RunPingPongTest("4_WaitableEvent_Threads", 4);
+}
+
+// Build a minimal event using ConditionVariable.
+class ConditionVariableEvent {
+ public:
+  ConditionVariableEvent(WaitableEvent::ResetPolicy reset_policy,
+                         WaitableEvent::InitialState initial_state)
+      : cond_(&lock_), signaled_(false) {
+    DCHECK_EQ(WaitableEvent::ResetPolicy::AUTOMATIC, reset_policy);
+    DCHECK_EQ(WaitableEvent::InitialState::NOT_SIGNALED, initial_state);
+  }
+
+  void Signal() {
+    {
+      base::AutoLock scoped_lock(lock_);
+      signaled_ = true;
+    }
+    cond_.Signal();
+  }
+
+  void Wait() {
+    base::AutoLock scoped_lock(lock_);
+    while (!signaled_)
+      cond_.Wait();
+    signaled_ = false;
+  }
+
+ private:
+  base::Lock lock_;
+  base::ConditionVariable cond_;
+  bool signaled_;
+};
+
+// This is meant to test the absolute minimal context switching time
+// using our own base synchronization code.
+typedef EventPerfTest<ConditionVariableEvent> ConditionVariablePerfTest;
+TEST_F(ConditionVariablePerfTest, EventPingPong) {
+  RunPingPongTest("4_ConditionVariable_Threads", 4);
+}
+#if defined(OS_POSIX)
+
+// Absolutely 100% minimal posix waitable event. If there is a better/faster
+// way to force a context switch, we should use that instead.
+class PthreadEvent {
+ public:
+  PthreadEvent(WaitableEvent::ResetPolicy reset_policy,
+               WaitableEvent::InitialState initial_state) {
+    DCHECK_EQ(WaitableEvent::ResetPolicy::AUTOMATIC, reset_policy);
+    DCHECK_EQ(WaitableEvent::InitialState::NOT_SIGNALED, initial_state);
+    pthread_mutex_init(&mutex_, nullptr);
+    pthread_cond_init(&cond_, nullptr);
+    signaled_ = false;
+  }
+
+  ~PthreadEvent() {
+    pthread_cond_destroy(&cond_);
+    pthread_mutex_destroy(&mutex_);
+  }
+
+  void Signal() {
+    pthread_mutex_lock(&mutex_);
+    signaled_ = true;
+    pthread_mutex_unlock(&mutex_);
+    pthread_cond_signal(&cond_);
+  }
+
+  void Wait() {
+    pthread_mutex_lock(&mutex_);
+    while (!signaled_)
+      pthread_cond_wait(&cond_, &mutex_);
+    signaled_ = false;
+    pthread_mutex_unlock(&mutex_);
+  }
+
+ private:
+  bool signaled_;
+  pthread_mutex_t mutex_;
+  pthread_cond_t cond_;
+};
+
+// This is meant to test the absolute minimal context switching time.
+// If there is any faster way to do this we should substitute it in.
+typedef EventPerfTest<PthreadEvent> PthreadEventPerfTest;
+TEST_F(PthreadEventPerfTest, EventPingPong) {
+  RunPingPongTest("4_PthreadCondVar_Threads", 4);
+}
+
+#endif
+
+}  // namespace
+
+}  // namespace base
diff --git a/base/threading/thread_task_runner_handle_unittest.cc b/base/threading/thread_task_runner_handle_unittest.cc
new file mode 100644
index 0000000..1aa02d1
--- /dev/null
+++ b/base/threading/thread_task_runner_handle_unittest.cc
@@ -0,0 +1,122 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/threading/thread_task_runner_handle.h"
+
+#include "base/memory/ref_counted.h"
+#include "base/test/gtest_util.h"
+#include "base/test/test_simple_task_runner.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+TEST(ThreadTaskRunnerHandleTest, Basic) {
+  scoped_refptr<SingleThreadTaskRunner> task_runner(new TestSimpleTaskRunner);
+
+  EXPECT_FALSE(ThreadTaskRunnerHandle::IsSet());
+  {
+    ThreadTaskRunnerHandle ttrh1(task_runner);
+    EXPECT_TRUE(ThreadTaskRunnerHandle::IsSet());
+    EXPECT_EQ(task_runner, ThreadTaskRunnerHandle::Get());
+  }
+  EXPECT_FALSE(ThreadTaskRunnerHandle::IsSet());
+}
+
+TEST(ThreadTaskRunnerHandleTest, DeathOnImplicitOverride) {
+  scoped_refptr<SingleThreadTaskRunner> task_runner(new TestSimpleTaskRunner);
+  scoped_refptr<SingleThreadTaskRunner> overidding_task_runner(
+      new TestSimpleTaskRunner);
+
+  ThreadTaskRunnerHandle ttrh(task_runner);
+  EXPECT_DCHECK_DEATH(
+      { ThreadTaskRunnerHandle overriding_ttrh(overidding_task_runner); });
+}
+
+TEST(ThreadTaskRunnerHandleTest, OverrideForTestingExistingTTRH) {
+  scoped_refptr<SingleThreadTaskRunner> task_runner_1(new TestSimpleTaskRunner);
+  scoped_refptr<SingleThreadTaskRunner> task_runner_2(new TestSimpleTaskRunner);
+  scoped_refptr<SingleThreadTaskRunner> task_runner_3(new TestSimpleTaskRunner);
+  scoped_refptr<SingleThreadTaskRunner> task_runner_4(new TestSimpleTaskRunner);
+
+  EXPECT_FALSE(ThreadTaskRunnerHandle::IsSet());
+  {
+    // TTRH in place prior to override.
+    ThreadTaskRunnerHandle ttrh1(task_runner_1);
+    EXPECT_TRUE(ThreadTaskRunnerHandle::IsSet());
+    EXPECT_EQ(task_runner_1, ThreadTaskRunnerHandle::Get());
+
+    {
+      // Override.
+      ScopedClosureRunner undo_override_2 =
+          ThreadTaskRunnerHandle::OverrideForTesting(task_runner_2);
+      EXPECT_TRUE(ThreadTaskRunnerHandle::IsSet());
+      EXPECT_EQ(task_runner_2, ThreadTaskRunnerHandle::Get());
+
+      {
+        // Nested override.
+        ScopedClosureRunner undo_override_3 =
+            ThreadTaskRunnerHandle::OverrideForTesting(task_runner_3);
+        EXPECT_TRUE(ThreadTaskRunnerHandle::IsSet());
+        EXPECT_EQ(task_runner_3, ThreadTaskRunnerHandle::Get());
+      }
+
+      // Back to single override.
+      EXPECT_TRUE(ThreadTaskRunnerHandle::IsSet());
+      EXPECT_EQ(task_runner_2, ThreadTaskRunnerHandle::Get());
+
+      {
+        // Backup to double override with another TTRH.
+        ScopedClosureRunner undo_override_4 =
+            ThreadTaskRunnerHandle::OverrideForTesting(task_runner_4);
+        EXPECT_TRUE(ThreadTaskRunnerHandle::IsSet());
+        EXPECT_EQ(task_runner_4, ThreadTaskRunnerHandle::Get());
+      }
+    }
+
+    // Back to simple TTRH.
+    EXPECT_TRUE(ThreadTaskRunnerHandle::IsSet());
+    EXPECT_EQ(task_runner_1, ThreadTaskRunnerHandle::Get());
+  }
+  EXPECT_FALSE(ThreadTaskRunnerHandle::IsSet());
+}
+
+TEST(ThreadTaskRunnerHandleTest, OverrideForTestingNoExistingTTRH) {
+  scoped_refptr<SingleThreadTaskRunner> task_runner_1(new TestSimpleTaskRunner);
+  scoped_refptr<SingleThreadTaskRunner> task_runner_2(new TestSimpleTaskRunner);
+
+  EXPECT_FALSE(ThreadTaskRunnerHandle::IsSet());
+  {
+    // Override with no TTRH in place.
+    ScopedClosureRunner undo_override_1 =
+        ThreadTaskRunnerHandle::OverrideForTesting(task_runner_1);
+    EXPECT_TRUE(ThreadTaskRunnerHandle::IsSet());
+    EXPECT_EQ(task_runner_1, ThreadTaskRunnerHandle::Get());
+
+    {
+      // Nested override works the same.
+      ScopedClosureRunner undo_override_2 =
+          ThreadTaskRunnerHandle::OverrideForTesting(task_runner_2);
+      EXPECT_TRUE(ThreadTaskRunnerHandle::IsSet());
+      EXPECT_EQ(task_runner_2, ThreadTaskRunnerHandle::Get());
+    }
+
+    // Back to single override.
+    EXPECT_TRUE(ThreadTaskRunnerHandle::IsSet());
+    EXPECT_EQ(task_runner_1, ThreadTaskRunnerHandle::Get());
+  }
+  EXPECT_FALSE(ThreadTaskRunnerHandle::IsSet());
+}
+
+TEST(ThreadTaskRunnerHandleTest, DeathOnTTRHOverOverride) {
+  scoped_refptr<SingleThreadTaskRunner> task_runner(new TestSimpleTaskRunner);
+  scoped_refptr<SingleThreadTaskRunner> overidding_task_runner(
+      new TestSimpleTaskRunner);
+
+  ScopedClosureRunner undo_override =
+      ThreadTaskRunnerHandle::OverrideForTesting(task_runner);
+  EXPECT_DCHECK_DEATH(
+      { ThreadTaskRunnerHandle overriding_ttrh(overidding_task_runner); });
+}
+
+}  // namespace base
diff --git a/base/threading/watchdog.cc b/base/threading/watchdog.cc
new file mode 100644
index 0000000..0e48c8e
--- /dev/null
+++ b/base/threading/watchdog.cc
@@ -0,0 +1,183 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/threading/watchdog.h"
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/no_destructor.h"
+#include "base/threading/platform_thread.h"
+
+namespace base {
+
+namespace {
+
+// 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.
+
+struct StaticData {
+  // Lock for access of static data...
+  Lock lock;
+
+  // When did we last alarm and get stuck (for a while) in a debugger?
+  TimeTicks last_debugged_alarm_time;
+
+  // How long did we sit on a break in the debugger?
+  TimeDelta last_debugged_alarm_delay;
+};
+
+StaticData* GetStaticData() {
+  static base::NoDestructor<StaticData> static_data;
+  return static_data.get();
+}
+
+}  // namespace
+
+// Start thread running in a Disarmed state.
+Watchdog::Watchdog(const TimeDelta& duration,
+                   const std::string& thread_watched_name,
+                   bool enabled)
+  : enabled_(enabled),
+    lock_(),
+    condition_variable_(&lock_),
+    state_(DISARMED),
+    duration_(duration),
+    thread_watched_name_(thread_watched_name),
+    delegate_(this) {
+  if (!enabled_)
+    return;  // Don't start thread, or doing anything really.
+  enabled_ = PlatformThread::Create(0,  // Default stack size.
+                                    &delegate_,
+                                    &handle_);
+  DCHECK(enabled_);
+}
+
+// Notify watchdog thread, and wait for it to finish up.
+Watchdog::~Watchdog() {
+  if (!enabled_)
+    return;
+  if (!IsJoinable())
+    Cleanup();
+  PlatformThread::Join(handle_);
+}
+
+void Watchdog::Cleanup() {
+  if (!enabled_)
+    return;
+  AutoLock lock(lock_);
+  state_ = SHUTDOWN;
+  condition_variable_.Signal();
+}
+
+bool Watchdog::IsJoinable() {
+  if (!enabled_)
+    return true;
+  AutoLock lock(lock_);
+  return (state_ == JOINABLE);
+}
+
+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() {
+  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.
+}
+
+void Watchdog::Alarm() {
+  DVLOG(1) << "Watchdog alarmed for " << thread_watched_name_;
+}
+
+//------------------------------------------------------------------------------
+// Internal private methods that the watchdog thread uses.
+
+void Watchdog::ThreadDelegate::ThreadMain() {
+  SetThreadName();
+  TimeDelta remaining_duration;
+  StaticData* static_data = GetStaticData();
+  while (1) {
+    AutoLock lock(watchdog_->lock_);
+    while (DISARMED == watchdog_->state_)
+      watchdog_->condition_variable_.Wait();
+    if (SHUTDOWN == watchdog_->state_) {
+      watchdog_->state_ = JOINABLE;
+      return;
+    }
+    DCHECK(ARMED == watchdog_->state_);
+    remaining_duration = watchdog_->duration_ -
+        (TimeTicks::Now() - watchdog_->start_time_);
+    if (remaining_duration.InMilliseconds() > 0) {
+      // Spurios wake?  Timer drifts?  Go back to sleep for remaining time.
+      watchdog_->condition_variable_.TimedWait(remaining_duration);
+      continue;
+    }
+    // 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_data->lock);
+      if (static_data->last_debugged_alarm_time > watchdog_->start_time_) {
+        // False alarm: we started our clock before the debugger break (last
+        // alarm time).
+        watchdog_->start_time_ += static_data->last_debugged_alarm_delay;
+        if (static_data->last_debugged_alarm_time > watchdog_->start_time_)
+          // Too many alarms must have taken place.
+          watchdog_->state_ = DISARMED;
+        continue;
+      }
+    }
+    watchdog_->state_ = DISARMED;  // Only alarm at most once.
+    TimeTicks last_alarm_time = TimeTicks::Now();
+    {
+      AutoUnlock unlock(watchdog_->lock_);
+      watchdog_->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))
+      continue;
+    // Ignore race of two alarms/breaks going off at roughly the same time.
+    AutoLock static_lock(static_data->lock);
+    // This was a real debugger break.
+    static_data->last_debugged_alarm_time = last_alarm_time;
+    static_data->last_debugged_alarm_delay = last_alarm_delay;
+  }
+}
+
+void Watchdog::ThreadDelegate::SetThreadName() const {
+  std::string name = watchdog_->thread_watched_name_ + " Watchdog";
+  PlatformThread::SetName(name);
+  DVLOG(1) << "Watchdog active: " << name;
+}
+
+// static
+void Watchdog::ResetStaticData() {
+  StaticData* static_data = GetStaticData();
+  AutoLock lock(static_data->lock);
+  // See https://crbug.com/734232 for why this cannot be zero-initialized.
+  static_data->last_debugged_alarm_time = TimeTicks::Min();
+  static_data->last_debugged_alarm_delay = TimeDelta();
+}
+
+}  // namespace base
diff --git a/base/threading/watchdog.h b/base/threading/watchdog.h
new file mode 100644
index 0000000..f806984
--- /dev/null
+++ b/base/threading/watchdog.h
@@ -0,0 +1,96 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// 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_THREADING_WATCHDOG_H_
+#define BASE_THREADING_WATCHDOG_H_
+
+#include <string>
+
+#include "base/base_export.h"
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/synchronization/condition_variable.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/platform_thread.h"
+#include "base/time/time.h"
+
+namespace base {
+
+class BASE_EXPORT Watchdog {
+ public:
+  // Constructor specifies how long the Watchdog will wait before alarming.
+  Watchdog(const TimeDelta& duration,
+           const std::string& thread_watched_name,
+           bool enabled);
+  virtual ~Watchdog();
+
+  // Notify watchdog thread to finish up. Sets the state_ to SHUTDOWN.
+  void Cleanup();
+
+  // Returns true if we state_ is JOINABLE (which indicates that Watchdog has
+  // exited).
+  bool IsJoinable();
+
+  // 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();
+
+  // Reset static data to initial state. Useful for tests, to ensure
+  // they are independent.
+  static void ResetStaticData();
+
+ private:
+  class ThreadDelegate : public PlatformThread::Delegate {
+   public:
+    explicit ThreadDelegate(Watchdog* watchdog) : watchdog_(watchdog) {
+    }
+    void ThreadMain() override;
+
+   private:
+    void SetThreadName() const;
+
+    Watchdog* watchdog_;
+  };
+
+  enum State {ARMED, DISARMED, SHUTDOWN, JOINABLE };
+
+  bool enabled_;
+
+  Lock lock_;  // Mutex for state_.
+  ConditionVariable condition_variable_;
+  State state_;
+  const TimeDelta duration_;  // How long after start_time_ do we alarm?
+  const std::string thread_watched_name_;
+  PlatformThreadHandle handle_;
+  ThreadDelegate delegate_;  // Store it, because it must outlive the thread.
+
+  TimeTicks start_time_;  // Start of epoch, and alarm after duration_.
+
+  DISALLOW_COPY_AND_ASSIGN(Watchdog);
+};
+
+}  // namespace base
+
+#endif  // BASE_THREADING_WATCHDOG_H_
diff --git a/base/threading/watchdog_unittest.cc b/base/threading/watchdog_unittest.cc
new file mode 100644
index 0000000..f534a86
--- /dev/null
+++ b/base/threading/watchdog_unittest.cc
@@ -0,0 +1,141 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/threading/watchdog.h"
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/synchronization/spin_wait.h"
+#include "base/threading/platform_thread.h"
+#include "base/time/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+namespace {
+
+//------------------------------------------------------------------------------
+// Provide a derived class to facilitate testing.
+
+class WatchdogCounter : public Watchdog {
+ public:
+  WatchdogCounter(const TimeDelta& duration,
+                  const std::string& thread_watched_name,
+                  bool enabled)
+      : Watchdog(duration, thread_watched_name, enabled),
+        alarm_counter_(0) {
+  }
+
+  ~WatchdogCounter() override = default;
+
+  void Alarm() override {
+    alarm_counter_++;
+    Watchdog::Alarm();
+  }
+
+  int alarm_counter() { return alarm_counter_; }
+
+ private:
+  int alarm_counter_;
+
+  DISALLOW_COPY_AND_ASSIGN(WatchdogCounter);
+};
+
+class WatchdogTest : public testing::Test {
+ public:
+  void SetUp() override { Watchdog::ResetStaticData(); }
+};
+
+}  // namespace
+
+//------------------------------------------------------------------------------
+// Actual tests
+
+// Minimal constructor/destructor test.
+TEST_F(WatchdogTest, StartupShutdownTest) {
+  Watchdog watchdog1(TimeDelta::FromMilliseconds(300), "Disabled", false);
+  Watchdog watchdog2(TimeDelta::FromMilliseconds(300), "Enabled", true);
+}
+
+// Test ability to call Arm and Disarm repeatedly.
+TEST_F(WatchdogTest, ArmDisarmTest) {
+  Watchdog watchdog1(TimeDelta::FromMilliseconds(300), "Disabled", false);
+  watchdog1.Arm();
+  watchdog1.Disarm();
+  watchdog1.Arm();
+  watchdog1.Disarm();
+
+  Watchdog watchdog2(TimeDelta::FromMilliseconds(300), "Enabled", true);
+  watchdog2.Arm();
+  watchdog2.Disarm();
+  watchdog2.Arm();
+  watchdog2.Disarm();
+}
+
+// Make sure a basic alarm fires when the time has expired.
+TEST_F(WatchdogTest, AlarmTest) {
+  WatchdogCounter watchdog(TimeDelta::FromMilliseconds(10), "Enabled", true);
+  watchdog.Arm();
+  SPIN_FOR_TIMEDELTA_OR_UNTIL_TRUE(TimeDelta::FromMinutes(5),
+                                   watchdog.alarm_counter() > 0);
+  EXPECT_EQ(1, watchdog.alarm_counter());
+}
+
+// Make sure a basic alarm fires when the time has expired.
+TEST_F(WatchdogTest, AlarmPriorTimeTest) {
+  WatchdogCounter watchdog(TimeDelta(), "Enabled2", true);
+  // Set a time in the past.
+  watchdog.ArmSomeTimeDeltaAgo(TimeDelta::FromSeconds(2));
+  // It should instantly go off, but certainly in less than 5 minutes.
+  SPIN_FOR_TIMEDELTA_OR_UNTIL_TRUE(TimeDelta::FromMinutes(5),
+                                   watchdog.alarm_counter() > 0);
+
+  EXPECT_EQ(1, watchdog.alarm_counter());
+}
+
+// Make sure a disable alarm does nothing, even if we arm it.
+TEST_F(WatchdogTest, ConstructorDisabledTest) {
+  WatchdogCounter watchdog(TimeDelta::FromMilliseconds(10), "Disabled", false);
+  watchdog.Arm();
+  // Alarm should not fire, as it was disabled.
+  PlatformThread::Sleep(TimeDelta::FromMilliseconds(500));
+  EXPECT_EQ(0, watchdog.alarm_counter());
+}
+
+// Make sure Disarming will prevent firing, even after Arming.
+TEST_F(WatchdogTest, DisarmTest) {
+  WatchdogCounter watchdog(TimeDelta::FromSeconds(1), "Enabled3", true);
+
+  TimeTicks start = TimeTicks::Now();
+  watchdog.Arm();
+  // Sleep a bit, but not past the alarm point.
+  PlatformThread::Sleep(TimeDelta::FromMilliseconds(100));
+  watchdog.Disarm();
+  TimeTicks end = TimeTicks::Now();
+
+  if (end - start > TimeDelta::FromMilliseconds(500)) {
+    LOG(WARNING) << "100ms sleep took over 500ms, making the results of this "
+                 << "timing-sensitive test suspicious.  Aborting now.";
+    return;
+  }
+
+  // Alarm should not have fired before it was disarmed.
+  EXPECT_EQ(0, watchdog.alarm_counter());
+
+  // Sleep past the point where it would have fired if it wasn't disarmed,
+  // and verify that it didn't fire.
+  PlatformThread::Sleep(TimeDelta::FromSeconds(1));
+  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(10));
+  // It should almost instantly go off, but certainly in less than 5 minutes.
+  SPIN_FOR_TIMEDELTA_OR_UNTIL_TRUE(TimeDelta::FromMinutes(5),
+                                   watchdog.alarm_counter() > 0);
+
+  EXPECT_EQ(1, watchdog.alarm_counter());
+}
+
+}  // namespace base
diff --git a/base/tools_sanity_unittest.cc b/base/tools_sanity_unittest.cc
new file mode 100644
index 0000000..98c30df
--- /dev/null
+++ b/base/tools_sanity_unittest.cc
@@ -0,0 +1,423 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// This file contains intentional memory errors, some of which may lead to
+// crashes if the test is ran without special memory testing tools. We use these
+// errors to verify the sanity of the tools.
+
+#include <stddef.h>
+
+#include "base/atomicops.h"
+#include "base/cfi_buildflags.h"
+#include "base/debug/asan_invalid_access.h"
+#include "base/debug/profiler.h"
+#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
+#include "base/threading/thread.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+namespace {
+
+const base::subtle::Atomic32 kMagicValue = 42;
+
+// Helper for memory accesses that can potentially corrupt memory or cause a
+// crash during a native run.
+#if defined(ADDRESS_SANITIZER)
+#if defined(OS_IOS)
+// EXPECT_DEATH is not supported on IOS.
+#define HARMFUL_ACCESS(action,error_regexp) do { action; } while (0)
+#else
+#define HARMFUL_ACCESS(action,error_regexp) EXPECT_DEATH(action,error_regexp)
+#endif  // !OS_IOS
+#else
+#define HARMFUL_ACCESS(action, error_regexp)
+#define HARMFUL_ACCESS_IS_NOOP
+#endif
+
+void DoReadUninitializedValue(char *ptr) {
+  // Comparison with 64 is to prevent clang from optimizing away the
+  // jump -- valgrind only catches jumps and conditional moves, but clang uses
+  // the borrow flag if the condition is just `*ptr == '\0'`.  We no longer
+  // support valgrind, but this constant should be fine to keep as-is.
+  if (*ptr == 64) {
+    VLOG(1) << "Uninit condition is true";
+  } else {
+    VLOG(1) << "Uninit condition is false";
+  }
+}
+
+void ReadUninitializedValue(char *ptr) {
+#if defined(MEMORY_SANITIZER)
+  EXPECT_DEATH(DoReadUninitializedValue(ptr),
+               "use-of-uninitialized-value");
+#else
+  DoReadUninitializedValue(ptr);
+#endif
+}
+
+#ifndef HARMFUL_ACCESS_IS_NOOP
+void ReadValueOutOfArrayBoundsLeft(char *ptr) {
+  char c = ptr[-2];
+  VLOG(1) << "Reading a byte out of bounds: " << c;
+}
+
+void ReadValueOutOfArrayBoundsRight(char *ptr, size_t size) {
+  char c = ptr[size + 1];
+  VLOG(1) << "Reading a byte out of bounds: " << c;
+}
+
+void WriteValueOutOfArrayBoundsLeft(char *ptr) {
+  ptr[-1] = kMagicValue;
+}
+
+void WriteValueOutOfArrayBoundsRight(char *ptr, size_t size) {
+  ptr[size] = kMagicValue;
+}
+#endif  // HARMFUL_ACCESS_IS_NOOP
+
+void MakeSomeErrors(char *ptr, size_t size) {
+  ReadUninitializedValue(ptr);
+
+  HARMFUL_ACCESS(ReadValueOutOfArrayBoundsLeft(ptr),
+                 "2 bytes to the left");
+  HARMFUL_ACCESS(ReadValueOutOfArrayBoundsRight(ptr, size),
+                 "1 bytes to the right");
+  HARMFUL_ACCESS(WriteValueOutOfArrayBoundsLeft(ptr),
+                 "1 bytes to the left");
+  HARMFUL_ACCESS(WriteValueOutOfArrayBoundsRight(ptr, size),
+                 "0 bytes to the right");
+}
+
+}  // namespace
+
+// A memory leak detector should report an error in this test.
+TEST(ToolsSanityTest, MemoryLeak) {
+  // Without the |volatile|, clang optimizes away the next two lines.
+  int* volatile leak = new int[256];  // Leak some memory intentionally.
+  leak[4] = 1;  // Make sure the allocated memory is used.
+}
+
+#if (defined(ADDRESS_SANITIZER) && defined(OS_IOS))
+// Because iOS doesn't support death tests, each of the following tests will
+// crash the whole program under Asan.
+#define MAYBE_AccessesToNewMemory DISABLED_AccessesToNewMemory
+#define MAYBE_AccessesToMallocMemory DISABLED_AccessesToMallocMemory
+#else
+#define MAYBE_AccessesToNewMemory AccessesToNewMemory
+#define MAYBE_AccessesToMallocMemory AccessesToMallocMemory
+#endif  // (defined(ADDRESS_SANITIZER) && defined(OS_IOS))
+
+// The following tests pass with Clang r170392, but not r172454, which
+// makes AddressSanitizer detect errors in them. We disable these tests under
+// AddressSanitizer until we fully switch to Clang r172454. After that the
+// tests should be put back under the (defined(OS_IOS) || defined(OS_WIN))
+// clause above.
+// See also http://crbug.com/172614.
+#if defined(ADDRESS_SANITIZER)
+#define MAYBE_SingleElementDeletedWithBraces \
+    DISABLED_SingleElementDeletedWithBraces
+#define MAYBE_ArrayDeletedWithoutBraces DISABLED_ArrayDeletedWithoutBraces
+#else
+#define MAYBE_ArrayDeletedWithoutBraces ArrayDeletedWithoutBraces
+#define MAYBE_SingleElementDeletedWithBraces SingleElementDeletedWithBraces
+#endif  // defined(ADDRESS_SANITIZER)
+
+TEST(ToolsSanityTest, MAYBE_AccessesToNewMemory) {
+  char *foo = new char[10];
+  MakeSomeErrors(foo, 10);
+  delete [] foo;
+  // Use after delete.
+  HARMFUL_ACCESS(foo[5] = 0, "heap-use-after-free");
+}
+
+TEST(ToolsSanityTest, MAYBE_AccessesToMallocMemory) {
+  char *foo = reinterpret_cast<char*>(malloc(10));
+  MakeSomeErrors(foo, 10);
+  free(foo);
+  // Use after free.
+  HARMFUL_ACCESS(foo[5] = 0, "heap-use-after-free");
+}
+
+#if defined(ADDRESS_SANITIZER)
+
+static int* allocateArray() {
+  // Clang warns about the mismatched new[]/delete if they occur in the same
+  // function.
+  return new int[10];
+}
+
+// This test may corrupt memory if not compiled with AddressSanitizer.
+TEST(ToolsSanityTest, MAYBE_ArrayDeletedWithoutBraces) {
+  // Without the |volatile|, clang optimizes away the next two lines.
+  int* volatile foo = allocateArray();
+  delete foo;
+}
+#endif
+
+#if defined(ADDRESS_SANITIZER)
+static int* allocateScalar() {
+  // Clang warns about the mismatched new/delete[] if they occur in the same
+  // function.
+  return new int;
+}
+
+// This test may corrupt memory if not compiled with AddressSanitizer.
+TEST(ToolsSanityTest, MAYBE_SingleElementDeletedWithBraces) {
+  // Without the |volatile|, clang optimizes away the next two lines.
+  int* volatile foo = allocateScalar();
+  (void) foo;
+  delete [] foo;
+}
+#endif
+
+#if defined(ADDRESS_SANITIZER)
+
+TEST(ToolsSanityTest, DISABLED_AddressSanitizerNullDerefCrashTest) {
+  // Intentionally crash to make sure AddressSanitizer is running.
+  // This test should not be ran on bots.
+  int* volatile zero = NULL;
+  *zero = 0;
+}
+
+TEST(ToolsSanityTest, DISABLED_AddressSanitizerLocalOOBCrashTest) {
+  // Intentionally crash to make sure AddressSanitizer is instrumenting
+  // the local variables.
+  // This test should not be ran on bots.
+  int array[5];
+  // Work around the OOB warning reported by Clang.
+  int* volatile access = &array[5];
+  *access = 43;
+}
+
+namespace {
+int g_asan_test_global_array[10];
+}  // namespace
+
+TEST(ToolsSanityTest, DISABLED_AddressSanitizerGlobalOOBCrashTest) {
+  // Intentionally crash to make sure AddressSanitizer is instrumenting
+  // the global variables.
+  // This test should not be ran on bots.
+
+  // Work around the OOB warning reported by Clang.
+  int* volatile access = g_asan_test_global_array - 1;
+  *access = 43;
+}
+
+#ifndef HARMFUL_ACCESS_IS_NOOP
+TEST(ToolsSanityTest, AsanHeapOverflow) {
+  HARMFUL_ACCESS(debug::AsanHeapOverflow() ,"to the right");
+}
+
+TEST(ToolsSanityTest, AsanHeapUnderflow) {
+  HARMFUL_ACCESS(debug::AsanHeapUnderflow(), "to the left");
+}
+
+TEST(ToolsSanityTest, AsanHeapUseAfterFree) {
+  HARMFUL_ACCESS(debug::AsanHeapUseAfterFree(), "heap-use-after-free");
+}
+
+#if defined(OS_WIN)
+// The ASAN runtime doesn't detect heap corruption, this needs fixing before
+// ASAN builds can ship to the wild. See https://crbug.com/818747.
+TEST(ToolsSanityTest, DISABLED_AsanCorruptHeapBlock) {
+  HARMFUL_ACCESS(debug::AsanCorruptHeapBlock(), "");
+}
+
+TEST(ToolsSanityTest, DISABLED_AsanCorruptHeap) {
+  // This test will kill the process by raising an exception, there's no
+  // particular string to look for in the stack trace.
+  EXPECT_DEATH(debug::AsanCorruptHeap(), "");
+}
+#endif  // OS_WIN
+#endif  // !HARMFUL_ACCESS_IS_NOOP
+
+#endif  // ADDRESS_SANITIZER
+
+namespace {
+
+// We use caps here just to ensure that the method name doesn't interfere with
+// the wildcarded suppressions.
+class TOOLS_SANITY_TEST_CONCURRENT_THREAD : public PlatformThread::Delegate {
+ public:
+  explicit TOOLS_SANITY_TEST_CONCURRENT_THREAD(bool *value) : value_(value) {}
+  ~TOOLS_SANITY_TEST_CONCURRENT_THREAD() override = default;
+  void ThreadMain() override {
+    *value_ = true;
+
+    // Sleep for a few milliseconds so the two threads are more likely to live
+    // simultaneously. Otherwise we may miss the report due to mutex
+    // lock/unlock's inside thread creation code in pure-happens-before mode...
+    PlatformThread::Sleep(TimeDelta::FromMilliseconds(100));
+  }
+ private:
+  bool *value_;
+};
+
+class ReleaseStoreThread : public PlatformThread::Delegate {
+ public:
+  explicit ReleaseStoreThread(base::subtle::Atomic32 *value) : value_(value) {}
+  ~ReleaseStoreThread() override = default;
+  void ThreadMain() override {
+    base::subtle::Release_Store(value_, kMagicValue);
+
+    // Sleep for a few milliseconds so the two threads are more likely to live
+    // simultaneously. Otherwise we may miss the report due to mutex
+    // lock/unlock's inside thread creation code in pure-happens-before mode...
+    PlatformThread::Sleep(TimeDelta::FromMilliseconds(100));
+  }
+ private:
+  base::subtle::Atomic32 *value_;
+};
+
+class AcquireLoadThread : public PlatformThread::Delegate {
+ public:
+  explicit AcquireLoadThread(base::subtle::Atomic32 *value) : value_(value) {}
+  ~AcquireLoadThread() override = default;
+  void ThreadMain() override {
+    // Wait for the other thread to make Release_Store
+    PlatformThread::Sleep(TimeDelta::FromMilliseconds(100));
+    base::subtle::Acquire_Load(value_);
+  }
+ private:
+  base::subtle::Atomic32 *value_;
+};
+
+void RunInParallel(PlatformThread::Delegate *d1, PlatformThread::Delegate *d2) {
+  PlatformThreadHandle a;
+  PlatformThreadHandle b;
+  PlatformThread::Create(0, d1, &a);
+  PlatformThread::Create(0, d2, &b);
+  PlatformThread::Join(a);
+  PlatformThread::Join(b);
+}
+
+#if defined(THREAD_SANITIZER)
+void DataRace() {
+  bool *shared = new bool(false);
+  TOOLS_SANITY_TEST_CONCURRENT_THREAD thread1(shared), thread2(shared);
+  RunInParallel(&thread1, &thread2);
+  EXPECT_TRUE(*shared);
+  delete shared;
+  // We're in a death test - crash.
+  CHECK(0);
+}
+#endif
+
+}  // namespace
+
+#if defined(THREAD_SANITIZER)
+// A data race detector should report an error in this test.
+TEST(ToolsSanityTest, DataRace) {
+  // The suppression regexp must match that in base/debug/tsan_suppressions.cc.
+  EXPECT_DEATH(DataRace(), "1 race:base/tools_sanity_unittest.cc");
+}
+#endif
+
+TEST(ToolsSanityTest, AnnotateBenignRace) {
+  bool shared = false;
+  ANNOTATE_BENIGN_RACE(&shared, "Intentional race - make sure doesn't show up");
+  TOOLS_SANITY_TEST_CONCURRENT_THREAD thread1(&shared), thread2(&shared);
+  RunInParallel(&thread1, &thread2);
+  EXPECT_TRUE(shared);
+}
+
+TEST(ToolsSanityTest, AtomicsAreIgnored) {
+  base::subtle::Atomic32 shared = 0;
+  ReleaseStoreThread thread1(&shared);
+  AcquireLoadThread thread2(&shared);
+  RunInParallel(&thread1, &thread2);
+  EXPECT_EQ(kMagicValue, shared);
+}
+
+#if BUILDFLAG(CFI_ENFORCEMENT_TRAP)
+#if defined(OS_WIN)
+#define CFI_ERROR_MSG "EXCEPTION_ILLEGAL_INSTRUCTION"
+#elif defined(OS_ANDROID)
+// TODO(pcc): Produce proper stack dumps on Android and test for the correct
+// si_code here.
+#define CFI_ERROR_MSG "^$"
+#else
+#define CFI_ERROR_MSG "ILL_ILLOPN"
+#endif
+#elif BUILDFLAG(CFI_ENFORCEMENT_DIAGNOSTIC)
+#define CFI_ERROR_MSG "runtime error: control flow integrity check"
+#endif  // BUILDFLAG(CFI_ENFORCEMENT_TRAP || CFI_ENFORCEMENT_DIAGNOSTIC)
+
+#if defined(CFI_ERROR_MSG)
+class A {
+ public:
+  A(): n_(0) {}
+  virtual void f() { n_++; }
+ protected:
+  int n_;
+};
+
+class B: public A {
+ public:
+  void f() override { n_--; }
+};
+
+class C: public B {
+ public:
+  void f() override { n_ += 2; }
+};
+
+NOINLINE void KillVptrAndCall(A *obj) {
+  *reinterpret_cast<void **>(obj) = 0;
+  obj->f();
+}
+
+TEST(ToolsSanityTest, BadVirtualCallNull) {
+  A a;
+  B b;
+  EXPECT_DEATH({ KillVptrAndCall(&a); KillVptrAndCall(&b); }, CFI_ERROR_MSG);
+}
+
+NOINLINE void OverwriteVptrAndCall(B *obj, A *vptr) {
+  *reinterpret_cast<void **>(obj) = *reinterpret_cast<void **>(vptr);
+  obj->f();
+}
+
+TEST(ToolsSanityTest, BadVirtualCallWrongType) {
+  A a;
+  B b;
+  C c;
+  EXPECT_DEATH({ OverwriteVptrAndCall(&b, &a); OverwriteVptrAndCall(&b, &c); },
+               CFI_ERROR_MSG);
+}
+
+// TODO(pcc): remove CFI_CAST_CHECK, see https://crbug.com/626794.
+#if BUILDFLAG(CFI_CAST_CHECK)
+TEST(ToolsSanityTest, BadDerivedCast) {
+  A a;
+  EXPECT_DEATH((void)(B*)&a, CFI_ERROR_MSG);
+}
+
+TEST(ToolsSanityTest, BadUnrelatedCast) {
+  class A {
+    virtual void f() {}
+  };
+
+  class B {
+    virtual void f() {}
+  };
+
+  A a;
+  EXPECT_DEATH((void)(B*)&a, CFI_ERROR_MSG);
+}
+#endif  // BUILDFLAG(CFI_CAST_CHECK)
+
+#endif  // CFI_ERROR_MSG
+
+#undef CFI_ERROR_MSG
+#undef MAYBE_AccessesToNewMemory
+#undef MAYBE_AccessesToMallocMemory
+#undef MAYBE_ArrayDeletedWithoutBraces
+#undef MAYBE_SingleElementDeletedWithBraces
+#undef HARMFUL_ACCESS
+#undef HARMFUL_ACCESS_IS_NOOP
+
+}  // namespace base
diff --git a/base/trace_event/auto_open_close_event.cc b/base/trace_event/auto_open_close_event.cc
new file mode 100644
index 0000000..1879700
--- /dev/null
+++ b/base/trace_event/auto_open_close_event.cc
@@ -0,0 +1,52 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/trace_event/auto_open_close_event.h"
+
+#include "base/macros.h"
+#include "base/time/time.h"
+#include "base/trace_event/trace_event.h"
+
+namespace base {
+namespace trace_event {
+
+AutoOpenCloseEvent::AutoOpenCloseEvent(AutoOpenCloseEvent::Type type,
+  const char* category, const char* event_name):
+  category_(category),
+  event_name_(event_name),
+  weak_factory_(this) {
+  base::trace_event::TraceLog::GetInstance()->AddAsyncEnabledStateObserver(
+      weak_factory_.GetWeakPtr());
+}
+
+AutoOpenCloseEvent::~AutoOpenCloseEvent() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  base::trace_event::TraceLog::GetInstance()->RemoveAsyncEnabledStateObserver(
+      this);
+}
+
+void AutoOpenCloseEvent::Begin() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  start_time_ = TRACE_TIME_TICKS_NOW();
+  TRACE_EVENT_ASYNC_BEGIN_WITH_TIMESTAMP0(
+      category_, event_name_, static_cast<void*>(this), start_time_);
+}
+
+void AutoOpenCloseEvent::End() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  TRACE_EVENT_ASYNC_END0(category_, event_name_, static_cast<void*>(this));
+  start_time_ = base::TimeTicks();
+}
+
+void AutoOpenCloseEvent::OnTraceLogEnabled() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  if (start_time_.ToInternalValue() != 0)
+    TRACE_EVENT_ASYNC_BEGIN_WITH_TIMESTAMP0(
+        category_, event_name_, static_cast<void*>(this), start_time_);
+}
+
+void AutoOpenCloseEvent::OnTraceLogDisabled() {}
+
+}  // namespace trace_event
+}  // namespace base
diff --git a/base/trace_event/auto_open_close_event.h b/base/trace_event/auto_open_close_event.h
new file mode 100644
index 0000000..795a494
--- /dev/null
+++ b/base/trace_event/auto_open_close_event.h
@@ -0,0 +1,57 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_AUTO_OPEN_CLOSE_EVENT_H_
+#define BASE_AUTO_OPEN_CLOSE_EVENT_H_
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/threading/thread_checker.h"
+#include "base/time/time.h"
+#include "base/trace_event/trace_event.h"
+
+namespace base {
+namespace trace_event {
+
+// Class for tracing events that support "auto-opening" and "auto-closing".
+// "auto-opening" = if the trace event is started (call Begin() before
+// tracing is started,the trace event will be opened, with the start time
+// being the time that the trace event was actually started.
+// "auto-closing" = if the trace event is started but not ended by the time
+// tracing ends, then the trace event will be automatically closed at the
+// end of tracing.
+class BASE_EXPORT AutoOpenCloseEvent
+    : public TraceLog::AsyncEnabledStateObserver {
+ public:
+  enum Type {
+    ASYNC
+  };
+
+  // As in the rest of the tracing macros, the const char* arguments here
+  // must be pointers to indefinitely lived strings (e.g. hard-coded string
+  // literals are okay, but not strings created by c_str())
+  AutoOpenCloseEvent(Type type, const char* category, const char* event_name);
+  ~AutoOpenCloseEvent() override;
+
+  void Begin();
+  void End();
+
+  // AsyncEnabledStateObserver implementation
+  void OnTraceLogEnabled() override;
+  void OnTraceLogDisabled() override;
+
+ private:
+  const char* const category_;
+  const char* const event_name_;
+  base::TimeTicks start_time_;
+  base::ThreadChecker thread_checker_;
+  WeakPtrFactory<AutoOpenCloseEvent> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(AutoOpenCloseEvent);
+};
+
+}  // namespace trace_event
+}  // namespace base
+
+#endif  // BASE_AUTO_OPEN_CLOSE_EVENT_H_
\ No newline at end of file
diff --git a/base/trace_event/blame_context.cc b/base/trace_event/blame_context.cc
new file mode 100644
index 0000000..ae0b718
--- /dev/null
+++ b/base/trace_event/blame_context.cc
@@ -0,0 +1,111 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/trace_event/blame_context.h"
+
+#include "base/strings/stringprintf.h"
+#include "base/trace_event/trace_event.h"
+#include "base/trace_event/trace_event_argument.h"
+
+namespace base {
+namespace trace_event {
+
+BlameContext::BlameContext(const char* category,
+                           const char* name,
+                           const char* type,
+                           const char* scope,
+                           int64_t id,
+                           const BlameContext* parent_context)
+    : category_(category),
+      name_(name),
+      type_(type),
+      scope_(scope),
+      id_(id),
+      parent_scope_(parent_context ? parent_context->scope() : nullptr),
+      parent_id_(parent_context ? parent_context->id() : 0),
+      category_group_enabled_(nullptr),
+      weak_factory_(this) {
+  DCHECK(!parent_context || !std::strcmp(name_, parent_context->name()))
+      << "Parent blame context must have the same name";
+}
+
+BlameContext::~BlameContext() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(WasInitialized());
+  TRACE_EVENT_API_ADD_TRACE_EVENT(
+      TRACE_EVENT_PHASE_DELETE_OBJECT, category_group_enabled_, type_, scope_,
+      id_, 0, nullptr, nullptr, nullptr, nullptr, TRACE_EVENT_FLAG_HAS_ID);
+  trace_event::TraceLog::GetInstance()->RemoveAsyncEnabledStateObserver(this);
+}
+
+void BlameContext::Enter() {
+  DCHECK(WasInitialized());
+  TRACE_EVENT_API_ADD_TRACE_EVENT(TRACE_EVENT_PHASE_ENTER_CONTEXT,
+                                  category_group_enabled_, name_, scope_, id_,
+                                  0 /* num_args */, nullptr, nullptr, nullptr,
+                                  nullptr, TRACE_EVENT_FLAG_HAS_ID);
+}
+
+void BlameContext::Leave() {
+  DCHECK(WasInitialized());
+  TRACE_EVENT_API_ADD_TRACE_EVENT(TRACE_EVENT_PHASE_LEAVE_CONTEXT,
+                                  category_group_enabled_, name_, scope_, id_,
+                                  0 /* num_args */, nullptr, nullptr, nullptr,
+                                  nullptr, TRACE_EVENT_FLAG_HAS_ID);
+}
+
+void BlameContext::TakeSnapshot() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(WasInitialized());
+  if (!*category_group_enabled_)
+    return;
+  std::unique_ptr<trace_event::TracedValue> snapshot(
+      new trace_event::TracedValue);
+  AsValueInto(snapshot.get());
+  static const char* const kArgName = "snapshot";
+  const int kNumArgs = 1;
+  unsigned char arg_types[1] = {TRACE_VALUE_TYPE_CONVERTABLE};
+  std::unique_ptr<trace_event::ConvertableToTraceFormat> arg_values[1] = {
+      std::move(snapshot)};
+  TRACE_EVENT_API_ADD_TRACE_EVENT(TRACE_EVENT_PHASE_SNAPSHOT_OBJECT,
+                                  category_group_enabled_, type_, scope_, id_,
+                                  kNumArgs, &kArgName, arg_types, nullptr,
+                                  arg_values, TRACE_EVENT_FLAG_HAS_ID);
+}
+
+void BlameContext::OnTraceLogEnabled() {
+  DCHECK(WasInitialized());
+  TakeSnapshot();
+}
+
+void BlameContext::OnTraceLogDisabled() {}
+
+void BlameContext::AsValueInto(trace_event::TracedValue* state) {
+  DCHECK(WasInitialized());
+  if (!parent_id_)
+    return;
+  state->BeginDictionary("parent");
+  state->SetString("id_ref", StringPrintf("0x%" PRIx64, parent_id_));
+  state->SetString("scope", parent_scope_);
+  state->EndDictionary();
+}
+
+void BlameContext::Initialize() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  category_group_enabled_ =
+      TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(category_);
+  TRACE_EVENT_API_ADD_TRACE_EVENT(
+      TRACE_EVENT_PHASE_CREATE_OBJECT, category_group_enabled_, type_, scope_,
+      id_, 0, nullptr, nullptr, nullptr, nullptr, TRACE_EVENT_FLAG_HAS_ID);
+  trace_event::TraceLog::GetInstance()->AddAsyncEnabledStateObserver(
+      weak_factory_.GetWeakPtr());
+  TakeSnapshot();
+}
+
+bool BlameContext::WasInitialized() const {
+  return category_group_enabled_ != nullptr;
+}
+
+}  // namespace trace_event
+}  // namespace base
diff --git a/base/trace_event/blame_context.h b/base/trace_event/blame_context.h
new file mode 100644
index 0000000..a973a28
--- /dev/null
+++ b/base/trace_event/blame_context.h
@@ -0,0 +1,138 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TRACE_EVENT_BLAME_CONTEXT_H_
+#define BASE_TRACE_EVENT_BLAME_CONTEXT_H_
+
+#include <inttypes.h>
+
+#include "base/base_export.h"
+#include "base/macros.h"
+#include "base/threading/thread_checker.h"
+#include "base/trace_event/trace_log.h"
+
+namespace base {
+namespace trace_event {
+class TracedValue;
+}
+
+namespace trace_event {
+
+// A blame context represents a logical unit to which we want to attribute
+// different costs (e.g., CPU, network, or memory usage). An example of a blame
+// context is an <iframe> element on a web page. Different subsystems can
+// "enter" and "leave" blame contexts to indicate that they are doing work which
+// should be accounted against this blame context.
+//
+// A blame context can optionally have a parent context, forming a blame context
+// tree. When work is attributed to a particular blame context, it is considered
+// to count against all of that context's children too. This is useful when work
+// cannot be exactly attributed into a more specific context. For example,
+// Javascript garbage collection generally needs to inspect all objects on a
+// page instead looking at each <iframe> individually. In this case the work
+// should be attributed to a blame context which is the parent of all <iframe>
+// blame contexts.
+class BASE_EXPORT BlameContext
+    : public trace_event::TraceLog::AsyncEnabledStateObserver {
+ public:
+  // Construct a blame context belonging to the blame context tree |name|, using
+  // the tracing category |category|, identified by |id| from the |scope|
+  // namespace. |type| identifies the type of this object snapshot in the blame
+  // context tree. |parent_context| is the parent of this blame context or
+  // null. Note that all strings must have application lifetime.
+  //
+  // For example, a blame context which represents a specific <iframe> in a
+  // browser frame tree could be specified with:
+  //
+  //   category="blink",
+  //   name="FrameTree",
+  //   type="IFrame",
+  //   scope="IFrameIdentifier",
+  //   id=1234.
+  //
+  // Each <iframe> blame context could have another <iframe> context as a
+  // parent, or a top-level context which represents the entire browser:
+  //
+  //   category="blink",
+  //   name="FrameTree",
+  //   type="Browser",
+  //   scope="BrowserIdentifier",
+  //   id=1.
+  //
+  // Note that the |name| property is identical, signifying that both context
+  // types are part of the same tree.
+  //
+  BlameContext(const char* category,
+               const char* name,
+               const char* type,
+               const char* scope,
+               int64_t id,
+               const BlameContext* parent_context);
+  ~BlameContext() override;
+
+  // Initialize the blame context, automatically taking a snapshot if tracing is
+  // enabled. Must be called before any other methods on this class.
+  void Initialize();
+
+  // Indicate that the current thread is now doing work which should count
+  // against this blame context.  This function is allowed to be called in a
+  // thread different from where the blame context was created; However, any
+  // client doing that must be fully responsible for ensuring thready safety.
+  void Enter();
+
+  // Leave and stop doing work for a previously entered blame context. If
+  // another blame context belonging to the same tree was entered prior to this
+  // one, it becomes the active blame context for this thread again.  Similar
+  // to Enter(), this function can be called in a thread different from where
+  // the blame context was created, and the same requirement on thread safety
+  // must be satisfied.
+  void Leave();
+
+  // Record a snapshot of the blame context. This is normally only needed if a
+  // blame context subclass defines custom properties (see AsValueInto) and one
+  // or more of those properties have changed.
+  void TakeSnapshot();
+
+  const char* category() const { return category_; }
+  const char* name() const { return name_; }
+  const char* type() const { return type_; }
+  const char* scope() const { return scope_; }
+  int64_t id() const { return id_; }
+
+  // trace_event::TraceLog::EnabledStateObserver implementation:
+  void OnTraceLogEnabled() override;
+  void OnTraceLogDisabled() override;
+
+ protected:
+  // Serialize the properties of this blame context into |state|. Subclasses can
+  // override this method to record additional properties (e.g, the URL for an
+  // <iframe> blame context). Note that an overridden implementation must still
+  // call this base method.
+  virtual void AsValueInto(trace_event::TracedValue* state);
+
+ private:
+  bool WasInitialized() const;
+
+  // The following string pointers have application lifetime.
+  const char* category_;
+  const char* name_;
+  const char* type_;
+  const char* scope_;
+  const int64_t id_;
+
+  const char* parent_scope_;
+  const int64_t parent_id_;
+
+  const unsigned char* category_group_enabled_;
+
+  ThreadChecker thread_checker_;
+  WeakPtrFactory<BlameContext> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(BlameContext);
+};
+
+}  // namespace trace_event
+}  // namespace base
+
+#endif  // BASE_TRACE_EVENT_BLAME_CONTEXT_H_
diff --git a/base/trace_event/blame_context_unittest.cc b/base/trace_event/blame_context_unittest.cc
new file mode 100644
index 0000000..12e7857
--- /dev/null
+++ b/base/trace_event/blame_context_unittest.cc
@@ -0,0 +1,175 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/trace_event/blame_context.h"
+
+#include "base/json/json_writer.h"
+#include "base/message_loop/message_loop.h"
+#include "base/test/trace_event_analyzer.h"
+#include "base/trace_event/trace_event_argument.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace trace_event {
+namespace {
+
+const char kTestBlameContextCategory[] = "test";
+const char kDisabledTestBlameContextCategory[] = "disabled-by-default-test";
+const char kTestBlameContextName[] = "TestBlameContext";
+const char kTestBlameContextType[] = "TestBlameContextType";
+const char kTestBlameContextScope[] = "TestBlameContextScope";
+
+class TestBlameContext : public BlameContext {
+ public:
+  explicit TestBlameContext(int id)
+      : BlameContext(kTestBlameContextCategory,
+                     kTestBlameContextName,
+                     kTestBlameContextType,
+                     kTestBlameContextScope,
+                     id,
+                     nullptr) {}
+
+  TestBlameContext(int id, const TestBlameContext& parent)
+      : BlameContext(kTestBlameContextCategory,
+                     kTestBlameContextName,
+                     kTestBlameContextType,
+                     kTestBlameContextScope,
+                     id,
+                     &parent) {}
+
+ protected:
+  void AsValueInto(trace_event::TracedValue* state) override {
+    BlameContext::AsValueInto(state);
+    state->SetBoolean("crossStreams", false);
+  }
+};
+
+class DisabledTestBlameContext : public BlameContext {
+ public:
+  explicit DisabledTestBlameContext(int id)
+      : BlameContext(kDisabledTestBlameContextCategory,
+                     kTestBlameContextName,
+                     kTestBlameContextType,
+                     kTestBlameContextScope,
+                     id,
+                     nullptr) {}
+};
+
+class BlameContextTest : public testing::Test {
+ protected:
+  MessageLoop loop_;
+};
+
+TEST_F(BlameContextTest, EnterAndLeave) {
+  using trace_analyzer::Query;
+  trace_analyzer::Start("*");
+  {
+    TestBlameContext blame_context(0x1234);
+    blame_context.Initialize();
+    blame_context.Enter();
+    blame_context.Leave();
+  }
+  auto analyzer = trace_analyzer::Stop();
+
+  trace_analyzer::TraceEventVector events;
+  Query q = Query::EventPhaseIs(TRACE_EVENT_PHASE_ENTER_CONTEXT) ||
+            Query::EventPhaseIs(TRACE_EVENT_PHASE_LEAVE_CONTEXT);
+  analyzer->FindEvents(q, &events);
+
+  EXPECT_EQ(2u, events.size());
+  EXPECT_EQ(TRACE_EVENT_PHASE_ENTER_CONTEXT, events[0]->phase);
+  EXPECT_EQ(kTestBlameContextCategory, events[0]->category);
+  EXPECT_EQ(kTestBlameContextName, events[0]->name);
+  EXPECT_EQ("0x1234", events[0]->id);
+  EXPECT_EQ(TRACE_EVENT_PHASE_LEAVE_CONTEXT, events[1]->phase);
+  EXPECT_EQ(kTestBlameContextCategory, events[1]->category);
+  EXPECT_EQ(kTestBlameContextName, events[1]->name);
+  EXPECT_EQ("0x1234", events[1]->id);
+}
+
+TEST_F(BlameContextTest, DifferentCategories) {
+  // Ensure there is no cross talk between blame contexts from different
+  // categories.
+  using trace_analyzer::Query;
+  trace_analyzer::Start("*");
+  {
+    TestBlameContext blame_context(0x1234);
+    DisabledTestBlameContext disabled_blame_context(0x5678);
+    blame_context.Initialize();
+    blame_context.Enter();
+    blame_context.Leave();
+    disabled_blame_context.Initialize();
+    disabled_blame_context.Enter();
+    disabled_blame_context.Leave();
+  }
+  auto analyzer = trace_analyzer::Stop();
+
+  trace_analyzer::TraceEventVector events;
+  Query q = Query::EventPhaseIs(TRACE_EVENT_PHASE_ENTER_CONTEXT) ||
+            Query::EventPhaseIs(TRACE_EVENT_PHASE_LEAVE_CONTEXT);
+  analyzer->FindEvents(q, &events);
+
+  // None of the events from the disabled-by-default category should show up.
+  EXPECT_EQ(2u, events.size());
+  EXPECT_EQ(TRACE_EVENT_PHASE_ENTER_CONTEXT, events[0]->phase);
+  EXPECT_EQ(kTestBlameContextCategory, events[0]->category);
+  EXPECT_EQ(kTestBlameContextName, events[0]->name);
+  EXPECT_EQ("0x1234", events[0]->id);
+  EXPECT_EQ(TRACE_EVENT_PHASE_LEAVE_CONTEXT, events[1]->phase);
+  EXPECT_EQ(kTestBlameContextCategory, events[1]->category);
+  EXPECT_EQ(kTestBlameContextName, events[1]->name);
+  EXPECT_EQ("0x1234", events[1]->id);
+}
+
+TEST_F(BlameContextTest, TakeSnapshot) {
+  using trace_analyzer::Query;
+  trace_analyzer::Start("*");
+  {
+    TestBlameContext parent_blame_context(0x5678);
+    TestBlameContext blame_context(0x1234, parent_blame_context);
+    parent_blame_context.Initialize();
+    blame_context.Initialize();
+    blame_context.TakeSnapshot();
+  }
+  auto analyzer = trace_analyzer::Stop();
+
+  trace_analyzer::TraceEventVector events;
+  Query q = Query::EventPhaseIs(TRACE_EVENT_PHASE_SNAPSHOT_OBJECT);
+  analyzer->FindEvents(q, &events);
+
+  // We should have 3 snapshots: one for both calls to Initialize() and one from
+  // the explicit call to TakeSnapshot().
+  EXPECT_EQ(3u, events.size());
+  EXPECT_EQ(kTestBlameContextCategory, events[0]->category);
+  EXPECT_EQ(kTestBlameContextType, events[0]->name);
+  EXPECT_EQ("0x5678", events[0]->id);
+  EXPECT_TRUE(events[0]->HasArg("snapshot"));
+
+  EXPECT_EQ(kTestBlameContextCategory, events[1]->category);
+  EXPECT_EQ(kTestBlameContextType, events[1]->name);
+  EXPECT_EQ("0x1234", events[1]->id);
+  EXPECT_TRUE(events[0]->HasArg("snapshot"));
+
+  EXPECT_EQ(kTestBlameContextCategory, events[2]->category);
+  EXPECT_EQ(kTestBlameContextType, events[2]->name);
+  EXPECT_EQ("0x1234", events[2]->id);
+  EXPECT_TRUE(events[0]->HasArg("snapshot"));
+
+  const char kExpectedSnapshotJson[] =
+      "{"
+          "\"crossStreams\":false,"
+          "\"parent\":{"
+              "\"id_ref\":\"0x5678\","
+              "\"scope\":\"TestBlameContextScope\""
+          "}"
+      "}";
+
+  std::string snapshot_json;
+  JSONWriter::Write(*events[2]->GetKnownArgAsValue("snapshot"), &snapshot_json);
+  EXPECT_EQ(kExpectedSnapshotJson, snapshot_json);
+}
+
+}  // namepace
+}  // namespace trace_event
+}  // namespace base
diff --git a/base/trace_event/category_registry.cc b/base/trace_event/category_registry.cc
new file mode 100644
index 0000000..e7c1460
--- /dev/null
+++ b/base/trace_event/category_registry.cc
@@ -0,0 +1,156 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/trace_event/category_registry.h"
+
+#include <string.h>
+
+#include <type_traits>
+
+#include "base/atomicops.h"
+#include "base/debug/leak_annotations.h"
+#include "base/logging.h"
+#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
+#include "base/trace_event/trace_category.h"
+
+namespace base {
+namespace trace_event {
+
+namespace {
+
+constexpr size_t kMaxCategories = 200;
+const int kNumBuiltinCategories = 4;
+
+// |g_categories| might end up causing creating dynamic initializers if not POD.
+static_assert(std::is_pod<TraceCategory>::value, "TraceCategory must be POD");
+
+// These entries must be kept consistent with the kCategory* consts below.
+TraceCategory g_categories[kMaxCategories] = {
+    {0, 0, "tracing categories exhausted; must increase kMaxCategories"},
+    {0, 0, "tracing already shutdown"},  // See kCategoryAlreadyShutdown below.
+    {0, 0, "__metadata"},                // See kCategoryMetadata below.
+    {0, 0, "toplevel"},                  // Warmup the toplevel category.
+};
+
+base::subtle::AtomicWord g_category_index = kNumBuiltinCategories;
+
+bool IsValidCategoryPtr(const TraceCategory* category) {
+  // If any of these are hit, something has cached a corrupt category pointer.
+  uintptr_t ptr = reinterpret_cast<uintptr_t>(category);
+  return ptr % sizeof(void*) == 0 &&
+         ptr >= reinterpret_cast<uintptr_t>(&g_categories[0]) &&
+         ptr <= reinterpret_cast<uintptr_t>(&g_categories[kMaxCategories - 1]);
+}
+
+}  // namespace
+
+// static
+TraceCategory* const CategoryRegistry::kCategoryExhausted = &g_categories[0];
+TraceCategory* const CategoryRegistry::kCategoryAlreadyShutdown =
+    &g_categories[1];
+TraceCategory* const CategoryRegistry::kCategoryMetadata = &g_categories[2];
+
+// static
+void CategoryRegistry::Initialize() {
+  // Trace is enabled or disabled on one thread while other threads are
+  // accessing the enabled flag. We don't care whether edge-case events are
+  // traced or not, so we allow races on the enabled flag to keep the trace
+  // macros fast.
+  for (size_t i = 0; i < kMaxCategories; ++i) {
+    ANNOTATE_BENIGN_RACE(g_categories[i].state_ptr(),
+                         "trace_event category enabled");
+    // If this DCHECK is hit in a test it means that ResetForTesting() is not
+    // called and the categories state leaks between test fixtures.
+    DCHECK(!g_categories[i].is_enabled());
+  }
+}
+
+// static
+void CategoryRegistry::ResetForTesting() {
+  // reset_for_testing clears up only the enabled state and filters. The
+  // categories themselves cannot be cleared up because the static pointers
+  // injected by the macros still point to them and cannot be reset.
+  for (size_t i = 0; i < kMaxCategories; ++i)
+    g_categories[i].reset_for_testing();
+}
+
+// static
+TraceCategory* CategoryRegistry::GetCategoryByName(const char* category_name) {
+  DCHECK(!strchr(category_name, '"'))
+      << "Category names may not contain double quote";
+
+  // The g_categories is append only, avoid using a lock for the fast path.
+  size_t category_index = base::subtle::Acquire_Load(&g_category_index);
+
+  // Search for pre-existing category group.
+  for (size_t i = 0; i < category_index; ++i) {
+    if (strcmp(g_categories[i].name(), category_name) == 0) {
+      return &g_categories[i];
+    }
+  }
+  return nullptr;
+}
+
+bool CategoryRegistry::GetOrCreateCategoryLocked(
+    const char* category_name,
+    CategoryInitializerFn category_initializer_fn,
+    TraceCategory** category) {
+  // This is the slow path: the lock is not held in the fastpath
+  // (GetCategoryByName), so more than one thread could have reached here trying
+  // to add the same category.
+  *category = GetCategoryByName(category_name);
+  if (*category)
+    return false;
+
+  // Create a new category.
+  size_t category_index = base::subtle::Acquire_Load(&g_category_index);
+  if (category_index >= kMaxCategories) {
+    NOTREACHED() << "must increase kMaxCategories";
+    *category = kCategoryExhausted;
+    return false;
+  }
+
+  // TODO(primiano): this strdup should be removed. The only documented reason
+  // for it was TraceWatchEvent, which is gone. However, something might have
+  // ended up relying on this. Needs some auditing before removal.
+  const char* category_name_copy = strdup(category_name);
+  ANNOTATE_LEAKING_OBJECT_PTR(category_name_copy);
+
+  *category = &g_categories[category_index];
+  DCHECK(!(*category)->is_valid());
+  DCHECK(!(*category)->is_enabled());
+  (*category)->set_name(category_name_copy);
+  category_initializer_fn(*category);
+
+  // Update the max index now.
+  base::subtle::Release_Store(&g_category_index, category_index + 1);
+  return true;
+}
+
+// static
+const TraceCategory* CategoryRegistry::GetCategoryByStatePtr(
+    const uint8_t* category_state) {
+  const TraceCategory* category = TraceCategory::FromStatePtr(category_state);
+  DCHECK(IsValidCategoryPtr(category));
+  return category;
+}
+
+// static
+bool CategoryRegistry::IsBuiltinCategory(const TraceCategory* category) {
+  DCHECK(IsValidCategoryPtr(category));
+  return category < &g_categories[kNumBuiltinCategories];
+}
+
+// static
+CategoryRegistry::Range CategoryRegistry::GetAllCategories() {
+  // The |g_categories| array is append only. We have to only guarantee to
+  // not return an index to a category which is being initialized by
+  // GetOrCreateCategoryByName().
+  size_t category_index = base::subtle::Acquire_Load(&g_category_index);
+  return CategoryRegistry::Range(&g_categories[0],
+                                 &g_categories[category_index]);
+}
+
+}  // namespace trace_event
+}  // namespace base
diff --git a/base/trace_event/category_registry.h b/base/trace_event/category_registry.h
new file mode 100644
index 0000000..9c08efa
--- /dev/null
+++ b/base/trace_event/category_registry.h
@@ -0,0 +1,93 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TRACE_EVENT_CATEGORY_REGISTRY_H_
+#define BASE_TRACE_EVENT_CATEGORY_REGISTRY_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "base/base_export.h"
+#include "base/logging.h"
+
+namespace base {
+namespace trace_event {
+
+struct TraceCategory;
+class TraceCategoryTest;
+class TraceLog;
+
+// Allows fast and thread-safe acces to the state of all tracing categories.
+// All the methods in this class can be concurrently called on multiple threads,
+// unless otherwise noted (e.g., GetOrCreateCategoryLocked).
+// The reason why this is a fully static class with global state is to allow to
+// statically define known categories as global linker-initialized structs,
+// without requiring static initializers.
+class BASE_EXPORT CategoryRegistry {
+ public:
+  // Allows for-each iterations over a slice of the categories array.
+  class Range {
+   public:
+    Range(TraceCategory* begin, TraceCategory* end) : begin_(begin), end_(end) {
+      DCHECK_LE(begin, end);
+    }
+    TraceCategory* begin() const { return begin_; }
+    TraceCategory* end() const { return end_; }
+
+   private:
+    TraceCategory* const begin_;
+    TraceCategory* const end_;
+  };
+
+  // Known categories.
+  static TraceCategory* const kCategoryExhausted;
+  static TraceCategory* const kCategoryMetadata;
+  static TraceCategory* const kCategoryAlreadyShutdown;
+
+  // Returns a category entry from the Category.state_ptr() pointer.
+  // TODO(primiano): trace macros should just keep a pointer to the entire
+  // TraceCategory, not just the enabled state pointer. That would remove the
+  // need for this function and make everything cleaner at no extra cost (as
+  // long as the |state_| is the first field of the struct, which can be
+  // guaranteed via static_assert, see TraceCategory ctor).
+  static const TraceCategory* GetCategoryByStatePtr(
+      const uint8_t* category_state);
+
+  // Returns a category from its name or nullptr if not found.
+  // The output |category| argument is an undefinitely lived pointer to the
+  // TraceCategory owned by the registry. TRACE_EVENTx macros will cache this
+  // pointer and use it for checks in their fast-paths.
+  static TraceCategory* GetCategoryByName(const char* category_name);
+
+  static bool IsBuiltinCategory(const TraceCategory*);
+
+ private:
+  friend class TraceCategoryTest;
+  friend class TraceLog;
+  using CategoryInitializerFn = void (*)(TraceCategory*);
+
+  // Only for debugging/testing purposes, is a no-op on release builds.
+  static void Initialize();
+
+  // Resets the state of all categories, to clear up the state between tests.
+  static void ResetForTesting();
+
+  // Used to get/create a category in the slow-path. If the category exists
+  // already, this has the same effect of GetCategoryByName and returns false.
+  // If not, a new category is created and the CategoryInitializerFn is invoked
+  // before retuning true. The caller must guarantee serialization: either call
+  // this method from a single thread or hold a lock when calling this.
+  static bool GetOrCreateCategoryLocked(const char* category_name,
+                                        CategoryInitializerFn,
+                                        TraceCategory**);
+
+  // Allows to iterate over the valid categories in a for-each loop.
+  // This includes builtin categories such as __metadata.
+  static Range GetAllCategories();
+};
+
+}  // namespace trace_event
+}  // namespace base
+
+#endif  // BASE_TRACE_EVENT_CATEGORY_REGISTRY_H_
diff --git a/base/trace_event/cfi_backtrace_android.cc b/base/trace_event/cfi_backtrace_android.cc
new file mode 100644
index 0000000..8fd8b95
--- /dev/null
+++ b/base/trace_event/cfi_backtrace_android.cc
@@ -0,0 +1,314 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/trace_event/cfi_backtrace_android.h"
+
+#include <sys/mman.h>
+#include <sys/types.h>
+
+#include "base/android/apk_assets.h"
+
+#if !defined(ARCH_CPU_ARMEL)
+#error This file should not be built for this architecture.
+#endif
+
+/*
+Basics of unwinding:
+For each instruction in a function we need to know what is the offset of SP
+(Stack Pointer) to reach the previous function's stack frame. To know which
+function is being invoked, we need the return address of the next function. The
+CFI information for an instruction is made up of 2 offsets, CFA (Call Frame
+Address) offset and RA (Return Address) offset. The CFA offset is the change in
+SP made by the function till the current instruction. This depends on amount of
+memory allocated on stack by the function plus some registers that the function
+stores that needs to be restored at the end of function. So, at each instruction
+the CFA offset tells the offset from original SP before the function call. The
+RA offset tells us the offset from the previous SP into the current function
+where the return address is stored.
+
+The unwind table file has 2 tables UNW_INDEX and UNW_DATA, inspired from ARM
+EHABI format. The first table contains function addresses and an index into the
+UNW_DATA table. The second table contains one or more rows for the function
+unwind information.
+
+UNW_INDEX contains two columns of N rows each, where N is the number of
+functions.
+  1. First column 4 byte rows of all the function start address as offset from
+     start of the binary, in sorted order.
+  2. For each function addr, the second column contains 2 byte indices in order.
+     The indices are offsets (in count of 2 bytes) of the CFI data from start of
+     UNW_DATA.
+The last entry in the table always contains CANT_UNWIND index to specify the
+end address of the last function.
+
+UNW_DATA contains data of all the functions. Each function data contains N rows.
+The data found at the address pointed from UNW_INDEX will be:
+  2 bytes: N - number of rows that belong to current function.
+  N * 4 bytes: N rows of data. 16 bits : Address offset from function start.
+                               14 bits : CFA offset / 4.
+                                2 bits : RA offset / 4.
+If the RA offset of a row is 0, then use the offset of the previous rows in the
+same function.
+TODO(ssid): Make sure RA offset is always present.
+
+See extract_unwind_tables.py for details about how this data is extracted from
+breakpad symbol files.
+*/
+
+extern "C" {
+extern char __executable_start;
+extern char _etext;
+}
+
+namespace base {
+namespace trace_event {
+
+namespace {
+
+// The value of index when the function does not have unwind information.
+constexpr uint32_t kCantUnwind = 0xFFFF;
+
+// The mask on the CFI row data that is used to get the high 14 bits and
+// multiply it by 4 to get CFA offset. Since the last 2 bits are masked out, a
+// shift is not necessary.
+constexpr uint16_t kCFAMask = 0xfffc;
+
+// The mask on the CFI row data that is used to get the low 2 bits and multiply
+// it by 4 to get the RA offset.
+constexpr uint16_t kRAMask = 0x3;
+constexpr uint16_t kRAShift = 2;
+
+// The code in this file assumes we are running in 32-bit builds since all the
+// addresses in the unwind table are specified in 32 bits.
+static_assert(sizeof(uintptr_t) == 4,
+              "The unwind table format is only valid for 32 bit builds.");
+
+// The CFI data in UNW_DATA table starts with number of rows (N) and then
+// followed by N rows of 4 bytes long. The CFIUnwindDataRow represents a single
+// row of CFI data of a function in the table. Since we cast the memory at the
+// address after the address of number of rows, into an array of
+// CFIUnwindDataRow, the size of the struct should be 4 bytes and the order of
+// the members is fixed according to the given format. The first 2 bytes tell
+// the address of function and last 2 bytes give the CFI data for the offset.
+struct CFIUnwindDataRow {
+  // The address of the instruction in terms of offset from the start of the
+  // function.
+  uint16_t addr_offset;
+  // Represents the CFA and RA offsets to get information about next stack
+  // frame. This is the CFI data at the point before executing the instruction
+  // at |addr_offset| from the start of the function.
+  uint16_t cfi_data;
+
+  // Return the RA offset for the current unwind row.
+  size_t ra_offset() const { return (cfi_data & kRAMask) << kRAShift; }
+
+  // Returns the CFA offset for the current unwind row.
+  size_t cfa_offset() const { return cfi_data & kCFAMask; }
+};
+
+static_assert(
+    sizeof(CFIUnwindDataRow) == 4,
+    "The CFIUnwindDataRow struct must be exactly 4 bytes for searching.");
+
+}  // namespace
+
+// static
+CFIBacktraceAndroid* CFIBacktraceAndroid::GetInitializedInstance() {
+  static CFIBacktraceAndroid* instance = new CFIBacktraceAndroid();
+  return instance;
+}
+
+CFIBacktraceAndroid::CFIBacktraceAndroid()
+    : thread_local_cfi_cache_(
+          [](void* ptr) { delete static_cast<CFICache*>(ptr); }) {
+  Initialize();
+}
+
+CFIBacktraceAndroid::~CFIBacktraceAndroid() {}
+
+void CFIBacktraceAndroid::Initialize() {
+  // The address |_etext| gives the end of the .text section in the binary. This
+  // value is more accurate than parsing the memory map since the mapped
+  // regions are usualy larger than the .text section.
+  executable_end_addr_ = reinterpret_cast<uintptr_t>(&_etext);
+  // The address of |__executable_start| gives the start address of the
+  // executable. This value is used to find the offset address of the
+  // instruction in binary from PC.
+  executable_start_addr_ = reinterpret_cast<uintptr_t>(&__executable_start);
+
+  // This file name is defined by extract_unwind_tables.gni.
+  static constexpr char kCfiFileName[] = "assets/unwind_cfi_32";
+  MemoryMappedFile::Region cfi_region;
+  int fd = base::android::OpenApkAsset(kCfiFileName, &cfi_region);
+  if (fd < 0)
+    return;
+  cfi_mmap_ = std::make_unique<MemoryMappedFile>();
+  // The CFI region starts at |cfi_region.offset|.
+  if (!cfi_mmap_->Initialize(base::File(fd), cfi_region))
+    return;
+
+  ParseCFITables();
+  can_unwind_stack_frames_ = true;
+}
+
+void CFIBacktraceAndroid::ParseCFITables() {
+  // The first 4 bytes in the file is the size of UNW_INDEX table.
+  static constexpr size_t kUnwIndexRowSize =
+      sizeof(*unw_index_function_col_) + sizeof(*unw_index_indices_col_);
+  size_t unw_index_size = 0;
+  memcpy(&unw_index_size, cfi_mmap_->data(), sizeof(unw_index_size));
+  DCHECK_EQ(0u, unw_index_size % kUnwIndexRowSize);
+  // UNW_INDEX table starts after 4 bytes.
+  unw_index_function_col_ =
+      reinterpret_cast<const uintptr_t*>(cfi_mmap_->data()) + 1;
+  unw_index_row_count_ = unw_index_size / kUnwIndexRowSize;
+  unw_index_indices_col_ = reinterpret_cast<const uint16_t*>(
+      unw_index_function_col_ + unw_index_row_count_);
+
+  // The UNW_DATA table data is right after the end of UNW_INDEX table.
+  // Interpret the UNW_DATA table as an array of 2 byte numbers since the
+  // indexes we have from the UNW_INDEX table are in terms of 2 bytes.
+  unw_data_start_addr_ = unw_index_indices_col_ + unw_index_row_count_;
+}
+
+size_t CFIBacktraceAndroid::Unwind(const void** out_trace, size_t max_depth) {
+  // This function walks the stack using the call frame information to find the
+  // return addresses of all the functions that belong to current binary in call
+  // stack. For each function the CFI table defines the offset of the previous
+  // call frame and offset where the return address is stored.
+  if (!can_unwind_stack_frames())
+    return 0;
+
+  // Get the current register state. This register state can be taken at any
+  // point in the function and the unwind information would be for this point.
+  // Define local variables before trying to get the current PC and SP to make
+  // sure the register state obtained is consistent with each other.
+  uintptr_t pc = 0, sp = 0;
+  asm volatile("mov %0, pc" : "=r"(pc));
+  asm volatile("mov %0, sp" : "=r"(sp));
+
+  // We can only unwind as long as the pc is within the chrome.so.
+  size_t depth = 0;
+  while (pc > executable_start_addr_ && pc <= executable_end_addr_ &&
+         depth < max_depth) {
+    out_trace[depth++] = reinterpret_cast<void*>(pc);
+    // The offset of function from the start of the chrome.so binary:
+    uintptr_t func_addr = pc - executable_start_addr_;
+    CFIRow cfi{};
+    if (!FindCFIRowForPC(func_addr, &cfi))
+      break;
+
+    // The rules for unwinding using the CFI information are:
+    // SP_prev = SP_cur + cfa_offset and
+    // PC_prev = * (SP_prev - ra_offset).
+    sp = sp + cfi.cfa_offset;
+    memcpy(&pc, reinterpret_cast<uintptr_t*>(sp - cfi.ra_offset),
+           sizeof(uintptr_t));
+  }
+  return depth;
+}
+
+bool CFIBacktraceAndroid::FindCFIRowForPC(uintptr_t func_addr,
+                                          CFIBacktraceAndroid::CFIRow* cfi) {
+  auto* cache = GetThreadLocalCFICache();
+  *cfi = {0};
+  if (cache->Find(func_addr, cfi))
+    return true;
+
+  // Consider each column of UNW_INDEX table as arrays of uintptr_t (function
+  // addresses) and uint16_t (indices). Define start and end iterator on the
+  // first column array (addresses) and use std::lower_bound() to binary search
+  // on this array to find the required function address.
+  static const uintptr_t* const unw_index_fn_end =
+      unw_index_function_col_ + unw_index_row_count_;
+  const uintptr_t* found =
+      std::lower_bound(unw_index_function_col_, unw_index_fn_end, func_addr);
+
+  // If found is start, then the given function is not in the table. If the
+  // given pc is start of a function then we cannot unwind.
+  if (found == unw_index_function_col_ || *found == func_addr)
+    return false;
+
+  // std::lower_bound() returns the iter that corresponds to the first address
+  // that is greater than the given address. So, the required iter is always one
+  // less than the value returned by std::lower_bound().
+  --found;
+  uintptr_t func_start_addr = *found;
+  size_t row_num = found - unw_index_function_col_;
+  uint16_t index = unw_index_indices_col_[row_num];
+  DCHECK_LE(func_start_addr, func_addr);
+  // If the index is CANT_UNWIND then we do not have unwind infomation for the
+  // function.
+  if (index == kCantUnwind)
+    return false;
+
+  // The unwind data for the current function is at an offsset of the index
+  // found in UNW_INDEX table.
+  const uint16_t* unwind_data = unw_data_start_addr_ + index;
+  // The value of first 2 bytes is the CFI data row count for the function.
+  uint16_t row_count = 0;
+  memcpy(&row_count, unwind_data, sizeof(row_count));
+  // And the actual CFI rows start after 2 bytes from the |unwind_data|. Cast
+  // the data into an array of CFIUnwindDataRow since the struct is designed to
+  // represent each row. We should be careful to read only |row_count| number of
+  // elements in the array.
+  const CFIUnwindDataRow* function_data =
+      reinterpret_cast<const CFIUnwindDataRow*>(unwind_data + 1);
+
+  // Iterate through the CFI rows of the function to find the row that gives
+  // offset for the given instruction address.
+  CFIUnwindDataRow cfi_row = {0, 0};
+  uint16_t ra_offset = 0;
+  for (uint16_t i = 0; i < row_count; ++i) {
+    CFIUnwindDataRow row;
+    memcpy(&row, function_data + i, sizeof(CFIUnwindDataRow));
+    // The return address of the function is the instruction that is not yet
+    // been executed. The cfi row specifies the unwind info before executing the
+    // given instruction. If the given address is equal to the instruction
+    // offset, then use the current row. Or use the row with highest address
+    // less than the given address.
+    if (row.addr_offset + func_start_addr > func_addr)
+      break;
+
+    cfi_row = row;
+    // The ra offset of the last specified row should be used, if unspecified.
+    // So, keep updating the RA offset till we reach the correct CFI row.
+    // TODO(ssid): This should be fixed in the format and we should always
+    // output ra offset.
+    if (cfi_row.ra_offset())
+      ra_offset = cfi_row.ra_offset();
+  }
+  DCHECK_NE(0u, cfi_row.addr_offset);
+  *cfi = {cfi_row.cfa_offset(), ra_offset};
+  DCHECK(cfi->cfa_offset);
+  DCHECK(cfi->ra_offset);
+
+  // safe to update since the cache is thread local.
+  cache->Add(func_addr, *cfi);
+  return true;
+}
+
+CFIBacktraceAndroid::CFICache* CFIBacktraceAndroid::GetThreadLocalCFICache() {
+  auto* cache = static_cast<CFICache*>(thread_local_cfi_cache_.Get());
+  if (!cache) {
+    cache = new CFICache();
+    thread_local_cfi_cache_.Set(cache);
+  }
+  return cache;
+}
+
+void CFIBacktraceAndroid::CFICache::Add(uintptr_t address, CFIRow cfi) {
+  cache_[address % kLimit] = {address, cfi};
+}
+
+bool CFIBacktraceAndroid::CFICache::Find(uintptr_t address, CFIRow* cfi) {
+  if (cache_[address % kLimit].address == address) {
+    *cfi = cache_[address % kLimit].cfi;
+    return true;
+  }
+  return false;
+}
+
+}  // namespace trace_event
+}  // namespace base
diff --git a/base/trace_event/cfi_backtrace_android.h b/base/trace_event/cfi_backtrace_android.h
new file mode 100644
index 0000000..0c51332
--- /dev/null
+++ b/base/trace_event/cfi_backtrace_android.h
@@ -0,0 +1,157 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TRACE_EVENT_CFI_BACKTRACE_ANDROID_H_
+#define BASE_TRACE_EVENT_CFI_BACKTRACE_ANDROID_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/base_export.h"
+#include "base/debug/debugging_buildflags.h"
+#include "base/files/memory_mapped_file.h"
+#include "base/gtest_prod_util.h"
+#include "base/threading/thread_local_storage.h"
+
+namespace base {
+namespace trace_event {
+
+// This class is used to unwind stack frames in the current thread. The unwind
+// information (dwarf debug info) is stripped from the chrome binary and we do
+// not build with exception tables (ARM EHABI) in release builds. So, we use a
+// custom unwind table which is generated and added to specific android builds,
+// when add_unwind_tables_in_apk build option is specified. This unwind table
+// contains information for unwinding stack frames when the functions calls are
+// from lib[mono]chrome.so. The file is added as an asset to the apk and the
+// table is used to unwind stack frames for profiling. This class implements
+// methods to read and parse the unwind table and unwind stack frames using this
+// data.
+class BASE_EXPORT CFIBacktraceAndroid {
+ public:
+  // Creates and initializes by memory mapping the unwind tables from apk assets
+  // on first call.
+  static CFIBacktraceAndroid* GetInitializedInstance();
+
+  // Returns true if stack unwinding is possible using CFI unwind tables in apk.
+  // There is no need to check this before each unwind call. Will always return
+  // the same value based on CFI tables being present in the binary.
+  bool can_unwind_stack_frames() const { return can_unwind_stack_frames_; }
+
+  // Returns the program counters by unwinding stack in the current thread in
+  // order of latest call frame first. Unwinding works only if
+  // can_unwind_stack_frames() returns true. This function allocates memory from
+  // heap for caches. For each stack frame, this method searches through the
+  // unwind table mapped in memory to find the unwind information for function
+  // and walks the stack to find all the return address. This only works until
+  // the last function call from the chrome.so. We do not have unwind
+  // information to unwind beyond any frame outside of chrome.so. Calls to
+  // Unwind() are thread safe and lock free, once Initialize() returns success.
+  size_t Unwind(const void** out_trace, size_t max_depth);
+
+ private:
+  FRIEND_TEST_ALL_PREFIXES(CFIBacktraceAndroidTest, TestCFICache);
+  FRIEND_TEST_ALL_PREFIXES(CFIBacktraceAndroidTest, TestFindCFIRow);
+  FRIEND_TEST_ALL_PREFIXES(CFIBacktraceAndroidTest, TestUnwinding);
+
+  // The CFI information that correspond to an instruction.
+  struct CFIRow {
+    bool operator==(const CFIBacktraceAndroid::CFIRow& o) const {
+      return cfa_offset == o.cfa_offset && ra_offset == o.ra_offset;
+    }
+
+    // The offset of the call frame address of previous function from the
+    // current stack pointer. Rule for unwinding SP: SP_prev = SP_cur +
+    // cfa_offset.
+    uint16_t cfa_offset = 0;
+    // The offset of location of return address from the previous call frame
+    // address. Rule for unwinding PC: PC_prev = * (SP_prev - ra_offset).
+    uint16_t ra_offset = 0;
+  };
+
+  // A simple cache that stores entries in table using prime modulo hashing.
+  // This cache with 500 entries already gives us 95% hit rate, and fits in a
+  // single system page (usually 4KiB). Using a thread local cache for each
+  // thread gives us 30% improvements on performance of heap profiling.
+  class CFICache {
+   public:
+    // Add new item to the cache. It replaces an existing item with same hash.
+    // Constant time operation.
+    void Add(uintptr_t address, CFIRow cfi);
+
+    // Finds the given address and fills |cfi| with the info for the address.
+    // returns true if found, otherwise false. Assumes |address| is never 0.
+    bool Find(uintptr_t address, CFIRow* cfi);
+
+   private:
+    FRIEND_TEST_ALL_PREFIXES(CFIBacktraceAndroidTest, TestCFICache);
+
+    // Size is the highest prime which fits the cache in a single system page,
+    // usually 4KiB. A prime is chosen to make sure addresses are hashed evenly.
+    static const int kLimit = 509;
+
+    struct AddrAndCFI {
+      uintptr_t address;
+      CFIRow cfi;
+    };
+    AddrAndCFI cache_[kLimit] = {};
+  };
+
+  static_assert(sizeof(CFIBacktraceAndroid::CFICache) < 4096,
+                "The cache does not fit in a single page.");
+
+  CFIBacktraceAndroid();
+  ~CFIBacktraceAndroid();
+
+  // Initializes unwind tables using the CFI asset file in the apk if present.
+  // Also stores the limits of mapped region of the lib[mono]chrome.so binary,
+  // since the unwind is only feasible for addresses within the .so file. Once
+  // initialized, the memory map of the unwind table is never cleared since we
+  // cannot guarantee that all the threads are done using the memory map when
+  // heap profiling is turned off. But since we keep the memory map is clean,
+  // the system can choose to evict the unused pages when needed. This would
+  // still reduce the total amount of address space available in process.
+  void Initialize();
+
+  // Finds the UNW_INDEX and UNW_DATA tables in from the CFI file memory map.
+  void ParseCFITables();
+
+  // Finds the CFI row for the given |func_addr| in terms of offset from
+  // the start of the current binary.
+  bool FindCFIRowForPC(uintptr_t func_addr, CFIRow* out);
+
+  CFICache* GetThreadLocalCFICache();
+
+  // Details about the memory mapped region which contains the libchrome.so
+  // library file.
+  uintptr_t executable_start_addr_ = 0;
+  uintptr_t executable_end_addr_ = 0;
+
+  // The start address of the memory mapped unwind table asset file. Unique ptr
+  // because it is replaced in tests.
+  std::unique_ptr<MemoryMappedFile> cfi_mmap_;
+
+  // The UNW_INDEX table: Start address of the function address column. The
+  // memory segment corresponding to this column is treated as an array of
+  // uintptr_t.
+  const uintptr_t* unw_index_function_col_ = nullptr;
+  // The UNW_INDEX table: Start address of the index column. The memory segment
+  // corresponding to this column is treated as an array of uint16_t.
+  const uint16_t* unw_index_indices_col_ = nullptr;
+  // The number of rows in UNW_INDEX table.
+  size_t unw_index_row_count_ = 0;
+
+  // The start address of UNW_DATA table.
+  const uint16_t* unw_data_start_addr_ = nullptr;
+
+  bool can_unwind_stack_frames_ = false;
+
+  ThreadLocalStorage::Slot thread_local_cfi_cache_;
+};
+
+}  // namespace trace_event
+}  // namespace base
+
+#endif  // BASE_TRACE_EVENT_CFI_BACKTRACE_ANDROID_H_
diff --git a/base/trace_event/cfi_backtrace_android_unittest.cc b/base/trace_event/cfi_backtrace_android_unittest.cc
new file mode 100644
index 0000000..3ad3d33
--- /dev/null
+++ b/base/trace_event/cfi_backtrace_android_unittest.cc
@@ -0,0 +1,197 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/trace_event/cfi_backtrace_android.h"
+
+#include "base/files/file_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace trace_event {
+
+namespace {
+
+void* GetPC() {
+  return __builtin_return_address(0);
+}
+
+}  // namespace
+
+TEST(CFIBacktraceAndroidTest, TestUnwinding) {
+  auto* unwinder = CFIBacktraceAndroid::GetInitializedInstance();
+  EXPECT_TRUE(unwinder->can_unwind_stack_frames());
+  EXPECT_GT(unwinder->executable_start_addr_, 0u);
+  EXPECT_GT(unwinder->executable_end_addr_, unwinder->executable_start_addr_);
+  EXPECT_GT(unwinder->cfi_mmap_->length(), 0u);
+
+  const size_t kMaxFrames = 100;
+  const void* frames[kMaxFrames];
+  size_t unwind_count = unwinder->Unwind(frames, kMaxFrames);
+  // Expect at least 2 frames in the result.
+  ASSERT_GT(unwind_count, 2u);
+  EXPECT_LE(unwind_count, kMaxFrames);
+
+  const size_t kMaxCurrentFuncCodeSize = 50;
+  const uintptr_t current_pc = reinterpret_cast<uintptr_t>(GetPC());
+  const uintptr_t actual_frame = reinterpret_cast<uintptr_t>(frames[2]);
+  EXPECT_NEAR(current_pc, actual_frame, kMaxCurrentFuncCodeSize);
+
+  for (size_t i = 0; i < unwind_count; ++i) {
+    EXPECT_GT(reinterpret_cast<uintptr_t>(frames[i]),
+              unwinder->executable_start_addr_);
+    EXPECT_LT(reinterpret_cast<uintptr_t>(frames[i]),
+              unwinder->executable_end_addr_);
+  }
+}
+
+// Flaky: https://bugs.chromium.org/p/chromium/issues/detail?id=829555
+TEST(CFIBacktraceAndroidTest, DISABLED_TestFindCFIRow) {
+  auto* unwinder = CFIBacktraceAndroid::GetInitializedInstance();
+  /* Input is generated from the CFI file:
+  STACK CFI INIT 1000 500
+  STACK CFI 1002 .cfa: sp 272 + .ra: .cfa -4 + ^ r4: .cfa -16 +
+  STACK CFI 1008 .cfa: sp 544 + .r1: .cfa -0 + ^ r4: .cfa -16 + ^
+  STACK CFI 1040 .cfa: sp 816 + .r1: .cfa -0 + ^ r4: .cfa -16 + ^
+  STACK CFI 1050 .cfa: sp 816 + .ra: .cfa -8 + ^ r4: .cfa -16 + ^
+  STACK CFI 1080 .cfa: sp 544 + .r1: .cfa -0 + ^ r4: .cfa -16 + ^
+
+  STACK CFI INIT 2000 22
+  STACK CFI 2004 .cfa: sp 16 + .ra: .cfa -12 + ^ r4: .cfa -16 + ^
+  STACK CFI 2008 .cfa: sp 16 + .ra: .cfa -12 + ^ r4: .cfa -16 + ^
+
+  STACK CFI INIT 2024 100
+  STACK CFI 2030 .cfa: sp 48 + .ra: .cfa -12 + ^ r4: .cfa -16 + ^
+  STACK CFI 2100 .cfa: sp 64 + .r1: .cfa -0 + ^ r4: .cfa -16 + ^
+
+  STACK CFI INIT 2200 10
+  STACK CFI 2204 .cfa: sp 44 + .ra: .cfa -8 + ^ r4: .cfa -16 + ^
+  */
+  uint16_t input[] = {// UNW_INDEX size
+                      0x2A,
+
+                      // UNW_INDEX address column (4 byte rows).
+                      0x0, 0x1000, 0x0, 0x1502, 0x0, 0x2000, 0x0, 0x2024, 0x0,
+                      0x2126, 0x0, 0x2200, 0x0, 0x2212, 0x0,
+
+                      // UNW_INDEX index column (2 byte rows).
+                      0x0, 0xffff, 0xb, 0x10, 0xffff, 0x15, 0xffff,
+
+                      // UNW_DATA table.
+                      0x5, 0x2, 0x111, 0x8, 0x220, 0x40, 0x330, 0x50, 0x332,
+                      0x80, 0x220, 0x2, 0x4, 0x13, 0x8, 0x13, 0x2, 0xc, 0x33,
+                      0xdc, 0x40, 0x1, 0x4, 0x2e};
+  FilePath temp_path;
+  CreateTemporaryFile(&temp_path);
+  EXPECT_EQ(
+      static_cast<int>(sizeof(input)),
+      WriteFile(temp_path, reinterpret_cast<char*>(input), sizeof(input)));
+
+  unwinder->cfi_mmap_.reset(new MemoryMappedFile());
+  unwinder->cfi_mmap_->Initialize(temp_path);
+  unwinder->ParseCFITables();
+
+  CFIBacktraceAndroid::CFIRow cfi_row = {0};
+  EXPECT_FALSE(unwinder->FindCFIRowForPC(0x01, &cfi_row));
+  EXPECT_FALSE(unwinder->FindCFIRowForPC(0x100, &cfi_row));
+  EXPECT_FALSE(unwinder->FindCFIRowForPC(0x1502, &cfi_row));
+  EXPECT_FALSE(unwinder->FindCFIRowForPC(0x3000, &cfi_row));
+  EXPECT_FALSE(unwinder->FindCFIRowForPC(0x2024, &cfi_row));
+  EXPECT_FALSE(unwinder->FindCFIRowForPC(0x2212, &cfi_row));
+
+  const CFIBacktraceAndroid::CFIRow kRow1 = {0x110, 0x4};
+  const CFIBacktraceAndroid::CFIRow kRow2 = {0x220, 0x4};
+  const CFIBacktraceAndroid::CFIRow kRow3 = {0x220, 0x8};
+  const CFIBacktraceAndroid::CFIRow kRow4 = {0x30, 0xc};
+  const CFIBacktraceAndroid::CFIRow kRow5 = {0x2c, 0x8};
+  EXPECT_TRUE(unwinder->FindCFIRowForPC(0x1002, &cfi_row));
+  EXPECT_EQ(kRow1, cfi_row);
+  EXPECT_TRUE(unwinder->FindCFIRowForPC(0x1003, &cfi_row));
+  EXPECT_EQ(kRow1, cfi_row);
+  EXPECT_TRUE(unwinder->FindCFIRowForPC(0x1008, &cfi_row));
+  EXPECT_EQ(kRow2, cfi_row);
+  EXPECT_TRUE(unwinder->FindCFIRowForPC(0x1009, &cfi_row));
+  EXPECT_EQ(kRow2, cfi_row);
+  EXPECT_TRUE(unwinder->FindCFIRowForPC(0x1039, &cfi_row));
+  EXPECT_EQ(kRow2, cfi_row);
+  EXPECT_TRUE(unwinder->FindCFIRowForPC(0x1080, &cfi_row));
+  EXPECT_EQ(kRow3, cfi_row);
+  EXPECT_TRUE(unwinder->FindCFIRowForPC(0x1100, &cfi_row));
+  EXPECT_EQ(kRow3, cfi_row);
+  EXPECT_TRUE(unwinder->FindCFIRowForPC(0x2050, &cfi_row));
+  EXPECT_EQ(kRow4, cfi_row);
+  EXPECT_TRUE(unwinder->FindCFIRowForPC(0x2208, &cfi_row));
+  EXPECT_EQ(kRow5, cfi_row);
+  EXPECT_TRUE(unwinder->FindCFIRowForPC(0x2210, &cfi_row));
+  EXPECT_EQ(kRow5, cfi_row);
+
+  // Test if cache is used on the future calls to Find, all addresses should
+  // have different hash. Resetting the memory map to make sure it is never
+  // accessed in Find().
+  unwinder->cfi_mmap_.reset(new MemoryMappedFile());
+  EXPECT_TRUE(unwinder->FindCFIRowForPC(0x1002, &cfi_row));
+  EXPECT_EQ(kRow1, cfi_row);
+  EXPECT_TRUE(unwinder->FindCFIRowForPC(0x1003, &cfi_row));
+  EXPECT_EQ(kRow1, cfi_row);
+  EXPECT_TRUE(unwinder->FindCFIRowForPC(0x1008, &cfi_row));
+  EXPECT_EQ(kRow2, cfi_row);
+  EXPECT_TRUE(unwinder->FindCFIRowForPC(0x1009, &cfi_row));
+  EXPECT_EQ(kRow2, cfi_row);
+  EXPECT_TRUE(unwinder->FindCFIRowForPC(0x1039, &cfi_row));
+  EXPECT_EQ(kRow2, cfi_row);
+  EXPECT_TRUE(unwinder->FindCFIRowForPC(0x1080, &cfi_row));
+  EXPECT_EQ(kRow3, cfi_row);
+  EXPECT_TRUE(unwinder->FindCFIRowForPC(0x1100, &cfi_row));
+  EXPECT_EQ(kRow3, cfi_row);
+  EXPECT_TRUE(unwinder->FindCFIRowForPC(0x2050, &cfi_row));
+  EXPECT_EQ(kRow4, cfi_row);
+  EXPECT_TRUE(unwinder->FindCFIRowForPC(0x2208, &cfi_row));
+  EXPECT_EQ(kRow5, cfi_row);
+  EXPECT_TRUE(unwinder->FindCFIRowForPC(0x2210, &cfi_row));
+  EXPECT_EQ(kRow5, cfi_row);
+}
+
+TEST(CFIBacktraceAndroidTest, TestCFICache) {
+  // Use ASSERT macros in this function since they are in loop and using EXPECT
+  // prints too many failures.
+  CFIBacktraceAndroid::CFICache cache;
+  CFIBacktraceAndroid::CFIRow cfi;
+
+  // Empty cache should not find anything.
+  EXPECT_FALSE(cache.Find(1, &cfi));
+
+  // Insert 1 - 2*kLimit
+  for (size_t i = 1; i <= 2 * cache.kLimit; ++i) {
+    CFIBacktraceAndroid::CFIRow val = {4 * i, 2 * i};
+    cache.Add(i, val);
+    ASSERT_TRUE(cache.Find(i, &cfi));
+    ASSERT_EQ(cfi, val);
+
+    // Inserting more than kLimit items evicts |i - cache.kLimit| from cache.
+    if (i >= cache.kLimit)
+      ASSERT_FALSE(cache.Find(i - cache.kLimit, &cfi));
+  }
+  // Cache contains kLimit+1 - 2*kLimit.
+
+  // Check that 1 - kLimit cannot be found.
+  for (size_t i = 1; i <= cache.kLimit; ++i) {
+    ASSERT_FALSE(cache.Find(i, &cfi));
+  }
+
+  // Check if kLimit+1 - 2*kLimit still exists in cache.
+  for (size_t i = cache.kLimit + 1; i <= 2 * cache.kLimit; ++i) {
+    CFIBacktraceAndroid::CFIRow val = {4 * i, 2 * i};
+    ASSERT_TRUE(cache.Find(i, &cfi));
+    ASSERT_EQ(cfi, val);
+  }
+
+  // Insert 2*kLimit+1, will evict kLimit.
+  cfi = {1, 1};
+  cache.Add(2 * cache.kLimit + 1, cfi);
+  EXPECT_TRUE(cache.Find(2 * cache.kLimit + 1, &cfi));
+  EXPECT_FALSE(cache.Find(cache.kLimit + 1, &cfi));
+  // Cache contains kLimit+1 - 2*kLimit.
+}
+
+}  // namespace trace_event
+}  // namespace base
diff --git a/base/trace_event/event_name_filter.cc b/base/trace_event/event_name_filter.cc
new file mode 100644
index 0000000..7bf932e
--- /dev/null
+++ b/base/trace_event/event_name_filter.cc
@@ -0,0 +1,26 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/trace_event/event_name_filter.h"
+
+#include "base/trace_event/trace_event_impl.h"
+
+namespace base {
+namespace trace_event {
+
+// static
+const char EventNameFilter::kName[] = "event_whitelist_predicate";
+
+EventNameFilter::EventNameFilter(
+    std::unique_ptr<EventNamesWhitelist> event_names_whitelist)
+    : event_names_whitelist_(std::move(event_names_whitelist)) {}
+
+EventNameFilter::~EventNameFilter() = default;
+
+bool EventNameFilter::FilterTraceEvent(const TraceEvent& trace_event) const {
+  return event_names_whitelist_->count(trace_event.name()) != 0;
+}
+
+}  // namespace trace_event
+}  // namespace base
diff --git a/base/trace_event/event_name_filter.h b/base/trace_event/event_name_filter.h
new file mode 100644
index 0000000..19333b3
--- /dev/null
+++ b/base/trace_event/event_name_filter.h
@@ -0,0 +1,46 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TRACE_EVENT_EVENT_NAME_FILTER_H_
+#define BASE_TRACE_EVENT_EVENT_NAME_FILTER_H_
+
+#include <memory>
+#include <string>
+#include <unordered_set>
+
+#include "base/base_export.h"
+#include "base/macros.h"
+#include "base/trace_event/trace_event_filter.h"
+
+namespace base {
+namespace trace_event {
+
+class TraceEvent;
+
+// Filters trace events by checking the full name against a whitelist.
+// The current implementation is quite simple and dumb and just uses a
+// hashtable which requires char* to std::string conversion. It could be smarter
+// and use a bloom filter trie. However, today this is used too rarely to
+// justify that cost.
+class BASE_EXPORT EventNameFilter : public TraceEventFilter {
+ public:
+  using EventNamesWhitelist = std::unordered_set<std::string>;
+  static const char kName[];
+
+  EventNameFilter(std::unique_ptr<EventNamesWhitelist>);
+  ~EventNameFilter() override;
+
+  // TraceEventFilter implementation.
+  bool FilterTraceEvent(const TraceEvent&) const override;
+
+ private:
+  std::unique_ptr<const EventNamesWhitelist> event_names_whitelist_;
+
+  DISALLOW_COPY_AND_ASSIGN(EventNameFilter);
+};
+
+}  // namespace trace_event
+}  // namespace base
+
+#endif  // BASE_TRACE_EVENT_EVENT_NAME_FILTER_H_
diff --git a/base/trace_event/event_name_filter_unittest.cc b/base/trace_event/event_name_filter_unittest.cc
new file mode 100644
index 0000000..134be0d
--- /dev/null
+++ b/base/trace_event/event_name_filter_unittest.cc
@@ -0,0 +1,42 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/trace_event/event_name_filter.h"
+
+#include "base/memory/ptr_util.h"
+#include "base/trace_event/trace_event_impl.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace trace_event {
+
+const TraceEvent& MakeTraceEvent(const char* name) {
+  static TraceEvent event;
+  event.Reset();
+  event.Initialize(0, TimeTicks(), ThreadTicks(), 'b', nullptr, name, "", 0, 0,
+                   0, nullptr, nullptr, nullptr, nullptr, 0);
+  return event;
+}
+
+TEST(TraceEventNameFilterTest, Whitelist) {
+  auto empty_whitelist =
+      std::make_unique<EventNameFilter::EventNamesWhitelist>();
+  auto filter = std::make_unique<EventNameFilter>(std::move(empty_whitelist));
+
+  // No events should be filtered if the whitelist is empty.
+  EXPECT_FALSE(filter->FilterTraceEvent(MakeTraceEvent("foo")));
+
+  auto whitelist = std::make_unique<EventNameFilter::EventNamesWhitelist>();
+  whitelist->insert("foo");
+  whitelist->insert("bar");
+  filter = std::make_unique<EventNameFilter>(std::move(whitelist));
+  EXPECT_TRUE(filter->FilterTraceEvent(MakeTraceEvent("foo")));
+  EXPECT_FALSE(filter->FilterTraceEvent(MakeTraceEvent("fooz")));
+  EXPECT_FALSE(filter->FilterTraceEvent(MakeTraceEvent("afoo")));
+  EXPECT_TRUE(filter->FilterTraceEvent(MakeTraceEvent("bar")));
+  EXPECT_FALSE(filter->FilterTraceEvent(MakeTraceEvent("foobar")));
+}
+
+}  // namespace trace_event
+}  // namespace base
diff --git a/base/trace_event/heap_profiler_allocation_context.cc b/base/trace_event/heap_profiler_allocation_context.cc
new file mode 100644
index 0000000..bdc3c58
--- /dev/null
+++ b/base/trace_event/heap_profiler_allocation_context.cc
@@ -0,0 +1,88 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/trace_event/heap_profiler_allocation_context.h"
+
+#include <cstring>
+
+#include "base/hash.h"
+#include "base/macros.h"
+
+namespace base {
+namespace trace_event {
+
+bool operator < (const StackFrame& lhs, const StackFrame& rhs) {
+  return lhs.value < rhs.value;
+}
+
+bool operator == (const StackFrame& lhs, const StackFrame& rhs) {
+  return lhs.value == rhs.value;
+}
+
+bool operator != (const StackFrame& lhs, const StackFrame& rhs) {
+  return !(lhs.value == rhs.value);
+}
+
+Backtrace::Backtrace() = default;
+
+bool operator==(const Backtrace& lhs, const Backtrace& rhs) {
+  if (lhs.frame_count != rhs.frame_count) return false;
+  return std::equal(lhs.frames, lhs.frames + lhs.frame_count, rhs.frames);
+}
+
+bool operator!=(const Backtrace& lhs, const Backtrace& rhs) {
+  return !(lhs == rhs);
+}
+
+AllocationContext::AllocationContext(): type_name(nullptr) {}
+
+AllocationContext::AllocationContext(const Backtrace& backtrace,
+                                     const char* type_name)
+  : backtrace(backtrace), type_name(type_name) {}
+
+bool operator==(const AllocationContext& lhs, const AllocationContext& rhs) {
+  return (lhs.backtrace == rhs.backtrace) && (lhs.type_name == rhs.type_name);
+}
+
+bool operator!=(const AllocationContext& lhs, const AllocationContext& rhs) {
+  return !(lhs == rhs);
+}
+
+}  // namespace trace_event
+}  // namespace base
+
+namespace std {
+
+using base::trace_event::AllocationContext;
+using base::trace_event::Backtrace;
+using base::trace_event::StackFrame;
+
+size_t hash<StackFrame>::operator()(const StackFrame& frame) const {
+  return hash<const void*>()(frame.value);
+}
+
+size_t hash<Backtrace>::operator()(const Backtrace& backtrace) const {
+  const void* values[Backtrace::kMaxFrameCount];
+  for (size_t i = 0; i != backtrace.frame_count; ++i) {
+    values[i] = backtrace.frames[i].value;
+  }
+  return base::PersistentHash(values, backtrace.frame_count * sizeof(*values));
+}
+
+size_t hash<AllocationContext>::operator()(const AllocationContext& ctx) const {
+  size_t backtrace_hash = hash<Backtrace>()(ctx.backtrace);
+
+  // Multiplicative hash from [Knuth 1998]. Works best if |size_t| is 32 bits,
+  // because the magic number is a prime very close to 2^32 / golden ratio, but
+  // will still redistribute keys bijectively on 64-bit architectures because
+  // the magic number is coprime to 2^64.
+  size_t type_hash = reinterpret_cast<size_t>(ctx.type_name) * 2654435761;
+
+  // Multiply one side to break the commutativity of +. Multiplication with a
+  // number coprime to |numeric_limits<size_t>::max() + 1| is bijective so
+  // randomness is preserved.
+  return (backtrace_hash * 3) + type_hash;
+}
+
+}  // namespace std
diff --git a/base/trace_event/heap_profiler_allocation_context.h b/base/trace_event/heap_profiler_allocation_context.h
new file mode 100644
index 0000000..c35663f
--- /dev/null
+++ b/base/trace_event/heap_profiler_allocation_context.h
@@ -0,0 +1,132 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TRACE_EVENT_HEAP_PROFILER_ALLOCATION_CONTEXT_H_
+#define BASE_TRACE_EVENT_HEAP_PROFILER_ALLOCATION_CONTEXT_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <functional>
+
+#include "base/base_export.h"
+
+namespace base {
+namespace trace_event {
+
+// When heap profiling is enabled, tracing keeps track of the allocation
+// context for each allocation intercepted. It is generated by the
+// |AllocationContextTracker| which keeps stacks of context in TLS.
+// The tracker is initialized lazily.
+
+// The backtrace in the allocation context is a snapshot of the stack. For now,
+// this is the pseudo stack where frames are created by trace event macros. In
+// the future, we might add the option to use the native call stack. In that
+// case, |Backtrace| and |AllocationContextTracker::GetContextSnapshot| might
+// have different implementations that can be selected by a compile time flag.
+
+// The number of stack frames stored in the backtrace is a trade off between
+// memory used for tracing and accuracy. Measurements done on a prototype
+// revealed that:
+//
+// - In 60 percent of the cases, pseudo stack depth <= 7.
+// - In 87 percent of the cases, pseudo stack depth <= 9.
+// - In 95 percent of the cases, pseudo stack depth <= 11.
+//
+// See the design doc (https://goo.gl/4s7v7b) for more details.
+
+// Represents (pseudo) stack frame. Used in Backtrace class below.
+//
+// Conceptually stack frame is identified by its value, and type is used
+// mostly to properly format the value. Value is expected to be a valid
+// pointer from process' address space.
+struct BASE_EXPORT StackFrame {
+  enum class Type {
+    TRACE_EVENT_NAME,   // const char* string
+    THREAD_NAME,        // const char* thread name
+    PROGRAM_COUNTER,    // as returned by stack tracing (e.g. by StackTrace)
+  };
+
+  static StackFrame FromTraceEventName(const char* name) {
+    return {Type::TRACE_EVENT_NAME, name};
+  }
+  static StackFrame FromThreadName(const char* name) {
+    return {Type::THREAD_NAME, name};
+  }
+  static StackFrame FromProgramCounter(const void* pc) {
+    return {Type::PROGRAM_COUNTER, pc};
+  }
+
+  Type type;
+  const void* value;
+};
+
+bool BASE_EXPORT operator < (const StackFrame& lhs, const StackFrame& rhs);
+bool BASE_EXPORT operator == (const StackFrame& lhs, const StackFrame& rhs);
+bool BASE_EXPORT operator != (const StackFrame& lhs, const StackFrame& rhs);
+
+struct BASE_EXPORT Backtrace {
+  Backtrace();
+
+  // If the stack is higher than what can be stored here, the top frames
+  // (the ones further from main()) are stored. Depth of 12 is enough for most
+  // pseudo traces (see above), but not for native traces, where we need more.
+  enum { kMaxFrameCount = 48 };
+  StackFrame frames[kMaxFrameCount];
+  size_t frame_count = 0;
+};
+
+bool BASE_EXPORT operator==(const Backtrace& lhs, const Backtrace& rhs);
+bool BASE_EXPORT operator!=(const Backtrace& lhs, const Backtrace& rhs);
+
+// The |AllocationContext| is context metadata that is kept for every allocation
+// when heap profiling is enabled. To simplify memory management for book-
+// keeping, this struct has a fixed size.
+struct BASE_EXPORT AllocationContext {
+  AllocationContext();
+  AllocationContext(const Backtrace& backtrace, const char* type_name);
+
+  Backtrace backtrace;
+
+  // Type name of the type stored in the allocated memory. A null pointer
+  // indicates "unknown type". Grouping is done by comparing pointers, not by
+  // deep string comparison. In a component build, where a type name can have a
+  // string literal in several dynamic libraries, this may distort grouping.
+  const char* type_name;
+};
+
+bool BASE_EXPORT operator==(const AllocationContext& lhs,
+                            const AllocationContext& rhs);
+bool BASE_EXPORT operator!=(const AllocationContext& lhs,
+                            const AllocationContext& rhs);
+
+// Struct to store the size and count of the allocations.
+struct AllocationMetrics {
+  size_t size;
+  size_t count;
+};
+
+}  // namespace trace_event
+}  // namespace base
+
+namespace std {
+
+template <>
+struct BASE_EXPORT hash<base::trace_event::StackFrame> {
+  size_t operator()(const base::trace_event::StackFrame& frame) const;
+};
+
+template <>
+struct BASE_EXPORT hash<base::trace_event::Backtrace> {
+  size_t operator()(const base::trace_event::Backtrace& backtrace) const;
+};
+
+template <>
+struct BASE_EXPORT hash<base::trace_event::AllocationContext> {
+  size_t operator()(const base::trace_event::AllocationContext& context) const;
+};
+
+}  // namespace std
+
+#endif  // BASE_TRACE_EVENT_HEAP_PROFILER_ALLOCATION_CONTEXT_H_
diff --git a/base/trace_event/heap_profiler_allocation_context_tracker.cc b/base/trace_event/heap_profiler_allocation_context_tracker.cc
new file mode 100644
index 0000000..556719e
--- /dev/null
+++ b/base/trace_event/heap_profiler_allocation_context_tracker.cc
@@ -0,0 +1,274 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/trace_event/heap_profiler_allocation_context_tracker.h"
+
+#include <algorithm>
+#include <iterator>
+
+#include "base/atomicops.h"
+#include "base/debug/debugging_buildflags.h"
+#include "base/debug/leak_annotations.h"
+#include "base/debug/stack_trace.h"
+#include "base/no_destructor.h"
+#include "base/threading/platform_thread.h"
+#include "base/threading/thread_local_storage.h"
+#include "base/trace_event/heap_profiler_allocation_context.h"
+#include "build/build_config.h"
+
+#if defined(OS_ANDROID) && BUILDFLAG(CAN_UNWIND_WITH_CFI_TABLE)
+#include "base/trace_event/cfi_backtrace_android.h"
+#endif
+
+#if defined(OS_LINUX) || defined(OS_ANDROID)
+#include <sys/prctl.h>
+#endif
+
+namespace base {
+namespace trace_event {
+
+subtle::Atomic32 AllocationContextTracker::capture_mode_ =
+    static_cast<int32_t>(AllocationContextTracker::CaptureMode::DISABLED);
+
+namespace {
+
+const size_t kMaxStackDepth = 128u;
+const size_t kMaxTaskDepth = 16u;
+AllocationContextTracker* const kInitializingSentinel =
+    reinterpret_cast<AllocationContextTracker*>(-1);
+
+// This function is added to the TLS slot to clean up the instance when the
+// thread exits.
+void DestructAllocationContextTracker(void* alloc_ctx_tracker) {
+  delete static_cast<AllocationContextTracker*>(alloc_ctx_tracker);
+}
+
+ThreadLocalStorage::Slot& AllocationContextTrackerTLS() {
+  static NoDestructor<ThreadLocalStorage::Slot> tls_alloc_ctx_tracker(
+      &DestructAllocationContextTracker);
+  return *tls_alloc_ctx_tracker;
+}
+
+// Cannot call ThreadIdNameManager::GetName because it holds a lock and causes
+// deadlock when lock is already held by ThreadIdNameManager before the current
+// allocation. Gets the thread name from kernel if available or returns a string
+// with id. This function intentionally leaks the allocated strings since they
+// are used to tag allocations even after the thread dies.
+const char* GetAndLeakThreadName() {
+  char name[16];
+#if defined(OS_LINUX) || defined(OS_ANDROID)
+  // If the thread name is not set, try to get it from prctl. Thread name might
+  // not be set in cases where the thread started before heap profiling was
+  // enabled.
+  int err = prctl(PR_GET_NAME, name);
+  if (!err) {
+    return strdup(name);
+  }
+#endif  // defined(OS_LINUX) || defined(OS_ANDROID)
+
+  // Use tid if we don't have a thread name.
+  snprintf(name, sizeof(name), "%lu",
+           static_cast<unsigned long>(PlatformThread::CurrentId()));
+  return strdup(name);
+}
+
+}  // namespace
+
+// static
+AllocationContextTracker*
+AllocationContextTracker::GetInstanceForCurrentThread() {
+  AllocationContextTracker* tracker = static_cast<AllocationContextTracker*>(
+      AllocationContextTrackerTLS().Get());
+  if (tracker == kInitializingSentinel)
+    return nullptr;  // Re-entrancy case.
+
+  if (!tracker) {
+    AllocationContextTrackerTLS().Set(kInitializingSentinel);
+    tracker = new AllocationContextTracker();
+    AllocationContextTrackerTLS().Set(tracker);
+  }
+
+  return tracker;
+}
+
+AllocationContextTracker::AllocationContextTracker()
+    : thread_name_(nullptr), ignore_scope_depth_(0) {
+  tracked_stack_.reserve(kMaxStackDepth);
+  task_contexts_.reserve(kMaxTaskDepth);
+}
+AllocationContextTracker::~AllocationContextTracker() = default;
+
+// static
+void AllocationContextTracker::SetCurrentThreadName(const char* name) {
+  if (name && capture_mode() != CaptureMode::DISABLED) {
+    GetInstanceForCurrentThread()->thread_name_ = name;
+  }
+}
+
+// static
+void AllocationContextTracker::SetCaptureMode(CaptureMode mode) {
+  // Release ordering ensures that when a thread observes |capture_mode_| to
+  // be true through an acquire load, the TLS slot has been initialized.
+  subtle::Release_Store(&capture_mode_, static_cast<int32_t>(mode));
+}
+
+void AllocationContextTracker::PushPseudoStackFrame(
+    AllocationContextTracker::PseudoStackFrame stack_frame) {
+  // Impose a limit on the height to verify that every push is popped, because
+  // in practice the pseudo stack never grows higher than ~20 frames.
+  if (tracked_stack_.size() < kMaxStackDepth) {
+    tracked_stack_.push_back(
+        StackFrame::FromTraceEventName(stack_frame.trace_event_name));
+  } else {
+    NOTREACHED();
+  }
+}
+
+void AllocationContextTracker::PopPseudoStackFrame(
+    AllocationContextTracker::PseudoStackFrame stack_frame) {
+  // Guard for stack underflow. If tracing was started with a TRACE_EVENT in
+  // scope, the frame was never pushed, so it is possible that pop is called
+  // on an empty stack.
+  if (tracked_stack_.empty())
+    return;
+
+  tracked_stack_.pop_back();
+}
+
+void AllocationContextTracker::PushNativeStackFrame(const void* pc) {
+  if (tracked_stack_.size() < kMaxStackDepth)
+    tracked_stack_.push_back(StackFrame::FromProgramCounter(pc));
+  else
+    NOTREACHED();
+}
+
+void AllocationContextTracker::PopNativeStackFrame(const void* pc) {
+  if (tracked_stack_.empty())
+    return;
+
+  DCHECK_EQ(pc, tracked_stack_.back().value);
+  tracked_stack_.pop_back();
+}
+
+void AllocationContextTracker::PushCurrentTaskContext(const char* context) {
+  DCHECK(context);
+  if (task_contexts_.size() < kMaxTaskDepth)
+    task_contexts_.push_back(context);
+  else
+    NOTREACHED();
+}
+
+void AllocationContextTracker::PopCurrentTaskContext(const char* context) {
+  // Guard for stack underflow. If tracing was started with a TRACE_EVENT in
+  // scope, the context was never pushed, so it is possible that pop is called
+  // on an empty stack.
+  if (task_contexts_.empty())
+    return;
+
+  DCHECK_EQ(context, task_contexts_.back())
+      << "Encountered an unmatched context end";
+  task_contexts_.pop_back();
+}
+
+bool AllocationContextTracker::GetContextSnapshot(AllocationContext* ctx) {
+  if (ignore_scope_depth_)
+    return false;
+
+  CaptureMode mode = static_cast<CaptureMode>(
+      subtle::NoBarrier_Load(&capture_mode_));
+
+  auto* backtrace = std::begin(ctx->backtrace.frames);
+  auto* backtrace_end = std::end(ctx->backtrace.frames);
+
+  if (!thread_name_) {
+    // Ignore the string allocation made by GetAndLeakThreadName to avoid
+    // reentrancy.
+    ignore_scope_depth_++;
+    thread_name_ = GetAndLeakThreadName();
+    ANNOTATE_LEAKING_OBJECT_PTR(thread_name_);
+    DCHECK(thread_name_);
+    ignore_scope_depth_--;
+  }
+
+  // Add the thread name as the first entry in pseudo stack.
+  if (thread_name_) {
+    *backtrace++ = StackFrame::FromThreadName(thread_name_);
+  }
+
+  switch (mode) {
+    case CaptureMode::DISABLED:
+      {
+        break;
+      }
+    case CaptureMode::PSEUDO_STACK:
+    case CaptureMode::MIXED_STACK:
+      {
+        for (const StackFrame& stack_frame : tracked_stack_) {
+          if (backtrace == backtrace_end)
+            break;
+          *backtrace++ = stack_frame;
+        }
+        break;
+      }
+    case CaptureMode::NATIVE_STACK:
+      {
+// Backtrace contract requires us to return bottom frames, i.e.
+// from main() and up. Stack unwinding produces top frames, i.e.
+// from this point and up until main(). We intentionally request
+// kMaxFrameCount + 1 frames, so that we know if there are more frames
+// than our backtrace capacity.
+#if !defined(OS_NACL)  // We don't build base/debug/stack_trace.cc for NaCl.
+#if defined(OS_ANDROID) && BUILDFLAG(CAN_UNWIND_WITH_CFI_TABLE)
+        const void* frames[Backtrace::kMaxFrameCount + 1];
+        static_assert(arraysize(frames) >= Backtrace::kMaxFrameCount,
+                      "not requesting enough frames to fill Backtrace");
+        size_t frame_count =
+            CFIBacktraceAndroid::GetInitializedInstance()->Unwind(
+                frames, arraysize(frames));
+#elif BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)
+        const void* frames[Backtrace::kMaxFrameCount + 1];
+        static_assert(arraysize(frames) >= Backtrace::kMaxFrameCount,
+                      "not requesting enough frames to fill Backtrace");
+        size_t frame_count = debug::TraceStackFramePointers(
+            frames, arraysize(frames),
+            1 /* exclude this function from the trace */);
+#else
+        // Fall-back to capturing the stack with base::debug::StackTrace,
+        // which is likely slower, but more reliable.
+        base::debug::StackTrace stack_trace(Backtrace::kMaxFrameCount + 1);
+        size_t frame_count = 0u;
+        const void* const* frames = stack_trace.Addresses(&frame_count);
+#endif
+
+        // If there are too many frames, keep the ones furthest from main().
+        size_t backtrace_capacity = backtrace_end - backtrace;
+        int32_t starting_frame_index = frame_count;
+        if (frame_count > backtrace_capacity) {
+          starting_frame_index = backtrace_capacity - 1;
+          *backtrace++ = StackFrame::FromTraceEventName("<truncated>");
+        }
+        for (int32_t i = starting_frame_index - 1; i >= 0; --i) {
+          const void* frame = frames[i];
+          *backtrace++ = StackFrame::FromProgramCounter(frame);
+        }
+#endif  // !defined(OS_NACL)
+        break;
+      }
+  }
+
+  ctx->backtrace.frame_count = backtrace - std::begin(ctx->backtrace.frames);
+
+  // TODO(ssid): Fix crbug.com/594803 to add file name as 3rd dimension
+  // (component name) in the heap profiler and not piggy back on the type name.
+  if (!task_contexts_.empty()) {
+    ctx->type_name = task_contexts_.back();
+  } else {
+    ctx->type_name = nullptr;
+  }
+
+  return true;
+}
+
+}  // namespace trace_event
+}  // namespace base
diff --git a/base/trace_event/heap_profiler_allocation_context_tracker.h b/base/trace_event/heap_profiler_allocation_context_tracker.h
new file mode 100644
index 0000000..da03b7f
--- /dev/null
+++ b/base/trace_event/heap_profiler_allocation_context_tracker.h
@@ -0,0 +1,140 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TRACE_EVENT_HEAP_PROFILER_ALLOCATION_CONTEXT_TRACKER_H_
+#define BASE_TRACE_EVENT_HEAP_PROFILER_ALLOCATION_CONTEXT_TRACKER_H_
+
+#include <vector>
+
+#include "base/atomicops.h"
+#include "base/base_export.h"
+#include "base/macros.h"
+#include "base/trace_event/heap_profiler_allocation_context.h"
+
+namespace base {
+namespace trace_event {
+
+// AllocationContextTracker is a thread-local object. Its main purpose is to
+// keep track of a pseudo stack of trace events. Chrome has been instrumented
+// with lots of `TRACE_EVENT` macros. These trace events push their name to a
+// thread-local stack when they go into scope, and pop when they go out of
+// scope, if all of the following conditions have been met:
+//
+//  * A trace is being recorded.
+//  * The category of the event is enabled in the trace config.
+//  * Heap profiling is enabled (with the `--enable-heap-profiling` flag).
+//
+// This means that allocations that occur before tracing is started will not
+// have backtrace information in their context.
+//
+// AllocationContextTracker also keeps track of some thread state not related to
+// trace events. See |AllocationContext|.
+//
+// A thread-local instance of the context tracker is initialized lazily when it
+// is first accessed. This might be because a trace event pushed or popped, or
+// because `GetContextSnapshot()` was called when an allocation occurred
+class BASE_EXPORT AllocationContextTracker {
+ public:
+  enum class CaptureMode : int32_t {
+    DISABLED,      // Don't capture anything
+    PSEUDO_STACK,  // Backtrace has trace events
+    MIXED_STACK,   // Backtrace has trace events + from
+                   // HeapProfilerScopedStackFrame
+    NATIVE_STACK,  // Backtrace has full native backtraces from stack unwinding
+  };
+
+  // Stack frame constructed from trace events in codebase.
+  struct BASE_EXPORT PseudoStackFrame {
+    const char* trace_event_category;
+    const char* trace_event_name;
+
+    bool operator==(const PseudoStackFrame& other) const {
+      return trace_event_category == other.trace_event_category &&
+             trace_event_name == other.trace_event_name;
+    }
+  };
+
+  // Globally sets capturing mode.
+  // TODO(primiano): How to guard against *_STACK -> DISABLED -> *_STACK?
+  static void SetCaptureMode(CaptureMode mode);
+
+  // Returns global capturing mode.
+  inline static CaptureMode capture_mode() {
+    // A little lag after heap profiling is enabled or disabled is fine, it is
+    // more important that the check is as cheap as possible when capturing is
+    // not enabled, so do not issue a memory barrier in the fast path.
+    if (subtle::NoBarrier_Load(&capture_mode_) ==
+            static_cast<int32_t>(CaptureMode::DISABLED))
+      return CaptureMode::DISABLED;
+
+    // In the slow path, an acquire load is required to pair with the release
+    // store in |SetCaptureMode|. This is to ensure that the TLS slot for
+    // the thread-local allocation context tracker has been initialized if
+    // |capture_mode| returns something other than DISABLED.
+    return static_cast<CaptureMode>(subtle::Acquire_Load(&capture_mode_));
+  }
+
+  // Returns the thread-local instance, creating one if necessary. Returns
+  // always a valid instance, unless it is called re-entrantly, in which case
+  // returns nullptr in the nested calls.
+  static AllocationContextTracker* GetInstanceForCurrentThread();
+
+  // Set the thread name in the AllocationContextTracker of the current thread
+  // if capture is enabled.
+  static void SetCurrentThreadName(const char* name);
+
+  // Starts and ends a new ignore scope between which the allocations are
+  // ignored by the heap profiler. GetContextSnapshot() returns false when
+  // allocations are ignored.
+  void begin_ignore_scope() { ignore_scope_depth_++; }
+  void end_ignore_scope() {
+    if (ignore_scope_depth_)
+      ignore_scope_depth_--;
+  }
+
+  // Pushes and pops a frame onto the thread-local pseudo stack.
+  // TODO(ssid): Change PseudoStackFrame to const char*. Only event name is
+  // used.
+  void PushPseudoStackFrame(PseudoStackFrame stack_frame);
+  void PopPseudoStackFrame(PseudoStackFrame stack_frame);
+
+  // Pushes and pops a native stack frame onto thread local tracked stack.
+  void PushNativeStackFrame(const void* pc);
+  void PopNativeStackFrame(const void* pc);
+
+  // Push and pop current task's context. A stack is used to support nested
+  // tasks and the top of the stack will be used in allocation context.
+  void PushCurrentTaskContext(const char* context);
+  void PopCurrentTaskContext(const char* context);
+
+  // Fills a snapshot of the current thread-local context. Doesn't fill and
+  // returns false if allocations are being ignored.
+  bool GetContextSnapshot(AllocationContext* snapshot);
+
+  ~AllocationContextTracker();
+
+ private:
+  AllocationContextTracker();
+
+  static subtle::Atomic32 capture_mode_;
+
+  // The pseudo stack where frames are |TRACE_EVENT| names or inserted PCs.
+  std::vector<StackFrame> tracked_stack_;
+
+  // The thread name is used as the first entry in the pseudo stack.
+  const char* thread_name_;
+
+  // Stack of tasks' contexts. Context serves as a different dimension than
+  // pseudo stack to cluster allocations.
+  std::vector<const char*> task_contexts_;
+
+  uint32_t ignore_scope_depth_;
+
+  DISALLOW_COPY_AND_ASSIGN(AllocationContextTracker);
+};
+
+}  // namespace trace_event
+}  // namespace base
+
+#endif  // BASE_TRACE_EVENT_HEAP_PROFILER_ALLOCATION_CONTEXT_TRACKER_H_
diff --git a/base/trace_event/heap_profiler_allocation_context_tracker_unittest.cc b/base/trace_event/heap_profiler_allocation_context_tracker_unittest.cc
new file mode 100644
index 0000000..c26149e
--- /dev/null
+++ b/base/trace_event/heap_profiler_allocation_context_tracker_unittest.cc
@@ -0,0 +1,350 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+
+#include <iterator>
+
+#include "base/memory/ref_counted.h"
+#include "base/pending_task.h"
+#include "base/trace_event/heap_profiler.h"
+#include "base/trace_event/heap_profiler_allocation_context.h"
+#include "base/trace_event/heap_profiler_allocation_context_tracker.h"
+#include "base/trace_event/memory_dump_manager.h"
+#include "base/trace_event/trace_event.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace trace_event {
+
+// Define all strings once, because the pseudo stack requires pointer equality,
+// and string interning is unreliable.
+const char kThreadName[] = "TestThread";
+const char kCupcake[] = "Cupcake";
+const char kDonut[] = "Donut";
+const char kEclair[] = "Eclair";
+const char kFroyo[] = "Froyo";
+const char kGingerbread[] = "Gingerbread";
+
+const char kFilteringTraceConfig[] =
+    "{"
+    "  \"event_filters\": ["
+    "    {"
+    "      \"excluded_categories\": [],"
+    "      \"filter_args\": {},"
+    "      \"filter_predicate\": \"heap_profiler_predicate\","
+    "      \"included_categories\": ["
+    "        \"*\","
+    "        \"" TRACE_DISABLED_BY_DEFAULT("Testing") "\"]"
+    "    }"
+    "  ]"
+    "}";
+
+// Asserts that the fixed-size array |expected_backtrace| matches the backtrace
+// in |AllocationContextTracker::GetContextSnapshot|.
+template <size_t N>
+void AssertBacktraceEquals(const StackFrame(&expected_backtrace)[N]) {
+  AllocationContext ctx;
+  ASSERT_TRUE(AllocationContextTracker::GetInstanceForCurrentThread()
+                  ->GetContextSnapshot(&ctx));
+
+  auto* actual = std::begin(ctx.backtrace.frames);
+  auto* actual_bottom = actual + ctx.backtrace.frame_count;
+  auto expected = std::begin(expected_backtrace);
+  auto expected_bottom = std::end(expected_backtrace);
+
+  // Note that this requires the pointers to be equal, this is not doing a deep
+  // string comparison.
+  for (; actual != actual_bottom && expected != expected_bottom;
+       actual++, expected++)
+    ASSERT_EQ(*expected, *actual);
+
+  // Ensure that the height of the stacks is the same.
+  ASSERT_EQ(actual, actual_bottom);
+  ASSERT_EQ(expected, expected_bottom);
+}
+
+void AssertBacktraceContainsOnlyThreadName() {
+  StackFrame t = StackFrame::FromThreadName(kThreadName);
+  AllocationContext ctx;
+  ASSERT_TRUE(AllocationContextTracker::GetInstanceForCurrentThread()
+                  ->GetContextSnapshot(&ctx));
+
+  ASSERT_EQ(1u, ctx.backtrace.frame_count);
+  ASSERT_EQ(t, ctx.backtrace.frames[0]);
+}
+
+class AllocationContextTrackerTest : public testing::Test {
+ public:
+  void SetUp() override {
+    AllocationContextTracker::SetCaptureMode(
+        AllocationContextTracker::CaptureMode::PSEUDO_STACK);
+    // Enabling memory-infra category sets default memory dump config which
+    // includes filters for capturing pseudo stack.
+    TraceConfig config(kFilteringTraceConfig);
+    TraceLog::GetInstance()->SetEnabled(config, TraceLog::FILTERING_MODE);
+    AllocationContextTracker::SetCurrentThreadName(kThreadName);
+  }
+
+  void TearDown() override {
+    AllocationContextTracker::SetCaptureMode(
+        AllocationContextTracker::CaptureMode::DISABLED);
+    TraceLog::GetInstance()->SetDisabled(TraceLog::FILTERING_MODE);
+  }
+};
+
+// Check that |TRACE_EVENT| macros push and pop to the pseudo stack correctly.
+TEST_F(AllocationContextTrackerTest, PseudoStackScopedTrace) {
+  StackFrame t = StackFrame::FromThreadName(kThreadName);
+  StackFrame c = StackFrame::FromTraceEventName(kCupcake);
+  StackFrame d = StackFrame::FromTraceEventName(kDonut);
+  StackFrame e = StackFrame::FromTraceEventName(kEclair);
+  StackFrame f = StackFrame::FromTraceEventName(kFroyo);
+
+  AssertBacktraceContainsOnlyThreadName();
+
+  {
+    TRACE_EVENT0("Testing", kCupcake);
+    StackFrame frame_c[] = {t, c};
+    AssertBacktraceEquals(frame_c);
+
+    {
+      TRACE_EVENT0("Testing", kDonut);
+      StackFrame frame_cd[] = {t, c, d};
+      AssertBacktraceEquals(frame_cd);
+    }
+
+    AssertBacktraceEquals(frame_c);
+
+    {
+      TRACE_EVENT0("Testing", kEclair);
+      StackFrame frame_ce[] = {t, c, e};
+      AssertBacktraceEquals(frame_ce);
+    }
+
+    {
+      TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("NotTesting"), kDonut);
+      TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("Testing"), kCupcake);
+      StackFrame frame_cc[] = {t, c, c};
+      AssertBacktraceEquals(frame_cc);
+    }
+
+    AssertBacktraceEquals(frame_c);
+  }
+
+  AssertBacktraceContainsOnlyThreadName();
+
+  {
+    TRACE_EVENT0("Testing", kFroyo);
+    StackFrame frame_f[] = {t, f};
+    AssertBacktraceEquals(frame_f);
+  }
+
+  AssertBacktraceContainsOnlyThreadName();
+}
+
+// Same as |PseudoStackScopedTrace|, but now test the |TRACE_EVENT_BEGIN| and
+// |TRACE_EVENT_END| macros.
+TEST_F(AllocationContextTrackerTest, PseudoStackBeginEndTrace) {
+  StackFrame t = StackFrame::FromThreadName(kThreadName);
+  StackFrame c = StackFrame::FromTraceEventName(kCupcake);
+  StackFrame d = StackFrame::FromTraceEventName(kDonut);
+  StackFrame e = StackFrame::FromTraceEventName(kEclair);
+  StackFrame f = StackFrame::FromTraceEventName(kFroyo);
+
+  StackFrame frame_c[] = {t, c};
+  StackFrame frame_cd[] = {t, c, d};
+  StackFrame frame_ce[] = {t, c, e};
+  StackFrame frame_f[] = {t, f};
+
+  AssertBacktraceContainsOnlyThreadName();
+
+  TRACE_EVENT_BEGIN0("Testing", kCupcake);
+  AssertBacktraceEquals(frame_c);
+
+  TRACE_EVENT_BEGIN0("Testing", kDonut);
+  AssertBacktraceEquals(frame_cd);
+  TRACE_EVENT_END0("Testing", kDonut);
+
+  AssertBacktraceEquals(frame_c);
+
+  TRACE_EVENT_BEGIN0("Testing", kEclair);
+  AssertBacktraceEquals(frame_ce);
+  TRACE_EVENT_END0("Testing", kEclair);
+
+  AssertBacktraceEquals(frame_c);
+  TRACE_EVENT_END0("Testing", kCupcake);
+
+  AssertBacktraceContainsOnlyThreadName();
+
+  TRACE_EVENT_BEGIN0("Testing", kFroyo);
+  AssertBacktraceEquals(frame_f);
+  TRACE_EVENT_END0("Testing", kFroyo);
+
+  AssertBacktraceContainsOnlyThreadName();
+}
+
+TEST_F(AllocationContextTrackerTest, PseudoStackMixedTrace) {
+  StackFrame t = StackFrame::FromThreadName(kThreadName);
+  StackFrame c = StackFrame::FromTraceEventName(kCupcake);
+  StackFrame d = StackFrame::FromTraceEventName(kDonut);
+  StackFrame e = StackFrame::FromTraceEventName(kEclair);
+  StackFrame f = StackFrame::FromTraceEventName(kFroyo);
+
+  StackFrame frame_c[] = {t, c};
+  StackFrame frame_cd[] = {t, c, d};
+  StackFrame frame_e[] = {t, e};
+  StackFrame frame_ef[] = {t, e, f};
+
+  AssertBacktraceContainsOnlyThreadName();
+
+  TRACE_EVENT_BEGIN0("Testing", kCupcake);
+  AssertBacktraceEquals(frame_c);
+
+  {
+    TRACE_EVENT0("Testing", kDonut);
+    AssertBacktraceEquals(frame_cd);
+  }
+
+  AssertBacktraceEquals(frame_c);
+  TRACE_EVENT_END0("Testing", kCupcake);
+  AssertBacktraceContainsOnlyThreadName();
+
+  {
+    TRACE_EVENT0("Testing", kEclair);
+    AssertBacktraceEquals(frame_e);
+
+    TRACE_EVENT_BEGIN0("Testing", kFroyo);
+    AssertBacktraceEquals(frame_ef);
+    TRACE_EVENT_END0("Testing", kFroyo);
+    AssertBacktraceEquals(frame_e);
+  }
+
+  AssertBacktraceContainsOnlyThreadName();
+}
+
+TEST_F(AllocationContextTrackerTest, MixedStackWithProgramCounter) {
+  StackFrame t = StackFrame::FromThreadName(kThreadName);
+  StackFrame c = StackFrame::FromTraceEventName(kCupcake);
+  StackFrame f = StackFrame::FromTraceEventName(kFroyo);
+  const void* pc1 = reinterpret_cast<void*>(0x1000);
+  const void* pc2 = reinterpret_cast<void*>(0x2000);
+  StackFrame n1 = StackFrame::FromProgramCounter(pc1);
+  StackFrame n2 = StackFrame::FromProgramCounter(pc2);
+
+  StackFrame frame_c[] = {t, c};
+  StackFrame frame_cd[] = {t, c, n1};
+  StackFrame frame_e[] = {t, n2, n1};
+  StackFrame frame_ef[] = {t, n2, n1, f};
+
+  AssertBacktraceContainsOnlyThreadName();
+
+  AllocationContextTracker::SetCaptureMode(
+      AllocationContextTracker::CaptureMode::MIXED_STACK);
+
+  TRACE_EVENT_BEGIN0("Testing", kCupcake);
+  AssertBacktraceEquals(frame_c);
+
+  {
+    TRACE_HEAP_PROFILER_API_SCOPED_WITH_PROGRAM_COUNTER e1(pc1);
+    AssertBacktraceEquals(frame_cd);
+  }
+
+  AssertBacktraceEquals(frame_c);
+  TRACE_EVENT_END0("Testing", kCupcake);
+  AssertBacktraceContainsOnlyThreadName();
+
+  {
+    TRACE_HEAP_PROFILER_API_SCOPED_WITH_PROGRAM_COUNTER e1(pc2);
+    TRACE_HEAP_PROFILER_API_SCOPED_WITH_PROGRAM_COUNTER e2(pc1);
+    AssertBacktraceEquals(frame_e);
+
+    TRACE_EVENT0("Testing", kFroyo);
+    AssertBacktraceEquals(frame_ef);
+  }
+
+  AssertBacktraceContainsOnlyThreadName();
+  AllocationContextTracker::SetCaptureMode(
+      AllocationContextTracker::CaptureMode::DISABLED);
+}
+
+TEST_F(AllocationContextTrackerTest, BacktraceTakesTop) {
+  StackFrame t = StackFrame::FromThreadName(kThreadName);
+  StackFrame c = StackFrame::FromTraceEventName(kCupcake);
+  StackFrame f = StackFrame::FromTraceEventName(kFroyo);
+
+  // Push 11 events onto the pseudo stack.
+  TRACE_EVENT0("Testing", kCupcake);
+  TRACE_EVENT0("Testing", kCupcake);
+  TRACE_EVENT0("Testing", kCupcake);
+
+  TRACE_EVENT0("Testing", kCupcake);
+  TRACE_EVENT0("Testing", kCupcake);
+  TRACE_EVENT0("Testing", kCupcake);
+  TRACE_EVENT0("Testing", kCupcake);
+
+  TRACE_EVENT0("Testing", kCupcake);
+  TRACE_EVENT0("Testing", kDonut);
+  TRACE_EVENT0("Testing", kEclair);
+  TRACE_EVENT0("Testing", kFroyo);
+
+  {
+    TRACE_EVENT0("Testing", kGingerbread);
+    AllocationContext ctx;
+    ASSERT_TRUE(AllocationContextTracker::GetInstanceForCurrentThread()
+                    ->GetContextSnapshot(&ctx));
+
+    // The pseudo stack relies on pointer equality, not deep string comparisons.
+    ASSERT_EQ(t, ctx.backtrace.frames[0]);
+    ASSERT_EQ(c, ctx.backtrace.frames[1]);
+    ASSERT_EQ(f, ctx.backtrace.frames[11]);
+  }
+
+  {
+    AllocationContext ctx;
+    ASSERT_TRUE(AllocationContextTracker::GetInstanceForCurrentThread()
+                    ->GetContextSnapshot(&ctx));
+    ASSERT_EQ(t, ctx.backtrace.frames[0]);
+    ASSERT_EQ(c, ctx.backtrace.frames[1]);
+    ASSERT_EQ(f, ctx.backtrace.frames[11]);
+  }
+}
+
+TEST_F(AllocationContextTrackerTest, TrackCategoryName) {
+  const char kContext1[] = "context1";
+  const char kContext2[] = "context2";
+  {
+    // The context from the scoped task event should be used as type name.
+    TRACE_HEAP_PROFILER_API_SCOPED_TASK_EXECUTION event1(kContext1);
+    AllocationContext ctx1;
+    ASSERT_TRUE(AllocationContextTracker::GetInstanceForCurrentThread()
+                    ->GetContextSnapshot(&ctx1));
+    ASSERT_EQ(kContext1, ctx1.type_name);
+
+    // In case of nested events, the last event's context should be used.
+    TRACE_HEAP_PROFILER_API_SCOPED_TASK_EXECUTION event2(kContext2);
+    AllocationContext ctx2;
+    ASSERT_TRUE(AllocationContextTracker::GetInstanceForCurrentThread()
+                    ->GetContextSnapshot(&ctx2));
+    ASSERT_EQ(kContext2, ctx2.type_name);
+  }
+
+  // Type should be nullptr without task event.
+  AllocationContext ctx;
+  ASSERT_TRUE(AllocationContextTracker::GetInstanceForCurrentThread()
+                  ->GetContextSnapshot(&ctx));
+  ASSERT_FALSE(ctx.type_name);
+}
+
+TEST_F(AllocationContextTrackerTest, IgnoreAllocationTest) {
+  TRACE_EVENT0("Testing", kCupcake);
+  TRACE_EVENT0("Testing", kDonut);
+  HEAP_PROFILER_SCOPED_IGNORE;
+  AllocationContext ctx;
+  ASSERT_FALSE(AllocationContextTracker::GetInstanceForCurrentThread()
+                   ->GetContextSnapshot(&ctx));
+}
+
+}  // namespace trace_event
+}  // namespace base
diff --git a/base/trace_event/heap_profiler_event_filter.cc b/base/trace_event/heap_profiler_event_filter.cc
new file mode 100644
index 0000000..937072c
--- /dev/null
+++ b/base/trace_event/heap_profiler_event_filter.cc
@@ -0,0 +1,70 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/trace_event/heap_profiler_event_filter.h"
+
+#include "base/trace_event/category_registry.h"
+#include "base/trace_event/heap_profiler_allocation_context_tracker.h"
+#include "base/trace_event/trace_category.h"
+#include "base/trace_event/trace_event.h"
+#include "base/trace_event/trace_event_impl.h"
+
+namespace base {
+namespace trace_event {
+
+namespace {
+
+inline bool IsPseudoStackEnabled() {
+  // Only PSEUDO_STACK and MIXED_STACK modes require trace events.
+  return AllocationContextTracker::capture_mode() ==
+             AllocationContextTracker::CaptureMode::PSEUDO_STACK ||
+         AllocationContextTracker::capture_mode() ==
+             AllocationContextTracker::CaptureMode::MIXED_STACK;
+}
+
+inline AllocationContextTracker* GetThreadLocalTracker() {
+  return AllocationContextTracker::GetInstanceForCurrentThread();
+}
+
+}  // namespace
+
+// static
+const char HeapProfilerEventFilter::kName[] = "heap_profiler_predicate";
+
+HeapProfilerEventFilter::HeapProfilerEventFilter() = default;
+HeapProfilerEventFilter::~HeapProfilerEventFilter() = default;
+
+bool HeapProfilerEventFilter::FilterTraceEvent(
+    const TraceEvent& trace_event) const {
+  if (!IsPseudoStackEnabled())
+    return true;
+
+  // TODO(primiano): Add support for events with copied name crbug.com/581079.
+  if (trace_event.flags() & TRACE_EVENT_FLAG_COPY)
+    return true;
+
+  const auto* category = CategoryRegistry::GetCategoryByStatePtr(
+      trace_event.category_group_enabled());
+  AllocationContextTracker::PseudoStackFrame frame = {category->name(),
+                                                      trace_event.name()};
+  if (trace_event.phase() == TRACE_EVENT_PHASE_BEGIN ||
+      trace_event.phase() == TRACE_EVENT_PHASE_COMPLETE) {
+    GetThreadLocalTracker()->PushPseudoStackFrame(frame);
+  } else if (trace_event.phase() == TRACE_EVENT_PHASE_END) {
+    // The pop for |TRACE_EVENT_PHASE_COMPLETE| events is in |EndEvent|.
+    GetThreadLocalTracker()->PopPseudoStackFrame(frame);
+  }
+  // Do not filter-out any events and always return true. TraceLog adds the
+  // event only if it is enabled for recording.
+  return true;
+}
+
+void HeapProfilerEventFilter::EndEvent(const char* category_name,
+                                       const char* event_name) const {
+  if (IsPseudoStackEnabled())
+    GetThreadLocalTracker()->PopPseudoStackFrame({category_name, event_name});
+}
+
+}  // namespace trace_event
+}  // namespace base
diff --git a/base/trace_event/heap_profiler_event_filter.h b/base/trace_event/heap_profiler_event_filter.h
new file mode 100644
index 0000000..47368a1
--- /dev/null
+++ b/base/trace_event/heap_profiler_event_filter.h
@@ -0,0 +1,40 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TRACE_EVENT_HEAP_PROFILER_EVENT_FILTER_H_
+#define BASE_TRACE_EVENT_HEAP_PROFILER_EVENT_FILTER_H_
+
+#include "base/base_export.h"
+#include "base/macros.h"
+#include "base/trace_event/trace_event_filter.h"
+
+namespace base {
+namespace trace_event {
+
+class TraceEvent;
+
+// This filter unconditionally accepts all events and pushes/pops them from the
+// thread-local AllocationContextTracker instance as they are seen.
+// This is used to cheaply construct the heap profiler pseudo stack without
+// having to actually record all events.
+class BASE_EXPORT HeapProfilerEventFilter : public TraceEventFilter {
+ public:
+  static const char kName[];
+
+  HeapProfilerEventFilter();
+  ~HeapProfilerEventFilter() override;
+
+  // TraceEventFilter implementation.
+  bool FilterTraceEvent(const TraceEvent& trace_event) const override;
+  void EndEvent(const char* category_name,
+                const char* event_name) const override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(HeapProfilerEventFilter);
+};
+
+}  // namespace trace_event
+}  // namespace base
+
+#endif  // BASE_TRACE_EVENT_HEAP_PROFILER_EVENT_FILTER_H_
diff --git a/base/trace_event/java_heap_dump_provider_android.cc b/base/trace_event/java_heap_dump_provider_android.cc
new file mode 100644
index 0000000..684f730
--- /dev/null
+++ b/base/trace_event/java_heap_dump_provider_android.cc
@@ -0,0 +1,47 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/trace_event/java_heap_dump_provider_android.h"
+
+#include "base/android/java_runtime.h"
+#include "base/trace_event/process_memory_dump.h"
+
+namespace base {
+namespace trace_event {
+
+// static
+JavaHeapDumpProvider* JavaHeapDumpProvider::GetInstance() {
+  return Singleton<JavaHeapDumpProvider,
+                   LeakySingletonTraits<JavaHeapDumpProvider>>::get();
+}
+
+JavaHeapDumpProvider::JavaHeapDumpProvider() {
+}
+
+JavaHeapDumpProvider::~JavaHeapDumpProvider() {
+}
+
+// Called at trace dump point time. Creates a snapshot with the memory counters
+// for the current process.
+bool JavaHeapDumpProvider::OnMemoryDump(const MemoryDumpArgs& args,
+                                        ProcessMemoryDump* pmd) {
+  // These numbers come from java.lang.Runtime stats.
+  long total_heap_size = 0;
+  long free_heap_size = 0;
+  android::JavaRuntime::GetMemoryUsage(&total_heap_size, &free_heap_size);
+
+  MemoryAllocatorDump* outer_dump = pmd->CreateAllocatorDump("java_heap");
+  outer_dump->AddScalar(MemoryAllocatorDump::kNameSize,
+                        MemoryAllocatorDump::kUnitsBytes, total_heap_size);
+
+  MemoryAllocatorDump* inner_dump =
+      pmd->CreateAllocatorDump("java_heap/allocated_objects");
+  inner_dump->AddScalar(MemoryAllocatorDump::kNameSize,
+                        MemoryAllocatorDump::kUnitsBytes,
+                        total_heap_size - free_heap_size);
+  return true;
+}
+
+}  // namespace trace_event
+}  // namespace base
diff --git a/base/trace_event/java_heap_dump_provider_android.h b/base/trace_event/java_heap_dump_provider_android.h
new file mode 100644
index 0000000..b9f2333
--- /dev/null
+++ b/base/trace_event/java_heap_dump_provider_android.h
@@ -0,0 +1,36 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TRACE_EVENT_JAVA_HEAP_DUMP_PROVIDER_ANDROID_H_
+#define BASE_TRACE_EVENT_JAVA_HEAP_DUMP_PROVIDER_ANDROID_H_
+
+#include "base/macros.h"
+#include "base/memory/singleton.h"
+#include "base/trace_event/memory_dump_provider.h"
+
+namespace base {
+namespace trace_event {
+
+// Dump provider which collects process-wide memory stats.
+class BASE_EXPORT JavaHeapDumpProvider : public MemoryDumpProvider {
+ public:
+  static JavaHeapDumpProvider* GetInstance();
+
+  // MemoryDumpProvider implementation.
+  bool OnMemoryDump(const MemoryDumpArgs& args,
+                    ProcessMemoryDump* pmd) override;
+
+ private:
+  friend struct DefaultSingletonTraits<JavaHeapDumpProvider>;
+
+  JavaHeapDumpProvider();
+  ~JavaHeapDumpProvider() override;
+
+  DISALLOW_COPY_AND_ASSIGN(JavaHeapDumpProvider);
+};
+
+}  // namespace trace_event
+}  // namespace base
+
+#endif  // BASE_TRACE_EVENT_JAVA_HEAP_DUMP_PROVIDER_ANDROID_H_
diff --git a/base/trace_event/java_heap_dump_provider_android_unittest.cc b/base/trace_event/java_heap_dump_provider_android_unittest.cc
new file mode 100644
index 0000000..4deaf83
--- /dev/null
+++ b/base/trace_event/java_heap_dump_provider_android_unittest.cc
@@ -0,0 +1,22 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/trace_event/java_heap_dump_provider_android.h"
+
+#include "base/trace_event/process_memory_dump.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace trace_event {
+
+TEST(JavaHeapDumpProviderTest, JavaHeapDump) {
+  auto* jhdp = JavaHeapDumpProvider::GetInstance();
+  MemoryDumpArgs dump_args = {MemoryDumpLevelOfDetail::DETAILED};
+  std::unique_ptr<ProcessMemoryDump> pmd(new ProcessMemoryDump(dump_args));
+
+  jhdp->OnMemoryDump(dump_args, pmd.get());
+}
+
+}  // namespace trace_event
+}  // namespace base
diff --git a/base/trace_event/malloc_dump_provider.cc b/base/trace_event/malloc_dump_provider.cc
new file mode 100644
index 0000000..46fdb3e
--- /dev/null
+++ b/base/trace_event/malloc_dump_provider.cc
@@ -0,0 +1,189 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/trace_event/malloc_dump_provider.h"
+
+#include <stddef.h>
+
+#include <unordered_map>
+
+#include "base/allocator/allocator_extension.h"
+#include "base/allocator/buildflags.h"
+#include "base/debug/profiler.h"
+#include "base/trace_event/process_memory_dump.h"
+#include "base/trace_event/trace_event_argument.h"
+#include "build/build_config.h"
+
+#if defined(OS_MACOSX)
+#include <malloc/malloc.h>
+#else
+#include <malloc.h>
+#endif
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+
+namespace base {
+namespace trace_event {
+
+namespace {
+#if defined(OS_WIN)
+// A structure containing some information about a given heap.
+struct WinHeapInfo {
+  size_t committed_size;
+  size_t uncommitted_size;
+  size_t allocated_size;
+  size_t block_count;
+};
+
+// NOTE: crbug.com/665516
+// Unfortunately, there is no safe way to collect information from secondary
+// heaps due to limitations and racy nature of this piece of WinAPI.
+void WinHeapMemoryDumpImpl(WinHeapInfo* crt_heap_info) {
+  // Iterate through whichever heap our CRT is using.
+  HANDLE crt_heap = reinterpret_cast<HANDLE>(_get_heap_handle());
+  ::HeapLock(crt_heap);
+  PROCESS_HEAP_ENTRY heap_entry;
+  heap_entry.lpData = nullptr;
+  // Walk over all the entries in the main heap.
+  while (::HeapWalk(crt_heap, &heap_entry) != FALSE) {
+    if ((heap_entry.wFlags & PROCESS_HEAP_ENTRY_BUSY) != 0) {
+      crt_heap_info->allocated_size += heap_entry.cbData;
+      crt_heap_info->block_count++;
+    } else if ((heap_entry.wFlags & PROCESS_HEAP_REGION) != 0) {
+      crt_heap_info->committed_size += heap_entry.Region.dwCommittedSize;
+      crt_heap_info->uncommitted_size += heap_entry.Region.dwUnCommittedSize;
+    }
+  }
+  CHECK(::HeapUnlock(crt_heap) == TRUE);
+}
+#endif  // defined(OS_WIN)
+}  // namespace
+
+// static
+const char MallocDumpProvider::kAllocatedObjects[] = "malloc/allocated_objects";
+
+// static
+MallocDumpProvider* MallocDumpProvider::GetInstance() {
+  return Singleton<MallocDumpProvider,
+                   LeakySingletonTraits<MallocDumpProvider>>::get();
+}
+
+MallocDumpProvider::MallocDumpProvider() = default;
+MallocDumpProvider::~MallocDumpProvider() = default;
+
+// Called at trace dump point time. Creates a snapshot the memory counters for
+// the current process.
+bool MallocDumpProvider::OnMemoryDump(const MemoryDumpArgs& args,
+                                      ProcessMemoryDump* pmd) {
+  {
+    base::AutoLock auto_lock(emit_metrics_on_memory_dump_lock_);
+    if (!emit_metrics_on_memory_dump_)
+      return true;
+  }
+
+  size_t total_virtual_size = 0;
+  size_t resident_size = 0;
+  size_t allocated_objects_size = 0;
+  size_t allocated_objects_count = 0;
+#if defined(USE_TCMALLOC)
+  bool res =
+      allocator::GetNumericProperty("generic.heap_size", &total_virtual_size);
+  DCHECK(res);
+  res = allocator::GetNumericProperty("generic.total_physical_bytes",
+                                      &resident_size);
+  DCHECK(res);
+  res = allocator::GetNumericProperty("generic.current_allocated_bytes",
+                                      &allocated_objects_size);
+  DCHECK(res);
+#elif defined(OS_MACOSX) || defined(OS_IOS)
+  malloc_statistics_t stats = {0};
+  malloc_zone_statistics(nullptr, &stats);
+  total_virtual_size = stats.size_allocated;
+  allocated_objects_size = stats.size_in_use;
+
+  // Resident size is approximated pretty well by stats.max_size_in_use.
+  // However, on macOS, freed blocks are both resident and reusable, which is
+  // semantically equivalent to deallocated. The implementation of libmalloc
+  // will also only hold a fixed number of freed regions before actually
+  // starting to deallocate them, so stats.max_size_in_use is also not
+  // representative of the peak size. As a result, stats.max_size_in_use is
+  // typically somewhere between actually resident [non-reusable] pages, and
+  // peak size. This is not very useful, so we just use stats.size_in_use for
+  // resident_size, even though it's an underestimate and fails to account for
+  // fragmentation. See
+  // https://bugs.chromium.org/p/chromium/issues/detail?id=695263#c1.
+  resident_size = stats.size_in_use;
+#elif defined(OS_WIN)
+  // This is too expensive on Windows, crbug.com/780735.
+  if (args.level_of_detail == MemoryDumpLevelOfDetail::DETAILED) {
+    WinHeapInfo main_heap_info = {};
+    WinHeapMemoryDumpImpl(&main_heap_info);
+    total_virtual_size =
+        main_heap_info.committed_size + main_heap_info.uncommitted_size;
+    // Resident size is approximated with committed heap size. Note that it is
+    // possible to do this with better accuracy on windows by intersecting the
+    // working set with the virtual memory ranges occuipied by the heap. It's
+    // not clear that this is worth it, as it's fairly expensive to do.
+    resident_size = main_heap_info.committed_size;
+    allocated_objects_size = main_heap_info.allocated_size;
+    allocated_objects_count = main_heap_info.block_count;
+  }
+#elif defined(OS_FUCHSIA)
+// TODO(fuchsia): Port, see https://crbug.com/706592.
+#else
+  struct mallinfo info = mallinfo();
+  DCHECK_GE(info.arena + info.hblkhd, info.uordblks);
+
+  // In case of Android's jemalloc |arena| is 0 and the outer pages size is
+  // reported by |hblkhd|. In case of dlmalloc the total is given by
+  // |arena| + |hblkhd|. For more details see link: http://goo.gl/fMR8lF.
+  total_virtual_size = info.arena + info.hblkhd;
+  resident_size = info.uordblks;
+
+  // Total allocated space is given by |uordblks|.
+  allocated_objects_size = info.uordblks;
+#endif
+
+  MemoryAllocatorDump* outer_dump = pmd->CreateAllocatorDump("malloc");
+  outer_dump->AddScalar("virtual_size", MemoryAllocatorDump::kUnitsBytes,
+                        total_virtual_size);
+  outer_dump->AddScalar(MemoryAllocatorDump::kNameSize,
+                        MemoryAllocatorDump::kUnitsBytes, resident_size);
+
+  MemoryAllocatorDump* inner_dump = pmd->CreateAllocatorDump(kAllocatedObjects);
+  inner_dump->AddScalar(MemoryAllocatorDump::kNameSize,
+                        MemoryAllocatorDump::kUnitsBytes,
+                        allocated_objects_size);
+  if (allocated_objects_count != 0) {
+    inner_dump->AddScalar(MemoryAllocatorDump::kNameObjectCount,
+                          MemoryAllocatorDump::kUnitsObjects,
+                          allocated_objects_count);
+  }
+
+  if (resident_size > allocated_objects_size) {
+    // Explicitly specify why is extra memory resident. In tcmalloc it accounts
+    // for free lists and caches. In mac and ios it accounts for the
+    // fragmentation and metadata.
+    MemoryAllocatorDump* other_dump =
+        pmd->CreateAllocatorDump("malloc/metadata_fragmentation_caches");
+    other_dump->AddScalar(MemoryAllocatorDump::kNameSize,
+                          MemoryAllocatorDump::kUnitsBytes,
+                          resident_size - allocated_objects_size);
+  }
+  return true;
+}
+
+void MallocDumpProvider::EnableMetrics() {
+  base::AutoLock auto_lock(emit_metrics_on_memory_dump_lock_);
+  emit_metrics_on_memory_dump_ = true;
+}
+
+void MallocDumpProvider::DisableMetrics() {
+  base::AutoLock auto_lock(emit_metrics_on_memory_dump_lock_);
+  emit_metrics_on_memory_dump_ = false;
+}
+
+}  // namespace trace_event
+}  // namespace base
diff --git a/base/trace_event/malloc_dump_provider.h b/base/trace_event/malloc_dump_provider.h
new file mode 100644
index 0000000..e02eb9d
--- /dev/null
+++ b/base/trace_event/malloc_dump_provider.h
@@ -0,0 +1,56 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TRACE_EVENT_MALLOC_DUMP_PROVIDER_H_
+#define BASE_TRACE_EVENT_MALLOC_DUMP_PROVIDER_H_
+
+#include "base/macros.h"
+#include "base/memory/singleton.h"
+#include "base/synchronization/lock.h"
+#include "base/trace_event/memory_dump_provider.h"
+#include "build/build_config.h"
+
+#if defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_WIN) || \
+    (defined(OS_MACOSX) && !defined(OS_IOS))
+#define MALLOC_MEMORY_TRACING_SUPPORTED
+#endif
+
+namespace base {
+namespace trace_event {
+
+// Dump provider which collects process-wide memory stats.
+class BASE_EXPORT MallocDumpProvider : public MemoryDumpProvider {
+ public:
+  // Name of the allocated_objects dump. Use this to declare suballocator dumps
+  // from other dump providers.
+  static const char kAllocatedObjects[];
+
+  static MallocDumpProvider* GetInstance();
+
+  // MemoryDumpProvider implementation.
+  bool OnMemoryDump(const MemoryDumpArgs& args,
+                    ProcessMemoryDump* pmd) override;
+
+  // Used by out-of-process heap-profiling. When malloc is profiled by an
+  // external process, that process will be responsible for emitting metrics on
+  // behalf of this one. Thus, MallocDumpProvider should not do anything.
+  void EnableMetrics();
+  void DisableMetrics();
+
+ private:
+  friend struct DefaultSingletonTraits<MallocDumpProvider>;
+
+  MallocDumpProvider();
+  ~MallocDumpProvider() override;
+
+  bool emit_metrics_on_memory_dump_ = true;
+  base::Lock emit_metrics_on_memory_dump_lock_;
+
+  DISALLOW_COPY_AND_ASSIGN(MallocDumpProvider);
+};
+
+}  // namespace trace_event
+}  // namespace base
+
+#endif  // BASE_TRACE_EVENT_MALLOC_DUMP_PROVIDER_H_
diff --git a/base/trace_event/memory_allocator_dump.cc b/base/trace_event/memory_allocator_dump.cc
new file mode 100644
index 0000000..5260a73
--- /dev/null
+++ b/base/trace_event/memory_allocator_dump.cc
@@ -0,0 +1,148 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/trace_event/memory_allocator_dump.h"
+
+#include <string.h>
+
+#include "base/format_macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/trace_event/memory_dump_manager.h"
+#include "base/trace_event/memory_dump_provider.h"
+#include "base/trace_event/process_memory_dump.h"
+#include "base/trace_event/trace_event_argument.h"
+#include "base/values.h"
+
+namespace base {
+namespace trace_event {
+
+const char MemoryAllocatorDump::kNameSize[] = "size";
+const char MemoryAllocatorDump::kNameObjectCount[] = "object_count";
+const char MemoryAllocatorDump::kTypeScalar[] = "scalar";
+const char MemoryAllocatorDump::kTypeString[] = "string";
+const char MemoryAllocatorDump::kUnitsBytes[] = "bytes";
+const char MemoryAllocatorDump::kUnitsObjects[] = "objects";
+
+MemoryAllocatorDump::MemoryAllocatorDump(
+    const std::string& absolute_name,
+    MemoryDumpLevelOfDetail level_of_detail,
+    const MemoryAllocatorDumpGuid& guid)
+    : absolute_name_(absolute_name),
+      guid_(guid),
+      level_of_detail_(level_of_detail),
+      flags_(Flags::DEFAULT) {
+  // The |absolute_name| cannot be empty.
+  DCHECK(!absolute_name.empty());
+
+  // The |absolute_name| can contain slash separator, but not leading or
+  // trailing ones.
+  DCHECK(absolute_name[0] != '/' && *absolute_name.rbegin() != '/');
+}
+
+MemoryAllocatorDump::~MemoryAllocatorDump() = default;
+
+void MemoryAllocatorDump::AddScalar(const char* name,
+                                    const char* units,
+                                    uint64_t value) {
+  entries_.emplace_back(name, units, value);
+}
+
+void MemoryAllocatorDump::AddString(const char* name,
+                                    const char* units,
+                                    const std::string& value) {
+  // String attributes are disabled in background mode.
+  if (level_of_detail_ == MemoryDumpLevelOfDetail::BACKGROUND) {
+    NOTREACHED();
+    return;
+  }
+  entries_.emplace_back(name, units, value);
+}
+
+void MemoryAllocatorDump::AsValueInto(TracedValue* value) const {
+  std::string string_conversion_buffer;
+  value->BeginDictionaryWithCopiedName(absolute_name_);
+  value->SetString("guid", guid_.ToString());
+  value->BeginDictionary("attrs");
+
+  for (const Entry& entry : entries_) {
+    value->BeginDictionaryWithCopiedName(entry.name);
+    switch (entry.entry_type) {
+      case Entry::kUint64:
+        SStringPrintf(&string_conversion_buffer, "%" PRIx64,
+                      entry.value_uint64);
+        value->SetString("type", kTypeScalar);
+        value->SetString("units", entry.units);
+        value->SetString("value", string_conversion_buffer);
+        break;
+      case Entry::kString:
+        value->SetString("type", kTypeString);
+        value->SetString("units", entry.units);
+        value->SetString("value", entry.value_string);
+        break;
+    }
+    value->EndDictionary();
+  }
+  value->EndDictionary();  // "attrs": { ... }
+  if (flags_)
+    value->SetInteger("flags", flags_);
+  value->EndDictionary();  // "allocator_name/heap_subheap": { ... }
+}
+
+uint64_t MemoryAllocatorDump::GetSizeInternal() const {
+  if (cached_size_.has_value())
+    return *cached_size_;
+  for (const auto& entry : entries_) {
+    if (entry.entry_type == Entry::kUint64 && entry.units == kUnitsBytes &&
+        strcmp(entry.name.c_str(), kNameSize) == 0) {
+      cached_size_ = entry.value_uint64;
+      return entry.value_uint64;
+    }
+  }
+  return 0;
+};
+
+MemoryAllocatorDump::Entry::Entry() : entry_type(kString), value_uint64() {}
+MemoryAllocatorDump::Entry::Entry(MemoryAllocatorDump::Entry&&) noexcept =
+    default;
+MemoryAllocatorDump::Entry& MemoryAllocatorDump::Entry::operator=(
+    MemoryAllocatorDump::Entry&&) = default;
+MemoryAllocatorDump::Entry::Entry(std::string name,
+                                  std::string units,
+                                  uint64_t value)
+    : name(name), units(units), entry_type(kUint64), value_uint64(value) {}
+MemoryAllocatorDump::Entry::Entry(std::string name,
+                                  std::string units,
+                                  std::string value)
+    : name(name), units(units), entry_type(kString), value_string(value) {}
+
+bool MemoryAllocatorDump::Entry::operator==(const Entry& rhs) const {
+  if (!(name == rhs.name && units == rhs.units && entry_type == rhs.entry_type))
+    return false;
+  switch (entry_type) {
+    case EntryType::kUint64:
+      return value_uint64 == rhs.value_uint64;
+    case EntryType::kString:
+      return value_string == rhs.value_string;
+  }
+  NOTREACHED();
+  return false;
+}
+
+void PrintTo(const MemoryAllocatorDump::Entry& entry, std::ostream* out) {
+  switch (entry.entry_type) {
+    case MemoryAllocatorDump::Entry::EntryType::kUint64:
+      *out << "<Entry(\"" << entry.name << "\", \"" << entry.units << "\", "
+           << entry.value_uint64 << ")>";
+      return;
+    case MemoryAllocatorDump::Entry::EntryType::kString:
+      *out << "<Entry(\"" << entry.name << "\", \"" << entry.units << "\", \""
+           << entry.value_string << "\")>";
+      return;
+  }
+  NOTREACHED();
+}
+
+}  // namespace trace_event
+}  // namespace base
diff --git a/base/trace_event/memory_allocator_dump.h b/base/trace_event/memory_allocator_dump.h
new file mode 100644
index 0000000..de38afd
--- /dev/null
+++ b/base/trace_event/memory_allocator_dump.h
@@ -0,0 +1,153 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TRACE_EVENT_MEMORY_ALLOCATOR_DUMP_H_
+#define BASE_TRACE_EVENT_MEMORY_ALLOCATOR_DUMP_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <ostream>
+#include <string>
+
+#include "base/base_export.h"
+#include "base/gtest_prod_util.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/optional.h"
+#include "base/trace_event/memory_allocator_dump_guid.h"
+#include "base/trace_event/memory_dump_request_args.h"
+#include "base/trace_event/trace_event_argument.h"
+#include "base/unguessable_token.h"
+#include "base/values.h"
+
+namespace base {
+namespace trace_event {
+
+class ProcessMemoryDump;
+class TracedValue;
+
+// Data model for user-land memory allocator dumps.
+class BASE_EXPORT MemoryAllocatorDump {
+ public:
+  enum Flags {
+    DEFAULT = 0,
+
+    // A dump marked weak will be discarded by TraceViewer.
+    WEAK = 1 << 0,
+  };
+
+  // In the TraceViewer UI table each MemoryAllocatorDump becomes
+  // a row and each Entry generates a column (if it doesn't already
+  // exist).
+  struct BASE_EXPORT Entry {
+    enum EntryType {
+      kUint64,
+      kString,
+    };
+
+    // By design name, units and value_string are  always coming from
+    // indefinitely lived const char* strings, the only reason we copy
+    // them into a std::string is to handle Mojo (de)serialization.
+    // TODO(hjd): Investigate optimization (e.g. using StringPiece).
+    Entry();  // Only for deserialization.
+    Entry(std::string name, std::string units, uint64_t value);
+    Entry(std::string name, std::string units, std::string value);
+    Entry(Entry&& other) noexcept;
+    Entry& operator=(Entry&& other);
+    bool operator==(const Entry& rhs) const;
+
+    std::string name;
+    std::string units;
+
+    EntryType entry_type;
+
+    uint64_t value_uint64;
+    std::string value_string;
+
+    DISALLOW_COPY_AND_ASSIGN(Entry);
+  };
+
+  MemoryAllocatorDump(const std::string& absolute_name,
+                      MemoryDumpLevelOfDetail,
+                      const MemoryAllocatorDumpGuid&);
+  ~MemoryAllocatorDump();
+
+  // Standard attribute |name|s for the AddScalar and AddString() methods.
+  static const char kNameSize[];          // To represent allocated space.
+  static const char kNameObjectCount[];   // To represent number of objects.
+
+  // Standard attribute |unit|s for the AddScalar and AddString() methods.
+  static const char kUnitsBytes[];    // Unit name to represent bytes.
+  static const char kUnitsObjects[];  // Unit name to represent #objects.
+
+  // Constants used only internally and by tests.
+  static const char kTypeScalar[];  // Type name for scalar attributes.
+  static const char kTypeString[];  // Type name for string attributes.
+
+  // Setters for scalar attributes. Some examples:
+  // - "size" column (all dumps are expected to have at least this one):
+  //     AddScalar(kNameSize, kUnitsBytes, 1234);
+  // - Some extra-column reporting internal details of the subsystem:
+  //    AddScalar("number_of_freelist_entries", kUnitsObjects, 42)
+  // - Other informational column:
+  //    AddString("kitten", "name", "shadow");
+  void AddScalar(const char* name, const char* units, uint64_t value);
+  void AddString(const char* name, const char* units, const std::string& value);
+
+  // Absolute name, unique within the scope of an entire ProcessMemoryDump.
+  const std::string& absolute_name() const { return absolute_name_; }
+
+  // Called at trace generation time to populate the TracedValue.
+  void AsValueInto(TracedValue* value) const;
+
+  // Get the size for this dump.
+  // The size is the value set with AddScalar(kNameSize, kUnitsBytes, size);
+  // TODO(hjd): this should return an Optional<uint64_t>.
+  uint64_t GetSizeInternal() const;
+
+  MemoryDumpLevelOfDetail level_of_detail() const { return level_of_detail_; }
+
+  // Use enum Flags to set values.
+  void set_flags(int flags) { flags_ |= flags; }
+  void clear_flags(int flags) { flags_ &= ~flags; }
+  int flags() const { return flags_; }
+
+  // |guid| is an optional global dump identifier, unique across all processes
+  // within the scope of a global dump. It is only required when using the
+  // graph APIs (see TODO_method_name) to express retention / suballocation or
+  // cross process sharing. See crbug.com/492102 for design docs.
+  // Subsequent MemoryAllocatorDump(s) with the same |absolute_name| are
+  // expected to have the same guid.
+  const MemoryAllocatorDumpGuid& guid() const { return guid_; }
+
+  const std::vector<Entry>& entries() const { return entries_; }
+
+  // Only for mojo serialization, which can mutate the collection.
+  std::vector<Entry>* mutable_entries_for_serialization() const {
+    cached_size_.reset();  // The caller can mutate the collection.
+
+    // Mojo takes a const input argument even for move-only types that can be
+    // mutate while serializing (like this one). Hence the const_cast.
+    return const_cast<std::vector<Entry>*>(&entries_);
+  }
+
+ private:
+  const std::string absolute_name_;
+  MemoryAllocatorDumpGuid guid_;
+  MemoryDumpLevelOfDetail level_of_detail_;
+  int flags_;  // See enum Flags.
+  mutable Optional<uint64_t> cached_size_;  // Lazy, for GetSizeInternal().
+  std::vector<Entry> entries_;
+
+  DISALLOW_COPY_AND_ASSIGN(MemoryAllocatorDump);
+};
+
+// This is required by gtest to print a readable output on test failures.
+void BASE_EXPORT PrintTo(const MemoryAllocatorDump::Entry&, std::ostream*);
+
+}  // namespace trace_event
+}  // namespace base
+
+#endif  // BASE_TRACE_EVENT_MEMORY_ALLOCATOR_DUMP_H_
diff --git a/base/trace_event/memory_allocator_dump_guid.cc b/base/trace_event/memory_allocator_dump_guid.cc
new file mode 100644
index 0000000..08ac677
--- /dev/null
+++ b/base/trace_event/memory_allocator_dump_guid.cc
@@ -0,0 +1,40 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/trace_event/memory_allocator_dump_guid.h"
+
+#include "base/format_macros.h"
+#include "base/sha1.h"
+#include "base/strings/stringprintf.h"
+
+namespace base {
+namespace trace_event {
+
+namespace {
+
+uint64_t HashString(const std::string& str) {
+  uint64_t hash[(kSHA1Length + sizeof(uint64_t) - 1) / sizeof(uint64_t)] = {0};
+  SHA1HashBytes(reinterpret_cast<const unsigned char*>(str.data()), str.size(),
+                reinterpret_cast<unsigned char*>(hash));
+  return hash[0];
+}
+
+}  // namespace
+
+MemoryAllocatorDumpGuid::MemoryAllocatorDumpGuid(uint64_t guid) : guid_(guid) {}
+
+MemoryAllocatorDumpGuid::MemoryAllocatorDumpGuid()
+    : MemoryAllocatorDumpGuid(0u) {
+}
+
+MemoryAllocatorDumpGuid::MemoryAllocatorDumpGuid(const std::string& guid_str)
+    : MemoryAllocatorDumpGuid(HashString(guid_str)) {
+}
+
+std::string MemoryAllocatorDumpGuid::ToString() const {
+  return StringPrintf("%" PRIx64, guid_);
+}
+
+}  // namespace trace_event
+}  // namespace base
diff --git a/base/trace_event/memory_allocator_dump_guid.h b/base/trace_event/memory_allocator_dump_guid.h
new file mode 100644
index 0000000..2a420a2
--- /dev/null
+++ b/base/trace_event/memory_allocator_dump_guid.h
@@ -0,0 +1,55 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TRACE_EVENT_MEMORY_ALLOCATOR_DUMP_GUID_H_
+#define BASE_TRACE_EVENT_MEMORY_ALLOCATOR_DUMP_GUID_H_
+
+#include <stdint.h>
+
+#include <string>
+
+#include "base/base_export.h"
+
+namespace base {
+namespace trace_event {
+
+class BASE_EXPORT MemoryAllocatorDumpGuid {
+ public:
+  MemoryAllocatorDumpGuid();
+  explicit MemoryAllocatorDumpGuid(uint64_t guid);
+
+  // Utility ctor to hash a GUID if the caller prefers a string. The caller
+  // still has to ensure that |guid_str| is unique, per snapshot, within the
+  // global scope of all the traced processes.
+  explicit MemoryAllocatorDumpGuid(const std::string& guid_str);
+
+  uint64_t ToUint64() const { return guid_; }
+
+  // Returns a (hex-encoded) string representation of the guid.
+  std::string ToString() const;
+
+  bool empty() const { return guid_ == 0u; }
+
+  bool operator==(const MemoryAllocatorDumpGuid& other) const {
+    return guid_ == other.guid_;
+  }
+
+  bool operator!=(const MemoryAllocatorDumpGuid& other) const {
+    return !(*this == other);
+  }
+
+  bool operator<(const MemoryAllocatorDumpGuid& other) const {
+    return guid_ < other.guid_;
+  }
+
+ private:
+  uint64_t guid_;
+
+  // Deliberately copy-able.
+};
+
+}  // namespace trace_event
+}  // namespace base
+
+#endif  // BASE_TRACE_EVENT_MEMORY_ALLOCATOR_DUMP_GUID_H_
diff --git a/base/trace_event/memory_allocator_dump_unittest.cc b/base/trace_event/memory_allocator_dump_unittest.cc
new file mode 100644
index 0000000..78a545f
--- /dev/null
+++ b/base/trace_event/memory_allocator_dump_unittest.cc
@@ -0,0 +1,177 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/trace_event/memory_allocator_dump.h"
+
+#include <stdint.h>
+
+#include "base/format_macros.h"
+#include "base/strings/stringprintf.h"
+#include "base/trace_event/memory_allocator_dump_guid.h"
+#include "base/trace_event/memory_dump_provider.h"
+#include "base/trace_event/process_memory_dump.h"
+#include "base/trace_event/trace_event_argument.h"
+#include "base/values.h"
+#include "build/build_config.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::ElementsAre;
+using testing::Eq;
+using testing::ByRef;
+using testing::IsEmpty;
+using testing::Contains;
+
+namespace base {
+namespace trace_event {
+
+namespace {
+
+class FakeMemoryAllocatorDumpProvider : public MemoryDumpProvider {
+ public:
+  bool OnMemoryDump(const MemoryDumpArgs& args,
+                    ProcessMemoryDump* pmd) override {
+    MemoryAllocatorDump* root_heap =
+        pmd->CreateAllocatorDump("foobar_allocator");
+
+    root_heap->AddScalar(MemoryAllocatorDump::kNameSize,
+                         MemoryAllocatorDump::kUnitsBytes, 4096);
+    root_heap->AddScalar(MemoryAllocatorDump::kNameObjectCount,
+                         MemoryAllocatorDump::kUnitsObjects, 42);
+    root_heap->AddScalar("attr1", "units1", 1234);
+    root_heap->AddString("attr2", "units2", "string_value");
+
+    MemoryAllocatorDump* sub_heap =
+        pmd->CreateAllocatorDump("foobar_allocator/sub_heap");
+    sub_heap->AddScalar(MemoryAllocatorDump::kNameSize,
+                        MemoryAllocatorDump::kUnitsBytes, 1);
+    sub_heap->AddScalar(MemoryAllocatorDump::kNameObjectCount,
+                        MemoryAllocatorDump::kUnitsObjects, 3);
+
+    pmd->CreateAllocatorDump("foobar_allocator/sub_heap/empty");
+    // Leave the rest of sub heap deliberately uninitialized, to check that
+    // CreateAllocatorDump returns a properly zero-initialized object.
+
+    return true;
+  }
+};
+
+void CheckString(const MemoryAllocatorDump* dump,
+                 const std::string& name,
+                 const char* expected_units,
+                 const std::string& expected_value) {
+  MemoryAllocatorDump::Entry expected(name, expected_units, expected_value);
+  EXPECT_THAT(dump->entries(), Contains(Eq(ByRef(expected))));
+}
+
+void CheckScalar(const MemoryAllocatorDump* dump,
+                 const std::string& name,
+                 const char* expected_units,
+                 uint64_t expected_value) {
+  MemoryAllocatorDump::Entry expected(name, expected_units, expected_value);
+  EXPECT_THAT(dump->entries(), Contains(Eq(ByRef(expected))));
+}
+
+}  // namespace
+
+TEST(MemoryAllocatorDumpTest, GuidGeneration) {
+  std::unique_ptr<MemoryAllocatorDump> mad(new MemoryAllocatorDump(
+      "foo", MemoryDumpLevelOfDetail::FIRST, MemoryAllocatorDumpGuid(0x42u)));
+  ASSERT_EQ("42", mad->guid().ToString());
+}
+
+TEST(MemoryAllocatorDumpTest, DumpIntoProcessMemoryDump) {
+  FakeMemoryAllocatorDumpProvider fmadp;
+  MemoryDumpArgs dump_args = {MemoryDumpLevelOfDetail::DETAILED};
+  ProcessMemoryDump pmd(dump_args);
+
+  fmadp.OnMemoryDump(dump_args, &pmd);
+
+  ASSERT_EQ(3u, pmd.allocator_dumps().size());
+
+  const MemoryAllocatorDump* root_heap =
+      pmd.GetAllocatorDump("foobar_allocator");
+  ASSERT_NE(nullptr, root_heap);
+  EXPECT_EQ("foobar_allocator", root_heap->absolute_name());
+  CheckScalar(root_heap, MemoryAllocatorDump::kNameSize,
+              MemoryAllocatorDump::kUnitsBytes, 4096);
+  CheckScalar(root_heap, MemoryAllocatorDump::kNameObjectCount,
+              MemoryAllocatorDump::kUnitsObjects, 42);
+  CheckScalar(root_heap, "attr1", "units1", 1234);
+  CheckString(root_heap, "attr2", "units2", "string_value");
+
+  const MemoryAllocatorDump* sub_heap =
+      pmd.GetAllocatorDump("foobar_allocator/sub_heap");
+  ASSERT_NE(nullptr, sub_heap);
+  EXPECT_EQ("foobar_allocator/sub_heap", sub_heap->absolute_name());
+  CheckScalar(sub_heap, MemoryAllocatorDump::kNameSize,
+              MemoryAllocatorDump::kUnitsBytes, 1);
+  CheckScalar(sub_heap, MemoryAllocatorDump::kNameObjectCount,
+              MemoryAllocatorDump::kUnitsObjects, 3);
+  const MemoryAllocatorDump* empty_sub_heap =
+      pmd.GetAllocatorDump("foobar_allocator/sub_heap/empty");
+  ASSERT_NE(nullptr, empty_sub_heap);
+  EXPECT_EQ("foobar_allocator/sub_heap/empty", empty_sub_heap->absolute_name());
+
+  EXPECT_THAT(empty_sub_heap->entries(), IsEmpty());
+
+  // Check that calling serialization routines doesn't cause a crash.
+  std::unique_ptr<TracedValue> traced_value(new TracedValue);
+  pmd.SerializeAllocatorDumpsInto(traced_value.get());
+}
+
+TEST(MemoryAllocatorDumpTest, GetSize) {
+  MemoryDumpArgs dump_args = {MemoryDumpLevelOfDetail::DETAILED};
+  ProcessMemoryDump pmd(dump_args);
+  MemoryAllocatorDump* dump = pmd.CreateAllocatorDump("allocator_for_size");
+  dump->AddScalar(MemoryAllocatorDump::kNameSize,
+                  MemoryAllocatorDump::kUnitsBytes, 1);
+  dump->AddScalar("foo", MemoryAllocatorDump::kUnitsBytes, 2);
+  EXPECT_EQ(1u, dump->GetSizeInternal());
+}
+
+TEST(MemoryAllocatorDumpTest, ReadValues) {
+  MemoryDumpArgs dump_args = {MemoryDumpLevelOfDetail::DETAILED};
+  ProcessMemoryDump pmd(dump_args);
+  MemoryAllocatorDump* dump = pmd.CreateAllocatorDump("allocator_for_size");
+  dump->AddScalar("one", "byte", 1);
+  dump->AddString("one", "object", "one");
+
+  MemoryAllocatorDump::Entry expected_scalar("one", "byte", 1);
+  MemoryAllocatorDump::Entry expected_string("one", "object", "one");
+  EXPECT_THAT(dump->entries(), ElementsAre(Eq(ByRef(expected_scalar)),
+                                           Eq(ByRef(expected_string))));
+}
+
+TEST(MemoryAllocatorDumpTest, MovingAnEntry) {
+  MemoryAllocatorDump::Entry expected_entry("one", "byte", 1);
+  MemoryAllocatorDump::Entry from_entry("one", "byte", 1);
+  MemoryAllocatorDump::Entry to_entry = std::move(from_entry);
+  EXPECT_EQ(expected_entry, to_entry);
+}
+
+// DEATH tests are not supported in Android/iOS/Fuchsia.
+#if !defined(NDEBUG) && !defined(OS_ANDROID) && !defined(OS_IOS) && \
+    !defined(OS_FUCHSIA)
+TEST(MemoryAllocatorDumpTest, ForbidDuplicatesDeathTest) {
+  FakeMemoryAllocatorDumpProvider fmadp;
+  MemoryDumpArgs dump_args = {MemoryDumpLevelOfDetail::DETAILED};
+  ProcessMemoryDump pmd(dump_args);
+  pmd.CreateAllocatorDump("foo_allocator");
+  pmd.CreateAllocatorDump("bar_allocator/heap");
+  ASSERT_DEATH(pmd.CreateAllocatorDump("foo_allocator"), "");
+  ASSERT_DEATH(pmd.CreateAllocatorDump("bar_allocator/heap"), "");
+  ASSERT_DEATH(pmd.CreateAllocatorDump(""), "");
+}
+
+TEST(MemoryAllocatorDumpTest, ForbidStringsInBackgroundModeDeathTest) {
+  MemoryDumpArgs dump_args = {MemoryDumpLevelOfDetail::BACKGROUND};
+  ProcessMemoryDump pmd(dump_args);
+  MemoryAllocatorDump* dump = pmd.CreateAllocatorDump("malloc");
+  ASSERT_DEATH(dump->AddString("foo", "bar", "baz"), "");
+}
+#endif
+
+}  // namespace trace_event
+}  // namespace base
diff --git a/base/trace_event/memory_dump_manager.cc b/base/trace_event/memory_dump_manager.cc
new file mode 100644
index 0000000..d61528a
--- /dev/null
+++ b/base/trace_event/memory_dump_manager.cc
@@ -0,0 +1,545 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/trace_event/memory_dump_manager.h"
+
+#include <inttypes.h>
+#include <stdio.h>
+
+#include <algorithm>
+#include <memory>
+#include <utility>
+
+#include "base/allocator/buildflags.h"
+#include "base/base_switches.h"
+#include "base/command_line.h"
+#include "base/debug/alias.h"
+#include "base/debug/stack_trace.h"
+#include "base/debug/thread_heap_usage_tracker.h"
+#include "base/memory/ptr_util.h"
+#include "base/sequenced_task_runner.h"
+#include "base/strings/string_util.h"
+#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
+#include "base/threading/thread.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/trace_event/heap_profiler.h"
+#include "base/trace_event/heap_profiler_allocation_context_tracker.h"
+#include "base/trace_event/heap_profiler_event_filter.h"
+#include "base/trace_event/malloc_dump_provider.h"
+#include "base/trace_event/memory_dump_provider.h"
+#include "base/trace_event/memory_dump_scheduler.h"
+#include "base/trace_event/memory_infra_background_whitelist.h"
+#include "base/trace_event/process_memory_dump.h"
+#include "base/trace_event/trace_event.h"
+#include "base/trace_event/trace_event_argument.h"
+#include "build/build_config.h"
+
+#if defined(OS_ANDROID)
+#include "base/trace_event/java_heap_dump_provider_android.h"
+
+#if BUILDFLAG(CAN_UNWIND_WITH_CFI_TABLE)
+#include "base/trace_event/cfi_backtrace_android.h"
+#endif
+
+#endif  // defined(OS_ANDROID)
+
+namespace base {
+namespace trace_event {
+
+namespace {
+
+MemoryDumpManager* g_memory_dump_manager_for_testing = nullptr;
+
+// Temporary (until scheduler is moved outside of here)
+// trampoline function to match the |request_dump_function| passed to Initialize
+// to the callback expected by MemoryDumpScheduler.
+// TODO(primiano): remove this.
+void DoGlobalDumpWithoutCallback(
+    MemoryDumpManager::RequestGlobalDumpFunction global_dump_fn,
+    MemoryDumpType dump_type,
+    MemoryDumpLevelOfDetail level_of_detail) {
+  global_dump_fn.Run(dump_type, level_of_detail);
+}
+
+}  // namespace
+
+// static
+const char* const MemoryDumpManager::kTraceCategory =
+    TRACE_DISABLED_BY_DEFAULT("memory-infra");
+
+// static
+const int MemoryDumpManager::kMaxConsecutiveFailuresCount = 3;
+
+// static
+const uint64_t MemoryDumpManager::kInvalidTracingProcessId = 0;
+
+// static
+const char* const MemoryDumpManager::kSystemAllocatorPoolName =
+#if defined(MALLOC_MEMORY_TRACING_SUPPORTED)
+    MallocDumpProvider::kAllocatedObjects;
+#else
+    nullptr;
+#endif
+
+// static
+MemoryDumpManager* MemoryDumpManager::GetInstance() {
+  if (g_memory_dump_manager_for_testing)
+    return g_memory_dump_manager_for_testing;
+
+  return Singleton<MemoryDumpManager,
+                   LeakySingletonTraits<MemoryDumpManager>>::get();
+}
+
+// static
+std::unique_ptr<MemoryDumpManager>
+MemoryDumpManager::CreateInstanceForTesting() {
+  DCHECK(!g_memory_dump_manager_for_testing);
+  std::unique_ptr<MemoryDumpManager> instance(new MemoryDumpManager());
+  g_memory_dump_manager_for_testing = instance.get();
+  return instance;
+}
+
+MemoryDumpManager::MemoryDumpManager()
+    : is_coordinator_(false),
+      tracing_process_id_(kInvalidTracingProcessId),
+      dumper_registrations_ignored_for_testing_(false) {}
+
+MemoryDumpManager::~MemoryDumpManager() {
+  Thread* dump_thread = nullptr;
+  {
+    AutoLock lock(lock_);
+    if (dump_thread_) {
+      dump_thread = dump_thread_.get();
+    }
+  }
+  if (dump_thread) {
+    dump_thread->Stop();
+  }
+  AutoLock lock(lock_);
+  dump_thread_.reset();
+  g_memory_dump_manager_for_testing = nullptr;
+}
+
+void MemoryDumpManager::Initialize(
+    RequestGlobalDumpFunction request_dump_function,
+    bool is_coordinator) {
+  {
+    AutoLock lock(lock_);
+    DCHECK(!request_dump_function.is_null());
+    DCHECK(!can_request_global_dumps());
+    request_dump_function_ = request_dump_function;
+    is_coordinator_ = is_coordinator;
+  }
+
+// Enable the core dump providers.
+#if defined(MALLOC_MEMORY_TRACING_SUPPORTED)
+  RegisterDumpProvider(MallocDumpProvider::GetInstance(), "Malloc", nullptr);
+#endif
+
+#if defined(OS_ANDROID)
+  RegisterDumpProvider(JavaHeapDumpProvider::GetInstance(), "JavaHeap",
+                       nullptr);
+#endif
+
+  TRACE_EVENT_WARMUP_CATEGORY(kTraceCategory);
+}
+
+void MemoryDumpManager::RegisterDumpProvider(
+    MemoryDumpProvider* mdp,
+    const char* name,
+    scoped_refptr<SingleThreadTaskRunner> task_runner,
+    MemoryDumpProvider::Options options) {
+  options.dumps_on_single_thread_task_runner = true;
+  RegisterDumpProviderInternal(mdp, name, std::move(task_runner), options);
+}
+
+void MemoryDumpManager::RegisterDumpProvider(
+    MemoryDumpProvider* mdp,
+    const char* name,
+    scoped_refptr<SingleThreadTaskRunner> task_runner) {
+  // Set |dumps_on_single_thread_task_runner| to true because all providers
+  // without task runner are run on dump thread.
+  MemoryDumpProvider::Options options;
+  options.dumps_on_single_thread_task_runner = true;
+  RegisterDumpProviderInternal(mdp, name, std::move(task_runner), options);
+}
+
+void MemoryDumpManager::RegisterDumpProviderWithSequencedTaskRunner(
+    MemoryDumpProvider* mdp,
+    const char* name,
+    scoped_refptr<SequencedTaskRunner> task_runner,
+    MemoryDumpProvider::Options options) {
+  DCHECK(task_runner);
+  options.dumps_on_single_thread_task_runner = false;
+  RegisterDumpProviderInternal(mdp, name, std::move(task_runner), options);
+}
+
+void MemoryDumpManager::RegisterDumpProviderInternal(
+    MemoryDumpProvider* mdp,
+    const char* name,
+    scoped_refptr<SequencedTaskRunner> task_runner,
+    const MemoryDumpProvider::Options& options) {
+  if (dumper_registrations_ignored_for_testing_)
+    return;
+
+  // Only a handful of MDPs are required to compute the memory metrics. These
+  // have small enough performance overhead that it is resonable to run them
+  // in the background while the user is doing other things. Those MDPs are
+  // 'whitelisted for background mode'.
+  bool whitelisted_for_background_mode = IsMemoryDumpProviderWhitelisted(name);
+
+  scoped_refptr<MemoryDumpProviderInfo> mdpinfo =
+      new MemoryDumpProviderInfo(mdp, name, std::move(task_runner), options,
+                                 whitelisted_for_background_mode);
+
+  {
+    AutoLock lock(lock_);
+    bool already_registered = !dump_providers_.insert(mdpinfo).second;
+    // This actually happens in some tests which don't have a clean tear-down
+    // path for RenderThreadImpl::Init().
+    if (already_registered)
+      return;
+  }
+}
+
+void MemoryDumpManager::UnregisterDumpProvider(MemoryDumpProvider* mdp) {
+  UnregisterDumpProviderInternal(mdp, false /* delete_async */);
+}
+
+void MemoryDumpManager::UnregisterAndDeleteDumpProviderSoon(
+    std::unique_ptr<MemoryDumpProvider> mdp) {
+  UnregisterDumpProviderInternal(mdp.release(), true /* delete_async */);
+}
+
+void MemoryDumpManager::UnregisterDumpProviderInternal(
+    MemoryDumpProvider* mdp,
+    bool take_mdp_ownership_and_delete_async) {
+  std::unique_ptr<MemoryDumpProvider> owned_mdp;
+  if (take_mdp_ownership_and_delete_async)
+    owned_mdp.reset(mdp);
+
+  AutoLock lock(lock_);
+
+  auto mdp_iter = dump_providers_.begin();
+  for (; mdp_iter != dump_providers_.end(); ++mdp_iter) {
+    if ((*mdp_iter)->dump_provider == mdp)
+      break;
+  }
+
+  if (mdp_iter == dump_providers_.end())
+    return;  // Not registered / already unregistered.
+
+  if (take_mdp_ownership_and_delete_async) {
+    // The MDP will be deleted whenever the MDPInfo struct will, that is either:
+    // - At the end of this function, if no dump is in progress.
+    // - In ContinueAsyncProcessDump() when MDPInfo is removed from
+    //   |pending_dump_providers|.
+    DCHECK(!(*mdp_iter)->owned_dump_provider);
+    (*mdp_iter)->owned_dump_provider = std::move(owned_mdp);
+  } else {
+    // If you hit this DCHECK, your dump provider has a bug.
+    // Unregistration of a MemoryDumpProvider is safe only if:
+    // - The MDP has specified a sequenced task runner affinity AND the
+    //   unregistration happens on the same task runner. So that the MDP cannot
+    //   unregister and be in the middle of a OnMemoryDump() at the same time.
+    // - The MDP has NOT specified a task runner affinity and its ownership is
+    //   transferred via UnregisterAndDeleteDumpProviderSoon().
+    // In all the other cases, it is not possible to guarantee that the
+    // unregistration will not race with OnMemoryDump() calls.
+    DCHECK((*mdp_iter)->task_runner &&
+           (*mdp_iter)->task_runner->RunsTasksInCurrentSequence())
+        << "MemoryDumpProvider \"" << (*mdp_iter)->name << "\" attempted to "
+        << "unregister itself in a racy way. Please file a crbug.";
+  }
+
+  // The MDPInfo instance can still be referenced by the
+  // |ProcessMemoryDumpAsyncState.pending_dump_providers|. For this reason
+  // the MDPInfo is flagged as disabled. It will cause InvokeOnMemoryDump()
+  // to just skip it, without actually invoking the |mdp|, which might be
+  // destroyed by the caller soon after this method returns.
+  (*mdp_iter)->disabled = true;
+  dump_providers_.erase(mdp_iter);
+}
+
+bool MemoryDumpManager::IsDumpProviderRegisteredForTesting(
+    MemoryDumpProvider* provider) {
+  AutoLock lock(lock_);
+
+  for (const auto& info : dump_providers_) {
+    if (info->dump_provider == provider)
+      return true;
+  }
+  return false;
+}
+
+scoped_refptr<base::SequencedTaskRunner>
+MemoryDumpManager::GetOrCreateBgTaskRunnerLocked() {
+  lock_.AssertAcquired();
+
+  if (dump_thread_)
+    return dump_thread_->task_runner();
+
+  dump_thread_ = std::make_unique<Thread>("MemoryInfra");
+  bool started = dump_thread_->Start();
+  CHECK(started);
+
+  return dump_thread_->task_runner();
+}
+
+void MemoryDumpManager::CreateProcessDump(
+    const MemoryDumpRequestArgs& args,
+    const ProcessMemoryDumpCallback& callback) {
+  char guid_str[20];
+  sprintf(guid_str, "0x%" PRIx64, args.dump_guid);
+  TRACE_EVENT_NESTABLE_ASYNC_BEGIN1(kTraceCategory, "ProcessMemoryDump",
+                                    TRACE_ID_LOCAL(args.dump_guid), "dump_guid",
+                                    TRACE_STR_COPY(guid_str));
+
+  // If argument filter is enabled then only background mode dumps should be
+  // allowed. In case the trace config passed for background tracing session
+  // missed the allowed modes argument, it crashes here instead of creating
+  // unexpected dumps.
+  if (TraceLog::GetInstance()
+          ->GetCurrentTraceConfig()
+          .IsArgumentFilterEnabled()) {
+    CHECK_EQ(MemoryDumpLevelOfDetail::BACKGROUND, args.level_of_detail);
+  }
+
+  std::unique_ptr<ProcessMemoryDumpAsyncState> pmd_async_state;
+  {
+    AutoLock lock(lock_);
+
+    pmd_async_state.reset(new ProcessMemoryDumpAsyncState(
+        args, dump_providers_, callback, GetOrCreateBgTaskRunnerLocked()));
+  }
+
+  // Start the process dump. This involves task runner hops as specified by the
+  // MemoryDumpProvider(s) in RegisterDumpProvider()).
+  ContinueAsyncProcessDump(pmd_async_state.release());
+}
+
+// Invokes OnMemoryDump() on all MDPs that are next in the pending list and run
+// on the current sequenced task runner. If the next MDP does not run in current
+// sequenced task runner, then switches to that task runner and continues. All
+// OnMemoryDump() invocations are linearized. |lock_| is used in these functions
+// purely to ensure consistency w.r.t. (un)registrations of |dump_providers_|.
+void MemoryDumpManager::ContinueAsyncProcessDump(
+    ProcessMemoryDumpAsyncState* owned_pmd_async_state) {
+  HEAP_PROFILER_SCOPED_IGNORE;
+  // Initalizes the ThreadLocalEventBuffer to guarantee that the TRACE_EVENTs
+  // in the PostTask below don't end up registering their own dump providers
+  // (for discounting trace memory overhead) while holding the |lock_|.
+  TraceLog::GetInstance()->InitializeThreadLocalEventBufferIfSupported();
+
+  // In theory |owned_pmd_async_state| should be a unique_ptr. The only reason
+  // why it isn't is because of the corner case logic of |did_post_task|
+  // above, which needs to take back the ownership of the |pmd_async_state| when
+  // the PostTask() fails.
+  // Unfortunately, PostTask() destroys the unique_ptr arguments upon failure
+  // to prevent accidental leaks. Using a unique_ptr would prevent us to to
+  // skip the hop and move on. Hence the manual naked -> unique ptr juggling.
+  auto pmd_async_state = WrapUnique(owned_pmd_async_state);
+  owned_pmd_async_state = nullptr;
+
+  while (!pmd_async_state->pending_dump_providers.empty()) {
+    // Read MemoryDumpProviderInfo thread safety considerations in
+    // memory_dump_manager.h when accessing |mdpinfo| fields.
+    MemoryDumpProviderInfo* mdpinfo =
+        pmd_async_state->pending_dump_providers.back().get();
+
+    // If we are in background mode, we should invoke only the whitelisted
+    // providers. Ignore other providers and continue.
+    if (pmd_async_state->req_args.level_of_detail ==
+            MemoryDumpLevelOfDetail::BACKGROUND &&
+        !mdpinfo->whitelisted_for_background_mode) {
+      pmd_async_state->pending_dump_providers.pop_back();
+      continue;
+    }
+
+    // If the dump provider did not specify a task runner affinity, dump on
+    // |dump_thread_|.
+    scoped_refptr<SequencedTaskRunner> task_runner = mdpinfo->task_runner;
+    if (!task_runner) {
+      DCHECK(mdpinfo->options.dumps_on_single_thread_task_runner);
+      task_runner = pmd_async_state->dump_thread_task_runner;
+      DCHECK(task_runner);
+    }
+
+    // If |RunsTasksInCurrentSequence()| is true then no PostTask is
+    // required since we are on the right SequencedTaskRunner.
+    if (task_runner->RunsTasksInCurrentSequence()) {
+      InvokeOnMemoryDump(mdpinfo, pmd_async_state->process_memory_dump.get());
+      pmd_async_state->pending_dump_providers.pop_back();
+      continue;
+    }
+
+    bool did_post_task = task_runner->PostTask(
+        FROM_HERE,
+        BindOnce(&MemoryDumpManager::ContinueAsyncProcessDump, Unretained(this),
+                 Unretained(pmd_async_state.get())));
+
+    if (did_post_task) {
+      // Ownership is tranferred to the posted task.
+      ignore_result(pmd_async_state.release());
+      return;
+    }
+
+    // PostTask usually fails only if the process or thread is shut down. So,
+    // the dump provider is disabled here. But, don't disable unbound dump
+    // providers, since the |dump_thread_| is controlled by MDM.
+    if (mdpinfo->task_runner) {
+      // A locked access is required to R/W |disabled| (for the
+      // UnregisterAndDeleteDumpProviderSoon() case).
+      AutoLock lock(lock_);
+      mdpinfo->disabled = true;
+    }
+
+    // PostTask failed. Ignore the dump provider and continue.
+    pmd_async_state->pending_dump_providers.pop_back();
+  }
+
+  FinishAsyncProcessDump(std::move(pmd_async_state));
+}
+
+// This function is called on the right task runner for current MDP. It is
+// either the task runner specified by MDP or |dump_thread_task_runner| if the
+// MDP did not specify task runner. Invokes the dump provider's OnMemoryDump()
+// (unless disabled).
+void MemoryDumpManager::InvokeOnMemoryDump(MemoryDumpProviderInfo* mdpinfo,
+                                           ProcessMemoryDump* pmd) {
+  HEAP_PROFILER_SCOPED_IGNORE;
+  DCHECK(!mdpinfo->task_runner ||
+         mdpinfo->task_runner->RunsTasksInCurrentSequence());
+
+  TRACE_EVENT1(kTraceCategory, "MemoryDumpManager::InvokeOnMemoryDump",
+               "dump_provider.name", mdpinfo->name);
+
+  // Do not add any other TRACE_EVENT macro (or function that might have them)
+  // below this point. Under some rare circunstances, they can re-initialize
+  // and invalide the current ThreadLocalEventBuffer MDP, making the
+  // |should_dump| check below susceptible to TOCTTOU bugs
+  // (https://crbug.com/763365).
+
+  bool is_thread_bound;
+  {
+    // A locked access is required to R/W |disabled| (for the
+    // UnregisterAndDeleteDumpProviderSoon() case).
+    AutoLock lock(lock_);
+
+    // Unregister the dump provider if it failed too many times consecutively.
+    if (!mdpinfo->disabled &&
+        mdpinfo->consecutive_failures >= kMaxConsecutiveFailuresCount) {
+      mdpinfo->disabled = true;
+      DLOG(ERROR) << "Disabling MemoryDumpProvider \"" << mdpinfo->name
+                  << "\". Dump failed multiple times consecutively.";
+    }
+    if (mdpinfo->disabled)
+      return;
+
+    is_thread_bound = mdpinfo->task_runner != nullptr;
+  }  // AutoLock lock(lock_);
+
+  // Invoke the dump provider.
+
+  // A stack allocated string with dump provider name is useful to debug
+  // crashes while invoking dump after a |dump_provider| is not unregistered
+  // in safe way.
+  char provider_name_for_debugging[16];
+  strncpy(provider_name_for_debugging, mdpinfo->name,
+          sizeof(provider_name_for_debugging) - 1);
+  provider_name_for_debugging[sizeof(provider_name_for_debugging) - 1] = '\0';
+  base::debug::Alias(provider_name_for_debugging);
+
+  ANNOTATE_BENIGN_RACE(&mdpinfo->disabled, "best-effort race detection");
+  CHECK(!is_thread_bound ||
+        !*(static_cast<volatile bool*>(&mdpinfo->disabled)));
+  bool dump_successful =
+      mdpinfo->dump_provider->OnMemoryDump(pmd->dump_args(), pmd);
+  mdpinfo->consecutive_failures =
+      dump_successful ? 0 : mdpinfo->consecutive_failures + 1;
+}
+
+void MemoryDumpManager::FinishAsyncProcessDump(
+    std::unique_ptr<ProcessMemoryDumpAsyncState> pmd_async_state) {
+  HEAP_PROFILER_SCOPED_IGNORE;
+  DCHECK(pmd_async_state->pending_dump_providers.empty());
+  const uint64_t dump_guid = pmd_async_state->req_args.dump_guid;
+  if (!pmd_async_state->callback_task_runner->BelongsToCurrentThread()) {
+    scoped_refptr<SingleThreadTaskRunner> callback_task_runner =
+        pmd_async_state->callback_task_runner;
+    callback_task_runner->PostTask(
+        FROM_HERE, BindOnce(&MemoryDumpManager::FinishAsyncProcessDump,
+                            Unretained(this), std::move(pmd_async_state)));
+    return;
+  }
+
+  TRACE_EVENT0(kTraceCategory, "MemoryDumpManager::FinishAsyncProcessDump");
+
+  if (!pmd_async_state->callback.is_null()) {
+    pmd_async_state->callback.Run(
+        true /* success */, dump_guid,
+        std::move(pmd_async_state->process_memory_dump));
+    pmd_async_state->callback.Reset();
+  }
+
+  TRACE_EVENT_NESTABLE_ASYNC_END0(kTraceCategory, "ProcessMemoryDump",
+                                  TRACE_ID_LOCAL(dump_guid));
+}
+
+void MemoryDumpManager::SetupForTracing(
+    const TraceConfig::MemoryDumpConfig& memory_dump_config) {
+  AutoLock lock(lock_);
+
+  // At this point we must have the ability to request global dumps.
+  DCHECK(can_request_global_dumps());
+
+  MemoryDumpScheduler::Config periodic_config;
+  for (const auto& trigger : memory_dump_config.triggers) {
+    if (trigger.trigger_type == MemoryDumpType::PERIODIC_INTERVAL) {
+      if (periodic_config.triggers.empty()) {
+        periodic_config.callback =
+            BindRepeating(&DoGlobalDumpWithoutCallback, request_dump_function_,
+                          MemoryDumpType::PERIODIC_INTERVAL);
+      }
+      periodic_config.triggers.push_back(
+          {trigger.level_of_detail, trigger.min_time_between_dumps_ms});
+    }
+  }
+
+  // Only coordinator process triggers periodic memory dumps.
+  if (is_coordinator_ && !periodic_config.triggers.empty()) {
+    MemoryDumpScheduler::GetInstance()->Start(periodic_config,
+                                              GetOrCreateBgTaskRunnerLocked());
+  }
+}
+
+void MemoryDumpManager::TeardownForTracing() {
+  // There might be a memory dump in progress while this happens. Therefore,
+  // ensure that the MDM state which depends on the tracing enabled / disabled
+  // state is always accessed by the dumping methods holding the |lock_|.
+  AutoLock lock(lock_);
+
+  MemoryDumpScheduler::GetInstance()->Stop();
+}
+
+MemoryDumpManager::ProcessMemoryDumpAsyncState::ProcessMemoryDumpAsyncState(
+    MemoryDumpRequestArgs req_args,
+    const MemoryDumpProviderInfo::OrderedSet& dump_providers,
+    ProcessMemoryDumpCallback callback,
+    scoped_refptr<SequencedTaskRunner> dump_thread_task_runner)
+    : req_args(req_args),
+      callback(callback),
+      callback_task_runner(ThreadTaskRunnerHandle::Get()),
+      dump_thread_task_runner(std::move(dump_thread_task_runner)) {
+  pending_dump_providers.reserve(dump_providers.size());
+  pending_dump_providers.assign(dump_providers.rbegin(), dump_providers.rend());
+  MemoryDumpArgs args = {req_args.level_of_detail, req_args.dump_guid};
+  process_memory_dump = std::make_unique<ProcessMemoryDump>(args);
+}
+
+MemoryDumpManager::ProcessMemoryDumpAsyncState::~ProcessMemoryDumpAsyncState() =
+    default;
+
+}  // namespace trace_event
+}  // namespace base
diff --git a/base/trace_event/memory_dump_manager.h b/base/trace_event/memory_dump_manager.h
new file mode 100644
index 0000000..6033cfb
--- /dev/null
+++ b/base/trace_event/memory_dump_manager.h
@@ -0,0 +1,267 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TRACE_EVENT_MEMORY_DUMP_MANAGER_H_
+#define BASE_TRACE_EVENT_MEMORY_DUMP_MANAGER_H_
+
+#include <stdint.h>
+
+#include <map>
+#include <memory>
+#include <unordered_set>
+#include <vector>
+
+#include "base/atomicops.h"
+#include "base/containers/hash_tables.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/singleton.h"
+#include "base/synchronization/lock.h"
+#include "base/trace_event/memory_allocator_dump.h"
+#include "base/trace_event/memory_dump_provider_info.h"
+#include "base/trace_event/memory_dump_request_args.h"
+#include "base/trace_event/process_memory_dump.h"
+#include "base/trace_event/trace_event.h"
+
+namespace base {
+
+class SequencedTaskRunner;
+class SingleThreadTaskRunner;
+class Thread;
+
+namespace trace_event {
+
+class MemoryDumpProvider;
+
+// This is the interface exposed to the rest of the codebase to deal with
+// memory tracing. The main entry point for clients is represented by
+// RequestDumpPoint(). The extension by Un(RegisterDumpProvider).
+class BASE_EXPORT MemoryDumpManager {
+ public:
+  using RequestGlobalDumpFunction =
+      RepeatingCallback<void(MemoryDumpType, MemoryDumpLevelOfDetail)>;
+
+  static const char* const kTraceCategory;
+
+  // This value is returned as the tracing id of the child processes by
+  // GetTracingProcessId() when tracing is not enabled.
+  static const uint64_t kInvalidTracingProcessId;
+
+  static MemoryDumpManager* GetInstance();
+  static std::unique_ptr<MemoryDumpManager> CreateInstanceForTesting();
+
+  // Invoked once per process to listen to trace begin / end events.
+  // Initialization can happen after (Un)RegisterMemoryDumpProvider() calls
+  // and the MemoryDumpManager guarantees to support this.
+  // On the other side, the MemoryDumpManager will not be fully operational
+  // (any CreateProcessDump() will return a failure) until initialized.
+  // Arguments:
+  //  is_coordinator: True when current process coordinates the periodic dump
+  //      triggering.
+  //  request_dump_function: Function to invoke a global dump. Global dump
+  //      involves embedder-specific behaviors like multiprocess handshaking.
+  //      TODO(primiano): this is only required to trigger global dumps from
+  //      the scheduler. Should be removed once they are both moved out of base.
+  void Initialize(RequestGlobalDumpFunction request_dump_function,
+                  bool is_coordinator);
+
+  // (Un)Registers a MemoryDumpProvider instance.
+  // Args:
+  //  - mdp: the MemoryDumpProvider instance to be registered. MemoryDumpManager
+  //      does NOT take memory ownership of |mdp|, which is expected to either
+  //      be a singleton or unregister itself.
+  //  - name: a friendly name (duplicates allowed). Used for debugging and
+  //      run-time profiling of memory-infra internals. Must be a long-lived
+  //      C string.
+  //  - task_runner: either a SingleThreadTaskRunner or SequencedTaskRunner. All
+  //      the calls to |mdp| will be run on the given |task_runner|. If passed
+  //      null |mdp| should be able to handle calls on arbitrary threads.
+  //  - options: extra optional arguments. See memory_dump_provider.h.
+  void RegisterDumpProvider(MemoryDumpProvider* mdp,
+                            const char* name,
+                            scoped_refptr<SingleThreadTaskRunner> task_runner);
+  void RegisterDumpProvider(MemoryDumpProvider* mdp,
+                            const char* name,
+                            scoped_refptr<SingleThreadTaskRunner> task_runner,
+                            MemoryDumpProvider::Options options);
+  void RegisterDumpProviderWithSequencedTaskRunner(
+      MemoryDumpProvider* mdp,
+      const char* name,
+      scoped_refptr<SequencedTaskRunner> task_runner,
+      MemoryDumpProvider::Options options);
+  void UnregisterDumpProvider(MemoryDumpProvider* mdp);
+
+  // Unregisters an unbound dump provider and takes care about its deletion
+  // asynchronously. Can be used only for for dump providers with no
+  // task-runner affinity.
+  // This method takes ownership of the dump provider and guarantees that:
+  //  - The |mdp| will be deleted at some point in the near future.
+  //  - Its deletion will not happen concurrently with the OnMemoryDump() call.
+  // Note that OnMemoryDump() calls can still happen after this method returns.
+  void UnregisterAndDeleteDumpProviderSoon(
+      std::unique_ptr<MemoryDumpProvider> mdp);
+
+  // Prepare MemoryDumpManager for CreateProcessDump() calls for tracing-related
+  // modes (i.e. |level_of_detail| != SUMMARY_ONLY).
+  // Also initializes the scheduler with the given config.
+  void SetupForTracing(const TraceConfig::MemoryDumpConfig&);
+
+  // Tear-down tracing related state.
+  // Non-tracing modes (e.g. SUMMARY_ONLY) will continue to work.
+  void TeardownForTracing();
+
+  // Creates a memory dump for the current process and appends it to the trace.
+  // |callback| will be invoked asynchronously upon completion on the same
+  // thread on which CreateProcessDump() was called. This method should only be
+  // used by the memory-infra service while creating a global memory dump.
+  void CreateProcessDump(const MemoryDumpRequestArgs& args,
+                         const ProcessMemoryDumpCallback& callback);
+
+  // Lets tests see if a dump provider is registered.
+  bool IsDumpProviderRegisteredForTesting(MemoryDumpProvider*);
+
+  // Returns a unique id for identifying the processes. The id can be
+  // retrieved by child processes only when tracing is enabled. This is
+  // intended to express cross-process sharing of memory dumps on the
+  // child-process side, without having to know its own child process id.
+  uint64_t GetTracingProcessId() const { return tracing_process_id_; }
+  void set_tracing_process_id(uint64_t tracing_process_id) {
+    tracing_process_id_ = tracing_process_id;
+  }
+
+  // Returns the name for a the allocated_objects dump. Use this to declare
+  // suballocator dumps from other dump providers.
+  // It will return nullptr if there is no dump provider for the system
+  // allocator registered (which is currently the case for Mac OS).
+  const char* system_allocator_pool_name() const {
+    return kSystemAllocatorPoolName;
+  };
+
+  // When set to true, calling |RegisterMemoryDumpProvider| is a no-op.
+  void set_dumper_registrations_ignored_for_testing(bool ignored) {
+    dumper_registrations_ignored_for_testing_ = ignored;
+  }
+
+ private:
+  friend std::default_delete<MemoryDumpManager>;  // For the testing instance.
+  friend struct DefaultSingletonTraits<MemoryDumpManager>;
+  friend class MemoryDumpManagerTest;
+  FRIEND_TEST_ALL_PREFIXES(MemoryDumpManagerTest,
+                           NoStackOverflowWithTooManyMDPs);
+
+  // Holds the state of a process memory dump that needs to be carried over
+  // across task runners in order to fulfill an asynchronous CreateProcessDump()
+  // request. At any time exactly one task runner owns a
+  // ProcessMemoryDumpAsyncState.
+  struct ProcessMemoryDumpAsyncState {
+    ProcessMemoryDumpAsyncState(
+        MemoryDumpRequestArgs req_args,
+        const MemoryDumpProviderInfo::OrderedSet& dump_providers,
+        ProcessMemoryDumpCallback callback,
+        scoped_refptr<SequencedTaskRunner> dump_thread_task_runner);
+    ~ProcessMemoryDumpAsyncState();
+
+    // A ProcessMemoryDump to collect data from MemoryDumpProviders.
+    std::unique_ptr<ProcessMemoryDump> process_memory_dump;
+
+    // The arguments passed to the initial CreateProcessDump() request.
+    const MemoryDumpRequestArgs req_args;
+
+    // An ordered sequence of dump providers that have to be invoked to complete
+    // the dump. This is a copy of |dump_providers_| at the beginning of a dump
+    // and becomes empty at the end, when all dump providers have been invoked.
+    std::vector<scoped_refptr<MemoryDumpProviderInfo>> pending_dump_providers;
+
+    // Callback passed to the initial call to CreateProcessDump().
+    ProcessMemoryDumpCallback callback;
+
+    // The thread on which FinishAsyncProcessDump() (and hence |callback|)
+    // should be invoked. This is the thread on which the initial
+    // CreateProcessDump() request was called.
+    const scoped_refptr<SingleThreadTaskRunner> callback_task_runner;
+
+    // The thread on which unbound dump providers should be invoked.
+    // This is essentially |dump_thread_|.task_runner() but needs to be kept
+    // as a separate variable as it needs to be accessed by arbitrary dumpers'
+    // threads outside of the lock_ to avoid races when disabling tracing.
+    // It is immutable for all the duration of a tracing session.
+    const scoped_refptr<SequencedTaskRunner> dump_thread_task_runner;
+
+   private:
+    DISALLOW_COPY_AND_ASSIGN(ProcessMemoryDumpAsyncState);
+  };
+
+  static const int kMaxConsecutiveFailuresCount;
+  static const char* const kSystemAllocatorPoolName;
+
+  MemoryDumpManager();
+  virtual ~MemoryDumpManager();
+
+  static void SetInstanceForTesting(MemoryDumpManager* instance);
+
+  // Lazily initializes dump_thread_ and returns its TaskRunner.
+  scoped_refptr<base::SequencedTaskRunner> GetOrCreateBgTaskRunnerLocked();
+
+  // Calls InvokeOnMemoryDump() for the each MDP that belongs to the current
+  // task runner and switches to the task runner of the next MDP. Handles
+  // failures in MDP and thread hops, and always calls FinishAsyncProcessDump()
+  // at the end.
+  void ContinueAsyncProcessDump(
+      ProcessMemoryDumpAsyncState* owned_pmd_async_state);
+
+  // Invokes OnMemoryDump() of the given MDP. Should be called on the MDP task
+  // runner.
+  void InvokeOnMemoryDump(MemoryDumpProviderInfo* mdpinfo,
+                          ProcessMemoryDump* pmd);
+
+  void FinishAsyncProcessDump(
+      std::unique_ptr<ProcessMemoryDumpAsyncState> pmd_async_state);
+
+  // Helper for RegierDumpProvider* functions.
+  void RegisterDumpProviderInternal(
+      MemoryDumpProvider* mdp,
+      const char* name,
+      scoped_refptr<SequencedTaskRunner> task_runner,
+      const MemoryDumpProvider::Options& options);
+
+  // Helper for the public UnregisterDumpProvider* functions.
+  void UnregisterDumpProviderInternal(MemoryDumpProvider* mdp,
+                                      bool take_mdp_ownership_and_delete_async);
+
+  bool can_request_global_dumps() const {
+    return !request_dump_function_.is_null();
+  }
+
+  // An ordered set of registered MemoryDumpProviderInfo(s), sorted by task
+  // runner affinity (MDPs belonging to the same task runners are adjacent).
+  MemoryDumpProviderInfo::OrderedSet dump_providers_;
+
+  // Function provided by the embedder to handle global dump requests.
+  RequestGlobalDumpFunction request_dump_function_;
+
+  // True when current process coordinates the periodic dump triggering.
+  bool is_coordinator_;
+
+  // Protects from concurrent accesses to the local state, eg: to guard against
+  // disabling logging while dumping on another thread.
+  Lock lock_;
+
+  // Thread used for MemoryDumpProviders which don't specify a task runner
+  // affinity.
+  std::unique_ptr<Thread> dump_thread_;
+
+  // The unique id of the child process. This is created only for tracing and is
+  // expected to be valid only when tracing is enabled.
+  uint64_t tracing_process_id_;
+
+  // When true, calling |RegisterMemoryDumpProvider| is a no-op.
+  bool dumper_registrations_ignored_for_testing_;
+
+  DISALLOW_COPY_AND_ASSIGN(MemoryDumpManager);
+};
+
+}  // namespace trace_event
+}  // namespace base
+
+#endif  // BASE_TRACE_EVENT_MEMORY_DUMP_MANAGER_H_
diff --git a/base/trace_event/memory_dump_manager_test_utils.h b/base/trace_event/memory_dump_manager_test_utils.h
new file mode 100644
index 0000000..413017f
--- /dev/null
+++ b/base/trace_event/memory_dump_manager_test_utils.h
@@ -0,0 +1,38 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TRACE_EVENT_MEMORY_DUMP_MANAGER_TEST_UTILS_H_
+#define BASE_TRACE_EVENT_MEMORY_DUMP_MANAGER_TEST_UTILS_H_
+
+#include "base/bind.h"
+#include "base/trace_event/memory_dump_manager.h"
+#include "base/trace_event/memory_dump_request_args.h"
+
+namespace base {
+namespace trace_event {
+
+void RequestGlobalDumpForInProcessTesting(
+    base::trace_event::MemoryDumpType dump_type,
+    base::trace_event::MemoryDumpLevelOfDetail level_of_detail) {
+  MemoryDumpRequestArgs local_args = {0 /* dump_guid */, dump_type,
+                                      level_of_detail};
+  MemoryDumpManager::GetInstance()->CreateProcessDump(
+      local_args, ProcessMemoryDumpCallback());
+};
+
+// Short circuits the RequestGlobalDumpFunction() to CreateProcessDump(),
+// effectively allowing to use both in unittests with the same behavior.
+// Unittests are in-process only and don't require all the multi-process
+// dump handshaking (which would require bits outside of base).
+void InitializeMemoryDumpManagerForInProcessTesting(bool is_coordinator) {
+  MemoryDumpManager* instance = MemoryDumpManager::GetInstance();
+  instance->set_dumper_registrations_ignored_for_testing(true);
+  instance->Initialize(BindRepeating(&RequestGlobalDumpForInProcessTesting),
+                       is_coordinator);
+}
+
+}  // namespace trace_event
+}  // namespace base
+
+#endif  // BASE_TRACE_EVENT_MEMORY_DUMP_MANAGER_TEST_UTILS_H_
diff --git a/base/trace_event/memory_dump_manager_unittest.cc b/base/trace_event/memory_dump_manager_unittest.cc
new file mode 100644
index 0000000..706df2d
--- /dev/null
+++ b/base/trace_event/memory_dump_manager_unittest.cc
@@ -0,0 +1,840 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/trace_event/memory_dump_manager.h"
+
+#include <stdint.h>
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "base/allocator/buildflags.h"
+#include "base/base_switches.h"
+#include "base/callback.h"
+#include "base/command_line.h"
+#include "base/debug/thread_heap_usage_tracker.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/run_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/task_scheduler/post_task.h"
+#include "base/test/scoped_task_environment.h"
+#include "base/test/test_io_thread.h"
+#include "base/threading/platform_thread.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "base/threading/thread.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/trace_event/memory_dump_manager_test_utils.h"
+#include "base/trace_event/memory_dump_provider.h"
+#include "base/trace_event/memory_dump_request_args.h"
+#include "base/trace_event/memory_dump_scheduler.h"
+#include "base/trace_event/memory_infra_background_whitelist.h"
+#include "base/trace_event/process_memory_dump.h"
+#include "build/build_config.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::AtMost;
+using testing::Between;
+using testing::Invoke;
+using testing::Return;
+
+namespace base {
+namespace trace_event {
+
+// GTest matchers for MemoryDumpRequestArgs arguments.
+MATCHER(IsDetailedDump, "") {
+  return arg.level_of_detail == MemoryDumpLevelOfDetail::DETAILED;
+}
+
+MATCHER(IsLightDump, "") {
+  return arg.level_of_detail == MemoryDumpLevelOfDetail::LIGHT;
+}
+
+namespace {
+
+const char* kMDPName = "TestDumpProvider";
+const char* kWhitelistedMDPName = "WhitelistedTestDumpProvider";
+const char* const kTestMDPWhitelist[] = {kWhitelistedMDPName, nullptr};
+
+void RegisterDumpProvider(
+    MemoryDumpProvider* mdp,
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+    const MemoryDumpProvider::Options& options,
+    const char* name = kMDPName) {
+  MemoryDumpManager* mdm = MemoryDumpManager::GetInstance();
+  mdm->set_dumper_registrations_ignored_for_testing(false);
+  mdm->RegisterDumpProvider(mdp, name, std::move(task_runner), options);
+  mdm->set_dumper_registrations_ignored_for_testing(true);
+}
+
+void RegisterDumpProvider(
+    MemoryDumpProvider* mdp,
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
+  RegisterDumpProvider(mdp, task_runner, MemoryDumpProvider::Options());
+}
+
+void RegisterDumpProviderWithSequencedTaskRunner(
+    MemoryDumpProvider* mdp,
+    scoped_refptr<base::SequencedTaskRunner> task_runner,
+    const MemoryDumpProvider::Options& options) {
+  MemoryDumpManager* mdm = MemoryDumpManager::GetInstance();
+  mdm->set_dumper_registrations_ignored_for_testing(false);
+  mdm->RegisterDumpProviderWithSequencedTaskRunner(mdp, kMDPName, task_runner,
+                                                   options);
+  mdm->set_dumper_registrations_ignored_for_testing(true);
+}
+
+// Posts |task| to |task_runner| and blocks until it is executed.
+void PostTaskAndWait(const Location& from_here,
+                     SequencedTaskRunner* task_runner,
+                     base::OnceClosure task) {
+  base::WaitableEvent event(WaitableEvent::ResetPolicy::MANUAL,
+                            WaitableEvent::InitialState::NOT_SIGNALED);
+  task_runner->PostTask(from_here, std::move(task));
+  task_runner->PostTask(FROM_HERE, base::BindOnce(&WaitableEvent::Signal,
+                                                  base::Unretained(&event)));
+  // The SequencedTaskRunner guarantees that |event| will only be signaled after
+  // |task| is executed.
+  event.Wait();
+}
+
+class MockMemoryDumpProvider : public MemoryDumpProvider {
+ public:
+  MOCK_METHOD0(Destructor, void());
+  MOCK_METHOD2(OnMemoryDump,
+               bool(const MemoryDumpArgs& args, ProcessMemoryDump* pmd));
+
+  MockMemoryDumpProvider() : enable_mock_destructor(false) {
+    ON_CALL(*this, OnMemoryDump(_, _))
+        .WillByDefault(
+            Invoke([](const MemoryDumpArgs&, ProcessMemoryDump* pmd) -> bool {
+              return true;
+            }));
+  }
+  ~MockMemoryDumpProvider() override {
+    if (enable_mock_destructor)
+      Destructor();
+  }
+
+  bool enable_mock_destructor;
+};
+
+class TestSequencedTaskRunner : public SequencedTaskRunner {
+ public:
+  TestSequencedTaskRunner() = default;
+
+  void set_enabled(bool value) { enabled_ = value; }
+  unsigned no_of_post_tasks() const { return num_of_post_tasks_; }
+
+  bool PostNonNestableDelayedTask(const Location& from_here,
+                                  OnceClosure task,
+                                  TimeDelta delay) override {
+    NOTREACHED();
+    return false;
+  }
+
+  bool PostDelayedTask(const Location& from_here,
+                       OnceClosure task,
+                       TimeDelta delay) override {
+    num_of_post_tasks_++;
+    if (enabled_) {
+      return task_runner_->PostDelayedTask(from_here, std::move(task), delay);
+    }
+    return false;
+  }
+
+  bool RunsTasksInCurrentSequence() const override {
+    return task_runner_->RunsTasksInCurrentSequence();
+  }
+
+ private:
+  ~TestSequencedTaskRunner() override = default;
+
+  const scoped_refptr<SequencedTaskRunner> task_runner_ =
+      CreateSequencedTaskRunnerWithTraits({});
+  bool enabled_ = true;
+  unsigned num_of_post_tasks_ = 0;
+};
+
+class TestingThreadHeapUsageTracker : public debug::ThreadHeapUsageTracker {
+ public:
+  using ThreadHeapUsageTracker::DisableHeapTrackingForTesting;
+};
+
+}  // namespace
+
+class MemoryDumpManagerTest : public testing::Test {
+ public:
+  MemoryDumpManagerTest(bool is_coordinator = false)
+      : is_coordinator_(is_coordinator) {}
+
+  void SetUp() override {
+    // Bring up and initialize MemoryDumpManager while single-threaded (before
+    // instantiating ScopedTaskEnvironment) to avoid data races if worker
+    // threads use tracing globals early.
+    mdm_ = MemoryDumpManager::CreateInstanceForTesting();
+    ASSERT_EQ(mdm_.get(), MemoryDumpManager::GetInstance());
+
+    InitializeMemoryDumpManagerForInProcessTesting(is_coordinator_);
+
+    scoped_task_environment_ = std::make_unique<test::ScopedTaskEnvironment>();
+  }
+
+  void TearDown() override {
+    scoped_task_environment_.reset();
+
+    // Tear down the MemoryDumpManager while single-threaded to mirror logic in
+    // SetUp().
+    mdm_.reset();
+    TraceLog::ResetForTesting();
+  }
+
+ protected:
+  // Blocks the current thread (spinning a nested message loop) until the
+  // memory dump is complete. Returns:
+  // - return value: the |success| from the CreateProcessDump() callback.
+  bool RequestProcessDumpAndWait(MemoryDumpType dump_type,
+                                 MemoryDumpLevelOfDetail level_of_detail) {
+    RunLoop run_loop;
+    bool success = false;
+    static uint64_t test_guid = 1;
+    test_guid++;
+    MemoryDumpRequestArgs request_args{test_guid, dump_type, level_of_detail};
+
+    // The signature of the callback delivered by MemoryDumpManager is:
+    // void ProcessMemoryDumpCallback(
+    //     uint64_t dump_guid,
+    //     bool success,
+    //     std::unique_ptr<ProcessMemoryDump> pmd)
+    // The extra arguments prepended to the |callback| below (the ones with the
+    // "curried_" prefix) are just passed from the Bind(). This is just to get
+    // around the limitation of Bind() in supporting only capture-less lambdas.
+    ProcessMemoryDumpCallback callback = Bind(
+        [](bool* curried_success, Closure curried_quit_closure,
+           uint64_t curried_expected_guid, bool success, uint64_t dump_guid,
+           std::unique_ptr<ProcessMemoryDump> pmd) {
+          *curried_success = success;
+          EXPECT_EQ(curried_expected_guid, dump_guid);
+          ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+                                                  curried_quit_closure);
+        },
+        Unretained(&success), run_loop.QuitClosure(), test_guid);
+
+    mdm_->CreateProcessDump(request_args, callback);
+    run_loop.Run();
+    return success;
+  }
+
+  void EnableForTracing() {
+    mdm_->SetupForTracing(TraceConfig::MemoryDumpConfig());
+  }
+
+  void EnableForTracingWithTraceConfig(const std::string trace_config_string) {
+    TraceConfig trace_config(trace_config_string);
+    mdm_->SetupForTracing(trace_config.memory_dump_config());
+  }
+
+  void DisableTracing() { mdm_->TeardownForTracing(); }
+
+  int GetMaxConsecutiveFailuresCount() const {
+    return MemoryDumpManager::kMaxConsecutiveFailuresCount;
+  }
+
+  const MemoryDumpProvider::Options kDefaultOptions;
+  std::unique_ptr<MemoryDumpManager> mdm_;
+
+ private:
+  // To tear down the singleton instance after each test.
+  ShadowingAtExitManager at_exit_manager_;
+
+  std::unique_ptr<test::ScopedTaskEnvironment> scoped_task_environment_;
+
+  // Whether the test MemoryDumpManager should be initialized as the
+  // coordinator.
+  const bool is_coordinator_;
+
+  DISALLOW_COPY_AND_ASSIGN(MemoryDumpManagerTest);
+};
+
+class MemoryDumpManagerTestAsCoordinator : public MemoryDumpManagerTest {
+ public:
+  MemoryDumpManagerTestAsCoordinator() : MemoryDumpManagerTest(true) {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MemoryDumpManagerTestAsCoordinator);
+};
+
+// Basic sanity checks. Registers a memory dump provider and checks that it is
+// called.
+TEST_F(MemoryDumpManagerTest, SingleDumper) {
+  MockMemoryDumpProvider mdp;
+  RegisterDumpProvider(&mdp, ThreadTaskRunnerHandle::Get());
+
+  // Now repeat enabling the memory category and check that the dumper is
+  // invoked this time.
+  EnableForTracing();
+  EXPECT_CALL(mdp, OnMemoryDump(_, _)).Times(3);
+  for (int i = 0; i < 3; ++i) {
+    EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
+                                          MemoryDumpLevelOfDetail::DETAILED));
+  }
+  DisableTracing();
+
+  mdm_->UnregisterDumpProvider(&mdp);
+
+  // Finally check the unregister logic: the global dump handler will be invoked
+  // but not the dump provider, as it has been unregistered.
+  EnableForTracing();
+  EXPECT_CALL(mdp, OnMemoryDump(_, _)).Times(0);
+  for (int i = 0; i < 3; ++i) {
+    EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
+                                          MemoryDumpLevelOfDetail::DETAILED));
+  }
+  DisableTracing();
+}
+
+// Checks that requesting dumps with high level of detail actually propagates
+// the level of the detail properly to OnMemoryDump() call on dump providers.
+TEST_F(MemoryDumpManagerTest, CheckMemoryDumpArgs) {
+  MockMemoryDumpProvider mdp;
+
+  RegisterDumpProvider(&mdp, ThreadTaskRunnerHandle::Get());
+  EnableForTracing();
+  EXPECT_CALL(mdp, OnMemoryDump(IsDetailedDump(), _));
+  EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
+                                        MemoryDumpLevelOfDetail::DETAILED));
+  DisableTracing();
+  mdm_->UnregisterDumpProvider(&mdp);
+
+  // Check that requesting dumps with low level of detail actually propagates to
+  // OnMemoryDump() call on dump providers.
+  RegisterDumpProvider(&mdp, ThreadTaskRunnerHandle::Get());
+  EnableForTracing();
+  EXPECT_CALL(mdp, OnMemoryDump(IsLightDump(), _));
+  EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
+                                        MemoryDumpLevelOfDetail::LIGHT));
+  DisableTracing();
+  mdm_->UnregisterDumpProvider(&mdp);
+}
+
+// Checks that the (Un)RegisterDumpProvider logic behaves sanely.
+TEST_F(MemoryDumpManagerTest, MultipleDumpers) {
+  MockMemoryDumpProvider mdp1;
+  MockMemoryDumpProvider mdp2;
+
+  // Enable only mdp1.
+  RegisterDumpProvider(&mdp1, ThreadTaskRunnerHandle::Get());
+  EnableForTracing();
+  EXPECT_CALL(mdp1, OnMemoryDump(_, _));
+  EXPECT_CALL(mdp2, OnMemoryDump(_, _)).Times(0);
+  EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
+                                        MemoryDumpLevelOfDetail::DETAILED));
+  DisableTracing();
+
+  // Invert: enable mdp2 and disable mdp1.
+  mdm_->UnregisterDumpProvider(&mdp1);
+  RegisterDumpProvider(&mdp2, nullptr);
+  EnableForTracing();
+  EXPECT_CALL(mdp1, OnMemoryDump(_, _)).Times(0);
+  EXPECT_CALL(mdp2, OnMemoryDump(_, _));
+  EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
+                                        MemoryDumpLevelOfDetail::DETAILED));
+  DisableTracing();
+
+  // Enable both mdp1 and mdp2.
+  RegisterDumpProvider(&mdp1, nullptr);
+  EnableForTracing();
+  EXPECT_CALL(mdp1, OnMemoryDump(_, _));
+  EXPECT_CALL(mdp2, OnMemoryDump(_, _));
+  EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
+                                        MemoryDumpLevelOfDetail::DETAILED));
+  DisableTracing();
+}
+
+// Checks that the dump provider invocations depend only on the current
+// registration state and not on previous registrations and dumps.
+// Flaky on iOS, see crbug.com/706874
+#if defined(OS_IOS)
+#define MAYBE_RegistrationConsistency DISABLED_RegistrationConsistency
+#else
+#define MAYBE_RegistrationConsistency RegistrationConsistency
+#endif
+TEST_F(MemoryDumpManagerTest, MAYBE_RegistrationConsistency) {
+  MockMemoryDumpProvider mdp;
+
+  RegisterDumpProvider(&mdp, ThreadTaskRunnerHandle::Get());
+
+  {
+    EXPECT_CALL(mdp, OnMemoryDump(_, _));
+    EnableForTracing();
+    EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
+                                          MemoryDumpLevelOfDetail::DETAILED));
+    DisableTracing();
+  }
+
+  mdm_->UnregisterDumpProvider(&mdp);
+
+  {
+    EXPECT_CALL(mdp, OnMemoryDump(_, _)).Times(0);
+    EnableForTracing();
+    EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
+                                          MemoryDumpLevelOfDetail::DETAILED));
+    DisableTracing();
+  }
+
+  RegisterDumpProvider(&mdp, ThreadTaskRunnerHandle::Get());
+  mdm_->UnregisterDumpProvider(&mdp);
+
+  {
+    EXPECT_CALL(mdp, OnMemoryDump(_, _)).Times(0);
+    EnableForTracing();
+    EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
+                                          MemoryDumpLevelOfDetail::DETAILED));
+    DisableTracing();
+  }
+
+  RegisterDumpProvider(&mdp, ThreadTaskRunnerHandle::Get());
+  mdm_->UnregisterDumpProvider(&mdp);
+  RegisterDumpProvider(&mdp, ThreadTaskRunnerHandle::Get());
+
+  {
+    EXPECT_CALL(mdp, OnMemoryDump(_, _));
+    EnableForTracing();
+    EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
+                                          MemoryDumpLevelOfDetail::DETAILED));
+    DisableTracing();
+  }
+}
+
+// Checks that the MemoryDumpManager respects the thread affinity when a
+// MemoryDumpProvider specifies a task_runner(). The test starts creating 8
+// threads and registering a MemoryDumpProvider on each of them. At each
+// iteration, one thread is removed, to check the live unregistration logic.
+TEST_F(MemoryDumpManagerTest, RespectTaskRunnerAffinity) {
+  const uint32_t kNumInitialThreads = 8;
+
+  std::vector<std::unique_ptr<Thread>> threads;
+  std::vector<std::unique_ptr<MockMemoryDumpProvider>> mdps;
+
+  // Create the threads and setup the expectations. Given that at each iteration
+  // we will pop out one thread/MemoryDumpProvider, each MDP is supposed to be
+  // invoked a number of times equal to its index.
+  for (uint32_t i = kNumInitialThreads; i > 0; --i) {
+    threads.push_back(WrapUnique(new Thread("test thread")));
+    auto* thread = threads.back().get();
+    thread->Start();
+    scoped_refptr<SingleThreadTaskRunner> task_runner = thread->task_runner();
+    mdps.push_back(WrapUnique(new MockMemoryDumpProvider()));
+    auto* mdp = mdps.back().get();
+    RegisterDumpProvider(mdp, task_runner, kDefaultOptions);
+    EXPECT_CALL(*mdp, OnMemoryDump(_, _))
+        .Times(i)
+        .WillRepeatedly(Invoke(
+            [task_runner](const MemoryDumpArgs&, ProcessMemoryDump*) -> bool {
+              EXPECT_TRUE(task_runner->RunsTasksInCurrentSequence());
+              return true;
+            }));
+  }
+  EnableForTracing();
+
+  while (!threads.empty()) {
+    EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
+                                          MemoryDumpLevelOfDetail::DETAILED));
+
+    // Unregister a MDP and destroy one thread at each iteration to check the
+    // live unregistration logic. The unregistration needs to happen on the same
+    // thread the MDP belongs to.
+    {
+      RunLoop run_loop;
+      Closure unregistration =
+          Bind(&MemoryDumpManager::UnregisterDumpProvider,
+               Unretained(mdm_.get()), Unretained(mdps.back().get()));
+      threads.back()->task_runner()->PostTaskAndReply(FROM_HERE, unregistration,
+                                                      run_loop.QuitClosure());
+      run_loop.Run();
+    }
+    mdps.pop_back();
+    threads.back()->Stop();
+    threads.pop_back();
+  }
+
+  DisableTracing();
+}
+
+// Check that the memory dump calls are always posted on task runner for
+// SequencedTaskRunner case and that the dump provider gets disabled when
+// PostTask fails, but the dump still succeeds.
+TEST_F(MemoryDumpManagerTest, PostTaskForSequencedTaskRunner) {
+  std::vector<MockMemoryDumpProvider> mdps(3);
+  scoped_refptr<TestSequencedTaskRunner> task_runner1(
+      MakeRefCounted<TestSequencedTaskRunner>());
+  scoped_refptr<TestSequencedTaskRunner> task_runner2(
+      MakeRefCounted<TestSequencedTaskRunner>());
+  RegisterDumpProviderWithSequencedTaskRunner(&mdps[0], task_runner1,
+                                              kDefaultOptions);
+  RegisterDumpProviderWithSequencedTaskRunner(&mdps[1], task_runner2,
+                                              kDefaultOptions);
+  RegisterDumpProviderWithSequencedTaskRunner(&mdps[2], task_runner2,
+                                              kDefaultOptions);
+  // |mdps[0]| should be disabled permanently after first dump.
+  EXPECT_CALL(mdps[0], OnMemoryDump(_, _)).Times(0);
+  EXPECT_CALL(mdps[1], OnMemoryDump(_, _)).Times(2);
+  EXPECT_CALL(mdps[2], OnMemoryDump(_, _)).Times(2);
+
+  EnableForTracing();
+
+  task_runner1->set_enabled(false);
+  EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
+                                        MemoryDumpLevelOfDetail::DETAILED));
+  EXPECT_EQ(1u, task_runner1->no_of_post_tasks());
+  EXPECT_EQ(1u, task_runner2->no_of_post_tasks());
+
+  task_runner1->set_enabled(true);
+  EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
+                                        MemoryDumpLevelOfDetail::DETAILED));
+  EXPECT_EQ(2u, task_runner1->no_of_post_tasks());
+  EXPECT_EQ(2u, task_runner2->no_of_post_tasks());
+  DisableTracing();
+}
+
+// Checks that providers get disabled after 3 consecutive failures, but not
+// otherwise (e.g., if interleaved).
+TEST_F(MemoryDumpManagerTest, DisableFailingDumpers) {
+  MockMemoryDumpProvider mdp1;
+  MockMemoryDumpProvider mdp2;
+
+  RegisterDumpProvider(&mdp1, nullptr);
+  RegisterDumpProvider(&mdp2, nullptr);
+  EnableForTracing();
+
+  EXPECT_CALL(mdp1, OnMemoryDump(_, _))
+      .Times(GetMaxConsecutiveFailuresCount())
+      .WillRepeatedly(Return(false));
+
+  EXPECT_CALL(mdp2, OnMemoryDump(_, _))
+      .WillOnce(Return(false))
+      .WillOnce(Return(true))
+      .WillOnce(Return(false))
+      .WillOnce(Return(false))
+      .WillOnce(Return(true))
+      .WillOnce(Return(false));
+
+  const int kNumDumps = 2 * GetMaxConsecutiveFailuresCount();
+  for (int i = 0; i < kNumDumps; i++) {
+    EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
+                                          MemoryDumpLevelOfDetail::DETAILED));
+  }
+
+  DisableTracing();
+}
+
+// Sneakily registers an extra memory dump provider while an existing one is
+// dumping and expect it to take part in the already active tracing session.
+TEST_F(MemoryDumpManagerTest, RegisterDumperWhileDumping) {
+  MockMemoryDumpProvider mdp1;
+  MockMemoryDumpProvider mdp2;
+
+  RegisterDumpProvider(&mdp1, nullptr);
+  EnableForTracing();
+
+  EXPECT_CALL(mdp1, OnMemoryDump(_, _))
+      .Times(4)
+      .WillOnce(Return(true))
+      .WillOnce(
+          Invoke([&mdp2](const MemoryDumpArgs&, ProcessMemoryDump*) -> bool {
+            RegisterDumpProvider(&mdp2, nullptr);
+            return true;
+          }))
+      .WillRepeatedly(Return(true));
+
+  // Depending on the insertion order (before or after mdp1), mdp2 might be
+  // called also immediately after it gets registered.
+  EXPECT_CALL(mdp2, OnMemoryDump(_, _)).Times(Between(2, 3));
+
+  for (int i = 0; i < 4; i++) {
+    EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
+                                          MemoryDumpLevelOfDetail::DETAILED));
+  }
+
+  DisableTracing();
+}
+
+// Like RegisterDumperWhileDumping, but unregister the dump provider instead.
+TEST_F(MemoryDumpManagerTest, UnregisterDumperWhileDumping) {
+  MockMemoryDumpProvider mdp1;
+  MockMemoryDumpProvider mdp2;
+
+  RegisterDumpProvider(&mdp1, ThreadTaskRunnerHandle::Get(), kDefaultOptions);
+  RegisterDumpProvider(&mdp2, ThreadTaskRunnerHandle::Get(), kDefaultOptions);
+  EnableForTracing();
+
+  EXPECT_CALL(mdp1, OnMemoryDump(_, _))
+      .Times(4)
+      .WillOnce(Return(true))
+      .WillOnce(
+          Invoke([&mdp2](const MemoryDumpArgs&, ProcessMemoryDump*) -> bool {
+            MemoryDumpManager::GetInstance()->UnregisterDumpProvider(&mdp2);
+            return true;
+          }))
+      .WillRepeatedly(Return(true));
+
+  // Depending on the insertion order (before or after mdp1), mdp2 might have
+  // been already called when UnregisterDumpProvider happens.
+  EXPECT_CALL(mdp2, OnMemoryDump(_, _)).Times(Between(1, 2));
+
+  for (int i = 0; i < 4; i++) {
+    EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
+                                          MemoryDumpLevelOfDetail::DETAILED));
+  }
+
+  DisableTracing();
+}
+
+// Checks that the dump does not abort when unregistering a provider while
+// dumping from a different thread than the dumping thread.
+TEST_F(MemoryDumpManagerTest, UnregisterDumperFromThreadWhileDumping) {
+  std::vector<std::unique_ptr<TestIOThread>> threads;
+  std::vector<std::unique_ptr<MockMemoryDumpProvider>> mdps;
+
+  for (int i = 0; i < 2; i++) {
+    threads.push_back(
+        WrapUnique(new TestIOThread(TestIOThread::kAutoStart)));
+    mdps.push_back(WrapUnique(new MockMemoryDumpProvider()));
+    RegisterDumpProvider(mdps.back().get(), threads.back()->task_runner(),
+                         kDefaultOptions);
+  }
+
+  int on_memory_dump_call_count = 0;
+
+  // When OnMemoryDump is called on either of the dump providers, it will
+  // unregister the other one.
+  for (const std::unique_ptr<MockMemoryDumpProvider>& mdp : mdps) {
+    int other_idx = (mdps.front() == mdp);
+    // TestIOThread's task runner must be obtained from the main thread but can
+    // then be used from other threads.
+    scoped_refptr<SingleThreadTaskRunner> other_runner =
+        threads[other_idx]->task_runner();
+    MockMemoryDumpProvider* other_mdp = mdps[other_idx].get();
+    auto on_dump = [this, other_runner, other_mdp, &on_memory_dump_call_count](
+                       const MemoryDumpArgs& args, ProcessMemoryDump* pmd) {
+      PostTaskAndWait(FROM_HERE, other_runner.get(),
+                      base::BindOnce(&MemoryDumpManager::UnregisterDumpProvider,
+                                     base::Unretained(&*mdm_), other_mdp));
+      on_memory_dump_call_count++;
+      return true;
+    };
+
+    // OnMemoryDump is called once for the provider that dumps first, and zero
+    // times for the other provider.
+    EXPECT_CALL(*mdp, OnMemoryDump(_, _))
+        .Times(AtMost(1))
+        .WillOnce(Invoke(on_dump));
+  }
+
+  EnableForTracing();
+  EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
+                                        MemoryDumpLevelOfDetail::DETAILED));
+  ASSERT_EQ(1, on_memory_dump_call_count);
+
+  DisableTracing();
+}
+
+// If a thread (with a dump provider living on it) is torn down during a dump
+// its dump provider should be skipped but the dump itself should succeed.
+TEST_F(MemoryDumpManagerTest, TearDownThreadWhileDumping) {
+  std::vector<std::unique_ptr<TestIOThread>> threads;
+  std::vector<std::unique_ptr<MockMemoryDumpProvider>> mdps;
+
+  for (int i = 0; i < 2; i++) {
+    threads.push_back(
+        WrapUnique(new TestIOThread(TestIOThread::kAutoStart)));
+    mdps.push_back(WrapUnique(new MockMemoryDumpProvider()));
+    RegisterDumpProvider(mdps.back().get(), threads.back()->task_runner(),
+                         kDefaultOptions);
+  }
+
+  int on_memory_dump_call_count = 0;
+
+  // When OnMemoryDump is called on either of the dump providers, it will
+  // tear down the thread of the other one.
+  for (const std::unique_ptr<MockMemoryDumpProvider>& mdp : mdps) {
+    int other_idx = (mdps.front() == mdp);
+    TestIOThread* other_thread = threads[other_idx].get();
+    // TestIOThread isn't thread-safe and must be stopped on the |main_runner|.
+    scoped_refptr<SequencedTaskRunner> main_runner =
+        SequencedTaskRunnerHandle::Get();
+    auto on_dump = [other_thread, main_runner, &on_memory_dump_call_count](
+                       const MemoryDumpArgs& args, ProcessMemoryDump* pmd) {
+      PostTaskAndWait(
+          FROM_HERE, main_runner.get(),
+          base::BindOnce(&TestIOThread::Stop, base::Unretained(other_thread)));
+      on_memory_dump_call_count++;
+      return true;
+    };
+
+    // OnMemoryDump is called once for the provider that dumps first, and zero
+    // times for the other provider.
+    EXPECT_CALL(*mdp, OnMemoryDump(_, _))
+        .Times(AtMost(1))
+        .WillOnce(Invoke(on_dump));
+  }
+
+  EnableForTracing();
+  EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
+                                        MemoryDumpLevelOfDetail::DETAILED));
+  ASSERT_EQ(1, on_memory_dump_call_count);
+
+  DisableTracing();
+}
+
+// Checks that the callback is invoked if CreateProcessDump() is called when
+// tracing is not enabled.
+TEST_F(MemoryDumpManagerTest, TriggerDumpWithoutTracing) {
+  MockMemoryDumpProvider mdp;
+  RegisterDumpProvider(&mdp, nullptr);
+  EXPECT_CALL(mdp, OnMemoryDump(_, _));
+  EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
+                                        MemoryDumpLevelOfDetail::DETAILED));
+}
+
+TEST_F(MemoryDumpManagerTest, BackgroundWhitelisting) {
+  SetDumpProviderWhitelistForTesting(kTestMDPWhitelist);
+
+  // Standard provider with default options (create dump for current process).
+  MockMemoryDumpProvider backgroundMdp;
+  RegisterDumpProvider(&backgroundMdp, nullptr, kDefaultOptions,
+                       kWhitelistedMDPName);
+
+  EnableForTracing();
+
+  EXPECT_CALL(backgroundMdp, OnMemoryDump(_, _)).Times(1);
+  EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::SUMMARY_ONLY,
+                                        MemoryDumpLevelOfDetail::BACKGROUND));
+  DisableTracing();
+}
+
+// Tests the basics of the UnregisterAndDeleteDumpProviderSoon(): the
+// unregistration should actually delete the providers and not leak them.
+TEST_F(MemoryDumpManagerTest, UnregisterAndDeleteDumpProviderSoon) {
+  static const int kNumProviders = 3;
+  int dtor_count = 0;
+  std::vector<std::unique_ptr<MemoryDumpProvider>> mdps;
+  for (int i = 0; i < kNumProviders; ++i) {
+    std::unique_ptr<MockMemoryDumpProvider> mdp(new MockMemoryDumpProvider);
+    mdp->enable_mock_destructor = true;
+    EXPECT_CALL(*mdp, Destructor())
+        .WillOnce(Invoke([&dtor_count]() { dtor_count++; }));
+    RegisterDumpProvider(mdp.get(), nullptr, kDefaultOptions);
+    mdps.push_back(std::move(mdp));
+  }
+
+  while (!mdps.empty()) {
+    mdm_->UnregisterAndDeleteDumpProviderSoon(std::move(mdps.back()));
+    mdps.pop_back();
+  }
+
+  ASSERT_EQ(kNumProviders, dtor_count);
+}
+
+// This test checks against races when unregistering an unbound dump provider
+// from another thread while dumping. It registers one MDP and, when
+// OnMemoryDump() is called, it invokes UnregisterAndDeleteDumpProviderSoon()
+// from another thread. The OnMemoryDump() and the dtor call are expected to
+// happen on the same thread (the MemoryDumpManager utility thread).
+TEST_F(MemoryDumpManagerTest, UnregisterAndDeleteDumpProviderSoonDuringDump) {
+  std::unique_ptr<MockMemoryDumpProvider> mdp(new MockMemoryDumpProvider);
+  mdp->enable_mock_destructor = true;
+  RegisterDumpProvider(mdp.get(), nullptr, kDefaultOptions);
+
+  base::PlatformThreadRef thread_ref;
+  auto self_unregister_from_another_thread = [&mdp, &thread_ref](
+      const MemoryDumpArgs&, ProcessMemoryDump*) -> bool {
+    thread_ref = PlatformThread::CurrentRef();
+    TestIOThread thread_for_unregistration(TestIOThread::kAutoStart);
+    PostTaskAndWait(
+        FROM_HERE, thread_for_unregistration.task_runner().get(),
+        base::BindOnce(&MemoryDumpManager::UnregisterAndDeleteDumpProviderSoon,
+                       base::Unretained(MemoryDumpManager::GetInstance()),
+                       std::move(mdp)));
+    thread_for_unregistration.Stop();
+    return true;
+  };
+  EXPECT_CALL(*mdp, OnMemoryDump(_, _))
+      .Times(1)
+      .WillOnce(Invoke(self_unregister_from_another_thread));
+  EXPECT_CALL(*mdp, Destructor())
+      .Times(1)
+      .WillOnce(Invoke([&thread_ref]() {
+        EXPECT_EQ(thread_ref, PlatformThread::CurrentRef());
+      }));
+
+  EnableForTracing();
+  for (int i = 0; i < 2; ++i) {
+    EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
+                                          MemoryDumpLevelOfDetail::DETAILED));
+  }
+  DisableTracing();
+}
+
+// Mock MDP class that tests if the number of OnMemoryDump() calls are expected.
+// It is implemented without gmocks since EXPECT_CALL implementation is slow
+// when there are 1000s of instances, as required in
+// NoStackOverflowWithTooManyMDPs test.
+class SimpleMockMemoryDumpProvider : public MemoryDumpProvider {
+ public:
+  SimpleMockMemoryDumpProvider(int expected_num_dump_calls)
+      : expected_num_dump_calls_(expected_num_dump_calls), num_dump_calls_(0) {}
+
+  ~SimpleMockMemoryDumpProvider() override {
+    EXPECT_EQ(expected_num_dump_calls_, num_dump_calls_);
+  }
+
+  bool OnMemoryDump(const MemoryDumpArgs& args,
+                    ProcessMemoryDump* pmd) override {
+    ++num_dump_calls_;
+    return true;
+  }
+
+ private:
+  int expected_num_dump_calls_;
+  int num_dump_calls_;
+};
+
+TEST_F(MemoryDumpManagerTest, NoStackOverflowWithTooManyMDPs) {
+  SetDumpProviderWhitelistForTesting(kTestMDPWhitelist);
+
+  int kMDPCount = 1000;
+  std::vector<std::unique_ptr<SimpleMockMemoryDumpProvider>> mdps;
+  for (int i = 0; i < kMDPCount; ++i) {
+    mdps.push_back(std::make_unique<SimpleMockMemoryDumpProvider>(1));
+    RegisterDumpProvider(mdps.back().get(), nullptr);
+  }
+  for (int i = 0; i < kMDPCount; ++i) {
+    mdps.push_back(std::make_unique<SimpleMockMemoryDumpProvider>(3));
+    RegisterDumpProvider(mdps.back().get(), nullptr, kDefaultOptions,
+                         kWhitelistedMDPName);
+  }
+  std::unique_ptr<Thread> stopped_thread(new Thread("test thread"));
+  stopped_thread->Start();
+  for (int i = 0; i < kMDPCount; ++i) {
+    mdps.push_back(std::make_unique<SimpleMockMemoryDumpProvider>(0));
+    RegisterDumpProvider(mdps.back().get(), stopped_thread->task_runner(),
+                         kDefaultOptions, kWhitelistedMDPName);
+  }
+  stopped_thread->Stop();
+
+  EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
+                                        MemoryDumpLevelOfDetail::DETAILED));
+  EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
+                                        MemoryDumpLevelOfDetail::BACKGROUND));
+  EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::SUMMARY_ONLY,
+                                        MemoryDumpLevelOfDetail::BACKGROUND));
+}
+
+}  // namespace trace_event
+}  // namespace base
diff --git a/base/trace_event/memory_dump_provider.h b/base/trace_event/memory_dump_provider.h
new file mode 100644
index 0000000..f55e2cf
--- /dev/null
+++ b/base/trace_event/memory_dump_provider.h
@@ -0,0 +1,52 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TRACE_EVENT_MEMORY_DUMP_PROVIDER_H_
+#define BASE_TRACE_EVENT_MEMORY_DUMP_PROVIDER_H_
+
+#include "base/base_export.h"
+#include "base/macros.h"
+#include "base/process/process_handle.h"
+#include "base/trace_event/memory_dump_request_args.h"
+
+namespace base {
+namespace trace_event {
+
+class ProcessMemoryDump;
+
+// The contract interface that memory dump providers must implement.
+class BASE_EXPORT MemoryDumpProvider {
+ public:
+  // Optional arguments for MemoryDumpManager::RegisterDumpProvider().
+  struct Options {
+    Options() : dumps_on_single_thread_task_runner(false) {}
+
+    // |dumps_on_single_thread_task_runner| is true if the dump provider runs on
+    // a SingleThreadTaskRunner, which is usually the case. It is faster to run
+    // all providers that run on the same thread together without thread hops.
+    bool dumps_on_single_thread_task_runner;
+  };
+
+  virtual ~MemoryDumpProvider() = default;
+
+  // Called by the MemoryDumpManager when generating memory dumps.
+  // The |args| specify if the embedder should generate light/heavy dumps on
+  // dump requests. The embedder should return true if the |pmd| was
+  // successfully populated, false if something went wrong and the dump should
+  // be considered invalid.
+  // (Note, the MemoryDumpManager has a fail-safe logic which will disable the
+  // MemoryDumpProvider for the entire trace session if it fails consistently).
+  virtual bool OnMemoryDump(const MemoryDumpArgs& args,
+                            ProcessMemoryDump* pmd) = 0;
+
+ protected:
+  MemoryDumpProvider() = default;
+
+  DISALLOW_COPY_AND_ASSIGN(MemoryDumpProvider);
+};
+
+}  // namespace trace_event
+}  // namespace base
+
+#endif  // BASE_TRACE_EVENT_MEMORY_DUMP_PROVIDER_H_
diff --git a/base/trace_event/memory_dump_provider_info.cc b/base/trace_event/memory_dump_provider_info.cc
new file mode 100644
index 0000000..3220476
--- /dev/null
+++ b/base/trace_event/memory_dump_provider_info.cc
@@ -0,0 +1,43 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/trace_event/memory_dump_provider_info.h"
+
+#include <tuple>
+
+#include "base/sequenced_task_runner.h"
+
+namespace base {
+namespace trace_event {
+
+MemoryDumpProviderInfo::MemoryDumpProviderInfo(
+    MemoryDumpProvider* dump_provider,
+    const char* name,
+    scoped_refptr<SequencedTaskRunner> task_runner,
+    const MemoryDumpProvider::Options& options,
+    bool whitelisted_for_background_mode)
+    : dump_provider(dump_provider),
+      options(options),
+      name(name),
+      task_runner(std::move(task_runner)),
+      whitelisted_for_background_mode(whitelisted_for_background_mode),
+      consecutive_failures(0),
+      disabled(false) {}
+
+MemoryDumpProviderInfo::~MemoryDumpProviderInfo() = default;
+
+bool MemoryDumpProviderInfo::Comparator::operator()(
+    const scoped_refptr<MemoryDumpProviderInfo>& a,
+    const scoped_refptr<MemoryDumpProviderInfo>& b) const {
+  if (!a || !b)
+    return a.get() < b.get();
+  // Ensure that unbound providers (task_runner == nullptr) always run last.
+  // Rationale: some unbound dump providers are known to be slow, keep them last
+  // to avoid skewing timings of the other dump providers.
+  return std::tie(a->task_runner, a->dump_provider) >
+         std::tie(b->task_runner, b->dump_provider);
+}
+
+}  // namespace trace_event
+}  // namespace base
diff --git a/base/trace_event/memory_dump_provider_info.h b/base/trace_event/memory_dump_provider_info.h
new file mode 100644
index 0000000..f0ea1e6
--- /dev/null
+++ b/base/trace_event/memory_dump_provider_info.h
@@ -0,0 +1,108 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TRACE_EVENT_MEMORY_DUMP_PROVIDER_INFO_H_
+#define BASE_TRACE_EVENT_MEMORY_DUMP_PROVIDER_INFO_H_
+
+#include <memory>
+#include <set>
+
+#include "base/base_export.h"
+#include "base/memory/ref_counted.h"
+#include "base/trace_event/memory_dump_provider.h"
+
+namespace base {
+
+class SequencedTaskRunner;
+
+namespace trace_event {
+
+// Wraps a MemoryDumpProvider (MDP), which is registered via
+// MemoryDumpManager(MDM)::RegisterDumpProvider(), holding the extra information
+// required to deal with it (which task runner it should be invoked onto,
+// whether it has been disabled, etc.)
+// More importantly, having a refptr to this object guarantees that a MDP that
+// is not thread-bound (hence which can only be unregistered via
+// MDM::UnregisterAndDeleteDumpProviderSoon()) will stay alive as long as the
+// refptr is held.
+//
+// Lifetime:
+// At any time, there is at most one instance of this class for each instance
+// of a given MemoryDumpProvider, but there might be several scoped_refptr
+// holding onto each of this. Specifically:
+// - In nominal conditions, there is a refptr for each registered MDP in the
+//   MDM's |dump_providers_| list.
+// - In most cases, the only refptr (in the |dump_providers_| list) is destroyed
+//   by MDM::UnregisterDumpProvider().
+// - However, when MDM starts a dump, the list of refptrs is copied into the
+//   ProcessMemoryDumpAsyncState. That list is pruned as MDP(s) are invoked.
+// - If UnregisterDumpProvider() is called on a non-thread-bound MDP while a
+//   dump is in progress, the extar extra of the handle is destroyed in
+//   MDM::SetupNextMemoryDump() or MDM::InvokeOnMemoryDump(), when the copy
+//   inside ProcessMemoryDumpAsyncState is erase()-d.
+// - The PeakDetector can keep extra refptrs when enabled.
+struct BASE_EXPORT MemoryDumpProviderInfo
+    : public RefCountedThreadSafe<MemoryDumpProviderInfo> {
+ public:
+  // Define a total order based on the |task_runner| affinity, so that MDPs
+  // belonging to the same SequencedTaskRunner are adjacent in the set.
+  struct Comparator {
+    bool operator()(const scoped_refptr<MemoryDumpProviderInfo>& a,
+                    const scoped_refptr<MemoryDumpProviderInfo>& b) const;
+  };
+  using OrderedSet =
+      std::set<scoped_refptr<MemoryDumpProviderInfo>, Comparator>;
+
+  MemoryDumpProviderInfo(MemoryDumpProvider* dump_provider,
+                         const char* name,
+                         scoped_refptr<SequencedTaskRunner> task_runner,
+                         const MemoryDumpProvider::Options& options,
+                         bool whitelisted_for_background_mode);
+
+  // It is safe to access the const fields below from any thread as they are
+  // never mutated.
+
+  MemoryDumpProvider* const dump_provider;
+
+  // The |options| arg passed to MDM::RegisterDumpProvider().
+  const MemoryDumpProvider::Options options;
+
+  // Human readable name, not unique (distinct MDP instances might have the same
+  // name). Used for debugging, testing and whitelisting for BACKGROUND mode.
+  const char* const name;
+
+  // The task runner on which the MDP::OnMemoryDump call should be posted onto.
+  // Can be nullptr, in which case the MDP will be invoked on a background
+  // thread handled by MDM.
+  const scoped_refptr<SequencedTaskRunner> task_runner;
+
+  // True if the dump provider is whitelisted for background mode.
+  const bool whitelisted_for_background_mode;
+
+  // These fields below, instead, are not thread safe and can be mutated only:
+  // - On the |task_runner|, when not null (i.e. for thread-bound MDPS).
+  // - By the MDM's background thread (or in any other way that guarantees
+  //   sequencing) for non-thread-bound MDPs.
+
+  // Used to transfer ownership for UnregisterAndDeleteDumpProviderSoon().
+  // nullptr in all other cases.
+  std::unique_ptr<MemoryDumpProvider> owned_dump_provider;
+
+  // For fail-safe logic (auto-disable failing MDPs).
+  int consecutive_failures;
+
+  // Flagged either by the auto-disable logic or during unregistration.
+  bool disabled;
+
+ private:
+  friend class base::RefCountedThreadSafe<MemoryDumpProviderInfo>;
+  ~MemoryDumpProviderInfo();
+
+  DISALLOW_COPY_AND_ASSIGN(MemoryDumpProviderInfo);
+};
+
+}  // namespace trace_event
+}  // namespace base
+
+#endif  // BASE_TRACE_EVENT_MEMORY_DUMP_PROVIDER_INFO_H_
diff --git a/base/trace_event/memory_dump_request_args.cc b/base/trace_event/memory_dump_request_args.cc
new file mode 100644
index 0000000..8be3c32
--- /dev/null
+++ b/base/trace_event/memory_dump_request_args.cc
@@ -0,0 +1,64 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/trace_event/memory_dump_request_args.h"
+
+#include "base/logging.h"
+
+namespace base {
+namespace trace_event {
+
+// static
+const char* MemoryDumpTypeToString(const MemoryDumpType& dump_type) {
+  switch (dump_type) {
+    case MemoryDumpType::PERIODIC_INTERVAL:
+      return "periodic_interval";
+    case MemoryDumpType::EXPLICITLY_TRIGGERED:
+      return "explicitly_triggered";
+    case MemoryDumpType::SUMMARY_ONLY:
+      return "summary_only";
+  }
+  NOTREACHED();
+  return "unknown";
+}
+
+MemoryDumpType StringToMemoryDumpType(const std::string& str) {
+  if (str == "periodic_interval")
+    return MemoryDumpType::PERIODIC_INTERVAL;
+  if (str == "explicitly_triggered")
+    return MemoryDumpType::EXPLICITLY_TRIGGERED;
+  if (str == "summary_only")
+    return MemoryDumpType::SUMMARY_ONLY;
+  NOTREACHED();
+  return MemoryDumpType::LAST;
+}
+
+const char* MemoryDumpLevelOfDetailToString(
+    const MemoryDumpLevelOfDetail& level_of_detail) {
+  switch (level_of_detail) {
+    case MemoryDumpLevelOfDetail::BACKGROUND:
+      return "background";
+    case MemoryDumpLevelOfDetail::LIGHT:
+      return "light";
+    case MemoryDumpLevelOfDetail::DETAILED:
+      return "detailed";
+  }
+  NOTREACHED();
+  return "unknown";
+}
+
+MemoryDumpLevelOfDetail StringToMemoryDumpLevelOfDetail(
+    const std::string& str) {
+  if (str == "background")
+    return MemoryDumpLevelOfDetail::BACKGROUND;
+  if (str == "light")
+    return MemoryDumpLevelOfDetail::LIGHT;
+  if (str == "detailed")
+    return MemoryDumpLevelOfDetail::DETAILED;
+  NOTREACHED();
+  return MemoryDumpLevelOfDetail::LAST;
+}
+
+}  // namespace trace_event
+}  // namespace base
diff --git a/base/trace_event/memory_dump_request_args.h b/base/trace_event/memory_dump_request_args.h
new file mode 100644
index 0000000..f854a4b
--- /dev/null
+++ b/base/trace_event/memory_dump_request_args.h
@@ -0,0 +1,101 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TRACE_EVENT_MEMORY_DUMP_REQUEST_ARGS_H_
+#define BASE_TRACE_EVENT_MEMORY_DUMP_REQUEST_ARGS_H_
+
+// This file defines the types and structs used to issue memory dump requests.
+// These are also used in the IPCs for coordinating inter-process memory dumps.
+
+#include <stdint.h>
+#include <map>
+#include <memory>
+#include <string>
+
+#include "base/base_export.h"
+#include "base/callback.h"
+#include "base/optional.h"
+#include "base/process/process_handle.h"
+
+namespace base {
+namespace trace_event {
+
+class ProcessMemoryDump;
+
+// Captures the reason why a memory dump is being requested. This is to allow
+// selective enabling of dumps, filtering and post-processing. Keep this
+// consistent with memory_instrumentation.mojo and
+// memory_instrumentation_struct_traits.{h,cc}
+enum class MemoryDumpType {
+  PERIODIC_INTERVAL,     // Dumping memory at periodic intervals.
+  EXPLICITLY_TRIGGERED,  // Non maskable dump request.
+  SUMMARY_ONLY,          // Calculate just the summary & don't add to the trace.
+  LAST = SUMMARY_ONLY
+};
+
+// Tells the MemoryDumpProvider(s) how much detailed their dumps should be.
+// Keep this consistent with memory_instrumentation.mojo and
+// memory_instrumentation_struct_traits.{h,cc}
+enum class MemoryDumpLevelOfDetail : uint32_t {
+  FIRST,
+
+  // For background tracing mode. The dump time is quick, and typically just the
+  // totals are expected. Suballocations need not be specified. Dump name must
+  // contain only pre-defined strings and string arguments cannot be added.
+  BACKGROUND = FIRST,
+
+  // For the levels below, MemoryDumpProvider instances must guarantee that the
+  // total size reported in the root node is consistent. Only the granularity of
+  // the child MemoryAllocatorDump(s) differs with the levels.
+
+  // Few entries, typically a fixed number, per dump.
+  LIGHT,
+
+  // Unrestricted amount of entries per dump.
+  DETAILED,
+
+  LAST = DETAILED
+};
+
+// Keep this consistent with memory_instrumentation.mojo and
+// memory_instrumentation_struct_traits.{h,cc}
+struct BASE_EXPORT MemoryDumpRequestArgs {
+  // Globally unique identifier. In multi-process dumps, all processes issue a
+  // local dump with the same guid. This allows the trace importers to
+  // reconstruct the global dump.
+  uint64_t dump_guid;
+
+  MemoryDumpType dump_type;
+  MemoryDumpLevelOfDetail level_of_detail;
+};
+
+// Args for ProcessMemoryDump and passed to OnMemoryDump calls for memory dump
+// providers. Dump providers are expected to read the args for creating dumps.
+struct MemoryDumpArgs {
+  // Specifies how detailed the dumps should be.
+  MemoryDumpLevelOfDetail level_of_detail;
+
+  // Globally unique identifier. In multi-process dumps, all processes issue a
+  // local dump with the same guid. This allows the trace importers to
+  // reconstruct the global dump.
+  uint64_t dump_guid;
+};
+
+using ProcessMemoryDumpCallback = Callback<
+    void(bool success, uint64_t dump_guid, std::unique_ptr<ProcessMemoryDump>)>;
+
+BASE_EXPORT const char* MemoryDumpTypeToString(const MemoryDumpType& dump_type);
+
+BASE_EXPORT MemoryDumpType StringToMemoryDumpType(const std::string& str);
+
+BASE_EXPORT const char* MemoryDumpLevelOfDetailToString(
+    const MemoryDumpLevelOfDetail& level_of_detail);
+
+BASE_EXPORT MemoryDumpLevelOfDetail
+StringToMemoryDumpLevelOfDetail(const std::string& str);
+
+}  // namespace trace_event
+}  // namespace base
+
+#endif  // BASE_TRACE_EVENT_MEMORY_DUMP_REQUEST_ARGS_H_
diff --git a/base/trace_event/memory_dump_scheduler.cc b/base/trace_event/memory_dump_scheduler.cc
new file mode 100644
index 0000000..8b03f5c
--- /dev/null
+++ b/base/trace_event/memory_dump_scheduler.cc
@@ -0,0 +1,118 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/trace_event/memory_dump_scheduler.h"
+
+#include <algorithm>
+#include <limits>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+
+namespace base {
+namespace trace_event {
+
+// static
+MemoryDumpScheduler* MemoryDumpScheduler::GetInstance() {
+  static MemoryDumpScheduler* instance = new MemoryDumpScheduler();
+  return instance;
+}
+
+MemoryDumpScheduler::MemoryDumpScheduler() : period_ms_(0), generation_(0) {}
+MemoryDumpScheduler::~MemoryDumpScheduler() {
+  // Hit only in tests. Check that tests don't leave without stopping.
+  DCHECK(!is_enabled_for_testing());
+}
+
+void MemoryDumpScheduler::Start(
+    MemoryDumpScheduler::Config config,
+    scoped_refptr<SequencedTaskRunner> task_runner) {
+  DCHECK(!task_runner_);
+  task_runner_ = task_runner;
+  task_runner->PostTask(FROM_HERE, BindOnce(&MemoryDumpScheduler::StartInternal,
+                                            Unretained(this), config));
+}
+
+void MemoryDumpScheduler::Stop() {
+  if (!task_runner_)
+    return;
+  task_runner_->PostTask(FROM_HERE, BindOnce(&MemoryDumpScheduler::StopInternal,
+                                             Unretained(this)));
+  task_runner_ = nullptr;
+}
+
+void MemoryDumpScheduler::StartInternal(MemoryDumpScheduler::Config config) {
+  uint32_t light_dump_period_ms = 0;
+  uint32_t heavy_dump_period_ms = 0;
+  uint32_t min_period_ms = std::numeric_limits<uint32_t>::max();
+  for (const Config::Trigger& trigger : config.triggers) {
+    DCHECK_GT(trigger.period_ms, 0u);
+    switch (trigger.level_of_detail) {
+      case MemoryDumpLevelOfDetail::BACKGROUND:
+        break;
+      case MemoryDumpLevelOfDetail::LIGHT:
+        DCHECK_EQ(0u, light_dump_period_ms);
+        light_dump_period_ms = trigger.period_ms;
+        break;
+      case MemoryDumpLevelOfDetail::DETAILED:
+        DCHECK_EQ(0u, heavy_dump_period_ms);
+        heavy_dump_period_ms = trigger.period_ms;
+        break;
+    }
+    min_period_ms = std::min(min_period_ms, trigger.period_ms);
+  }
+
+  DCHECK_EQ(0u, light_dump_period_ms % min_period_ms);
+  DCHECK_EQ(0u, heavy_dump_period_ms % min_period_ms);
+  DCHECK(!config.callback.is_null());
+  callback_ = config.callback;
+  period_ms_ = min_period_ms;
+  tick_count_ = 0;
+  light_dump_rate_ = light_dump_period_ms / min_period_ms;
+  heavy_dump_rate_ = heavy_dump_period_ms / min_period_ms;
+
+  // Trigger the first dump after 200ms.
+  // TODO(lalitm): this is a tempoarary hack to delay the first scheduled dump
+  // so that the child processes get tracing enabled notification via IPC.
+  // See crbug.com/770151.
+  SequencedTaskRunnerHandle::Get()->PostDelayedTask(
+      FROM_HERE,
+      BindOnce(&MemoryDumpScheduler::Tick, Unretained(this), ++generation_),
+      TimeDelta::FromMilliseconds(200));
+}
+
+void MemoryDumpScheduler::StopInternal() {
+  period_ms_ = 0;
+  generation_++;
+  callback_.Reset();
+}
+
+void MemoryDumpScheduler::Tick(uint32_t expected_generation) {
+  if (period_ms_ == 0 || generation_ != expected_generation)
+    return;
+
+  MemoryDumpLevelOfDetail level_of_detail = MemoryDumpLevelOfDetail::BACKGROUND;
+  if (light_dump_rate_ > 0 && tick_count_ % light_dump_rate_ == 0)
+    level_of_detail = MemoryDumpLevelOfDetail::LIGHT;
+  if (heavy_dump_rate_ > 0 && tick_count_ % heavy_dump_rate_ == 0)
+    level_of_detail = MemoryDumpLevelOfDetail::DETAILED;
+  tick_count_++;
+
+  callback_.Run(level_of_detail);
+
+  SequencedTaskRunnerHandle::Get()->PostDelayedTask(
+      FROM_HERE,
+      BindOnce(&MemoryDumpScheduler::Tick, Unretained(this),
+               expected_generation),
+      TimeDelta::FromMilliseconds(period_ms_));
+}
+
+MemoryDumpScheduler::Config::Config() = default;
+MemoryDumpScheduler::Config::~Config() = default;
+MemoryDumpScheduler::Config::Config(const MemoryDumpScheduler::Config&) =
+    default;
+
+}  // namespace trace_event
+}  // namespace base
diff --git a/base/trace_event/memory_dump_scheduler.h b/base/trace_event/memory_dump_scheduler.h
new file mode 100644
index 0000000..21334f0
--- /dev/null
+++ b/base/trace_event/memory_dump_scheduler.h
@@ -0,0 +1,76 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TRACE_EVENT_MEMORY_DUMP_SCHEDULER_H
+#define BASE_TRACE_EVENT_MEMORY_DUMP_SCHEDULER_H
+
+#include <stdint.h>
+
+#include <vector>
+
+#include "base/base_export.h"
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+#include "base/trace_event/memory_dump_request_args.h"
+
+namespace base {
+class SequencedTaskRunner;
+
+namespace trace_event {
+
+// Schedules global dump requests based on the triggers added. The methods of
+// this class are NOT thread safe and the client has to take care of invoking
+// all the methods of the class safely.
+class BASE_EXPORT MemoryDumpScheduler {
+ public:
+  using PeriodicCallback = RepeatingCallback<void(MemoryDumpLevelOfDetail)>;
+
+  // Passed to Start().
+  struct BASE_EXPORT Config {
+    struct Trigger {
+      MemoryDumpLevelOfDetail level_of_detail;
+      uint32_t period_ms;
+    };
+
+    Config();
+    Config(const Config&);
+    ~Config();
+
+    std::vector<Trigger> triggers;
+    PeriodicCallback callback;
+  };
+
+  static MemoryDumpScheduler* GetInstance();
+
+  void Start(Config, scoped_refptr<SequencedTaskRunner> task_runner);
+  void Stop();
+  bool is_enabled_for_testing() const { return bool(task_runner_); }
+
+ private:
+  friend class MemoryDumpSchedulerTest;
+  MemoryDumpScheduler();
+  ~MemoryDumpScheduler();
+
+  void StartInternal(Config);
+  void StopInternal();
+  void Tick(uint32_t expected_generation);
+
+  // Accessed only by the public methods (never from the task runner itself).
+  scoped_refptr<SequencedTaskRunner> task_runner_;
+
+  // These fields instead are only accessed from within the task runner.
+  uint32_t period_ms_;   // 0 == disabled.
+  uint32_t generation_;  // Used to invalidate outstanding tasks after Stop().
+  uint32_t tick_count_;
+  uint32_t light_dump_rate_;
+  uint32_t heavy_dump_rate_;
+  PeriodicCallback callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(MemoryDumpScheduler);
+};
+
+}  // namespace trace_event
+}  // namespace base
+
+#endif  // BASE_TRACE_EVENT_MEMORY_DUMP_SCHEDULER_H
diff --git a/base/trace_event/memory_dump_scheduler_unittest.cc b/base/trace_event/memory_dump_scheduler_unittest.cc
new file mode 100644
index 0000000..d5993b6
--- /dev/null
+++ b/base/trace_event/memory_dump_scheduler_unittest.cc
@@ -0,0 +1,200 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/trace_event/memory_dump_scheduler.h"
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/single_thread_task_runner.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::AtMost;
+using ::testing::Invoke;
+using ::testing::_;
+
+namespace base {
+namespace trace_event {
+
+namespace {
+
+// Wrapper to use gmock on a callback.
+struct CallbackWrapper {
+  MOCK_METHOD1(OnTick, void(MemoryDumpLevelOfDetail));
+};
+
+}  // namespace
+
+class MemoryDumpSchedulerTest : public testing::Test {
+ public:
+  MemoryDumpSchedulerTest()
+      : testing::Test(),
+        evt_(WaitableEvent::ResetPolicy::MANUAL,
+             WaitableEvent::InitialState::NOT_SIGNALED),
+        bg_thread_("MemoryDumpSchedulerTest Thread") {
+    bg_thread_.Start();
+  }
+
+ protected:
+  MemoryDumpScheduler scheduler_;
+  WaitableEvent evt_;
+  CallbackWrapper on_tick_;
+  Thread bg_thread_;
+};
+
+TEST_F(MemoryDumpSchedulerTest, SingleTrigger) {
+  const uint32_t kPeriodMs = 1;
+  const auto kLevelOfDetail = MemoryDumpLevelOfDetail::DETAILED;
+  const uint32_t kTicks = 5;
+  MemoryDumpScheduler::Config config;
+  config.triggers.push_back({kLevelOfDetail, kPeriodMs});
+  config.callback = Bind(&CallbackWrapper::OnTick, Unretained(&on_tick_));
+
+  testing::InSequence sequence;
+  EXPECT_CALL(on_tick_, OnTick(_)).Times(kTicks - 1);
+  EXPECT_CALL(on_tick_, OnTick(_))
+      .WillRepeatedly(Invoke(
+          [this, kLevelOfDetail](MemoryDumpLevelOfDetail level_of_detail) {
+            EXPECT_EQ(kLevelOfDetail, level_of_detail);
+            this->evt_.Signal();
+          }));
+
+  // Check that Stop() before Start() doesn't cause any error.
+  scheduler_.Stop();
+
+  const TimeTicks tstart = TimeTicks::Now();
+  scheduler_.Start(config, bg_thread_.task_runner());
+  evt_.Wait();
+  const double time_ms = (TimeTicks::Now() - tstart).InMillisecondsF();
+
+  // It takes N-1 ms to perform N ticks of 1ms each.
+  EXPECT_GE(time_ms, kPeriodMs * (kTicks - 1));
+
+  // Check that stopping twice doesn't cause any problems.
+  scheduler_.Stop();
+  scheduler_.Stop();
+}
+
+TEST_F(MemoryDumpSchedulerTest, MultipleTriggers) {
+  const uint32_t kPeriodLightMs = 3;
+  const uint32_t kPeriodDetailedMs = 9;
+  MemoryDumpScheduler::Config config;
+  const MemoryDumpLevelOfDetail kLight = MemoryDumpLevelOfDetail::LIGHT;
+  const MemoryDumpLevelOfDetail kDetailed = MemoryDumpLevelOfDetail::DETAILED;
+  config.triggers.push_back({kLight, kPeriodLightMs});
+  config.triggers.push_back({kDetailed, kPeriodDetailedMs});
+  config.callback = Bind(&CallbackWrapper::OnTick, Unretained(&on_tick_));
+
+  TimeTicks t1, t2, t3;
+
+  testing::InSequence sequence;
+  EXPECT_CALL(on_tick_, OnTick(kDetailed))
+      .WillOnce(
+          Invoke([&t1](MemoryDumpLevelOfDetail) { t1 = TimeTicks::Now(); }));
+  EXPECT_CALL(on_tick_, OnTick(kLight)).Times(1);
+  EXPECT_CALL(on_tick_, OnTick(kLight)).Times(1);
+  EXPECT_CALL(on_tick_, OnTick(kDetailed))
+      .WillOnce(
+          Invoke([&t2](MemoryDumpLevelOfDetail) { t2 = TimeTicks::Now(); }));
+  EXPECT_CALL(on_tick_, OnTick(kLight))
+      .WillOnce(
+          Invoke([&t3](MemoryDumpLevelOfDetail) { t3 = TimeTicks::Now(); }));
+
+  // Rationale for WillRepeatedly and not just WillOnce: Extra ticks might
+  // happen if the Stop() takes time. Not an interesting case, but we need to
+  // avoid gmock to shout in that case.
+  EXPECT_CALL(on_tick_, OnTick(_))
+      .WillRepeatedly(
+          Invoke([this](MemoryDumpLevelOfDetail) { this->evt_.Signal(); }));
+
+  scheduler_.Start(config, bg_thread_.task_runner());
+  evt_.Wait();
+  scheduler_.Stop();
+  EXPECT_GE((t2 - t1).InMillisecondsF(), kPeriodDetailedMs);
+  EXPECT_GE((t3 - t2).InMillisecondsF(), kPeriodLightMs);
+}
+
+TEST_F(MemoryDumpSchedulerTest, StartStopQuickly) {
+  const uint32_t kPeriodMs = 3;
+  const uint32_t kQuickIterations = 5;
+  const uint32_t kDetailedTicks = 10;
+
+  MemoryDumpScheduler::Config light_config;
+  light_config.triggers.push_back({MemoryDumpLevelOfDetail::LIGHT, kPeriodMs});
+  light_config.callback = Bind(&CallbackWrapper::OnTick, Unretained(&on_tick_));
+
+  MemoryDumpScheduler::Config detailed_config;
+  detailed_config.triggers.push_back(
+      {MemoryDumpLevelOfDetail::DETAILED, kPeriodMs});
+  detailed_config.callback =
+      Bind(&CallbackWrapper::OnTick, Unretained(&on_tick_));
+
+  testing::InSequence sequence;
+  EXPECT_CALL(on_tick_, OnTick(MemoryDumpLevelOfDetail::LIGHT))
+      .Times(AtMost(kQuickIterations));
+  EXPECT_CALL(on_tick_, OnTick(MemoryDumpLevelOfDetail::DETAILED))
+      .Times(kDetailedTicks - 1);
+  EXPECT_CALL(on_tick_, OnTick(MemoryDumpLevelOfDetail::DETAILED))
+      .WillRepeatedly(
+          Invoke([this](MemoryDumpLevelOfDetail) { this->evt_.Signal(); }));
+
+  const TimeTicks tstart = TimeTicks::Now();
+  for (unsigned int i = 0; i < kQuickIterations; i++) {
+    scheduler_.Start(light_config, bg_thread_.task_runner());
+    scheduler_.Stop();
+  }
+
+  scheduler_.Start(detailed_config, bg_thread_.task_runner());
+
+  evt_.Wait();
+  const double time_ms = (TimeTicks::Now() - tstart).InMillisecondsF();
+  scheduler_.Stop();
+
+  // It takes N-1 ms to perform N ticks of 1ms each.
+  EXPECT_GE(time_ms, kPeriodMs * (kDetailedTicks - 1));
+}
+
+TEST_F(MemoryDumpSchedulerTest, StopAndStartOnAnotherThread) {
+  const uint32_t kPeriodMs = 1;
+  const uint32_t kTicks = 3;
+  MemoryDumpScheduler::Config config;
+  config.triggers.push_back({MemoryDumpLevelOfDetail::DETAILED, kPeriodMs});
+  config.callback = Bind(&CallbackWrapper::OnTick, Unretained(&on_tick_));
+
+  scoped_refptr<TaskRunner> expected_task_runner = bg_thread_.task_runner();
+  testing::InSequence sequence;
+  EXPECT_CALL(on_tick_, OnTick(_)).Times(kTicks - 1);
+  EXPECT_CALL(on_tick_, OnTick(_))
+      .WillRepeatedly(
+          Invoke([this, expected_task_runner](MemoryDumpLevelOfDetail) {
+            EXPECT_TRUE(expected_task_runner->RunsTasksInCurrentSequence());
+            this->evt_.Signal();
+          }));
+
+  scheduler_.Start(config, bg_thread_.task_runner());
+  evt_.Wait();
+  scheduler_.Stop();
+  bg_thread_.Stop();
+
+  Thread bg_thread_2("MemoryDumpSchedulerTest Thread 2");
+  bg_thread_2.Start();
+  evt_.Reset();
+  expected_task_runner = bg_thread_2.task_runner();
+  EXPECT_CALL(on_tick_, OnTick(_)).Times(kTicks - 1);
+  EXPECT_CALL(on_tick_, OnTick(_))
+      .WillRepeatedly(
+          Invoke([this, expected_task_runner](MemoryDumpLevelOfDetail) {
+            EXPECT_TRUE(expected_task_runner->RunsTasksInCurrentSequence());
+            this->evt_.Signal();
+          }));
+  scheduler_.Start(config, bg_thread_2.task_runner());
+  evt_.Wait();
+  scheduler_.Stop();
+}
+
+}  // namespace trace_event
+}  // namespace base
diff --git a/base/trace_event/memory_infra_background_whitelist.cc b/base/trace_event/memory_infra_background_whitelist.cc
new file mode 100644
index 0000000..40f5ac8
--- /dev/null
+++ b/base/trace_event/memory_infra_background_whitelist.cc
@@ -0,0 +1,392 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/trace_event/memory_infra_background_whitelist.h"
+
+#include <ctype.h>
+#include <string.h>
+
+#include <string>
+
+#include "base/strings/string_util.h"
+
+namespace base {
+namespace trace_event {
+namespace {
+
+// The names of dump providers whitelisted for background tracing. Dump
+// providers can be added here only if the background mode dump has very
+// little processor and memory overhead.
+// TODO(ssid): Some dump providers do not create ownership edges on background
+// dump. So, the effective size will not be correct.
+const char* const kDumpProviderWhitelist[] = {
+    "android::ResourceManagerImpl",
+    "AutocompleteController",
+    "BlinkGC",
+    "BlinkObjectCounters",
+    "BlobStorageContext",
+    "ClientDiscardableSharedMemoryManager",
+    "DOMStorage",
+    "DownloadService",
+    "DiscardableSharedMemoryManager",
+    "gpu::BufferManager",
+    "gpu::RenderbufferManager",
+    "gpu::TextureManager",
+    "FontCaches",
+    "HistoryReport",
+    "IPCChannel",
+    "IndexedDBBackingStore",
+    "InMemoryURLIndex",
+    "JavaHeap",
+    "LevelDB",
+    "LeveldbValueStore",
+    "LocalStorage",
+    "Malloc",
+    "MemoryCache",
+    "MojoHandleTable",
+    "MojoLevelDB",
+    "MojoMessages",
+    "PartitionAlloc",
+    "ProcessMemoryMetrics",
+    "RenderProcessHost",
+    "SharedMemoryTracker",
+    "Skia",
+    "Sql",
+    "URLRequestContext",
+    "V8Isolate",
+    "SyncDirectory",
+    "TabRestoreServiceHelper",
+    nullptr  // End of list marker.
+};
+
+// A list of string names that are allowed for the memory allocator dumps in
+// background mode.
+const char* const kAllocatorDumpNameWhitelist[] = {
+    "blink_gc",
+    "blink_gc/allocated_objects",
+    "blink_objects/AdSubframe",
+    "blink_objects/AudioHandler",
+    "blink_objects/DetachedScriptState",
+    "blink_objects/Document",
+    "blink_objects/Frame",
+    "blink_objects/JSEventListener",
+    "blink_objects/LayoutObject",
+    "blink_objects/MediaKeySession",
+    "blink_objects/MediaKeys",
+    "blink_objects/Node",
+    "blink_objects/Resource",
+    "blink_objects/RTCPeerConnection",
+    "blink_objects/ScriptPromise",
+    "blink_objects/PausableObject",
+    "blink_objects/V8PerContextData",
+    "blink_objects/WorkerGlobalScope",
+    "blink_objects/UACSSResource",
+    "blink_objects/ResourceFetcher",
+    "components/download/controller_0x?",
+    "discardable",
+    "discardable/child_0x?",
+    "extensions/value_store/Extensions.Database.Open.Settings/0x?",
+    "extensions/value_store/Extensions.Database.Open.Rules/0x?",
+    "extensions/value_store/Extensions.Database.Open.State/0x?",
+    "extensions/value_store/Extensions.Database.Open/0x?",
+    "extensions/value_store/Extensions.Database.Restore/0x?",
+    "extensions/value_store/Extensions.Database.Value.Restore/0x?",
+    "font_caches/font_platform_data_cache",
+    "font_caches/shape_caches",
+    "gpu/gl/buffers/share_group_0x?",
+    "gpu/gl/renderbuffers/share_group_0x?",
+    "gpu/gl/textures/share_group_0x?",
+    "history/delta_file_service/leveldb_0x?",
+    "history/usage_reports_buffer/leveldb_0x?",
+    "java_heap",
+    "java_heap/allocated_objects",
+    "leveldatabase",
+    "leveldatabase/block_cache/browser",
+    "leveldatabase/block_cache/in_memory",
+    "leveldatabase/block_cache/unified",
+    "leveldatabase/block_cache/web",
+    "leveldatabase/db_0x?",
+    "leveldatabase/db_0x?/block_cache",
+    "leveldatabase/memenv_0x?",
+    "malloc",
+    "malloc/allocated_objects",
+    "malloc/metadata_fragmentation_caches",
+    "mojo",
+    "mojo/data_pipe_consumer",
+    "mojo/data_pipe_producer",
+    "mojo/invitation",
+    "mojo/messages",
+    "mojo/message_pipe",
+    "mojo/platform_handle",
+    "mojo/queued_ipc_channel_message/0x?",
+    "mojo/render_process_host/0x?",
+    "mojo/shared_buffer",
+    "mojo/unknown",
+    "mojo/watcher",
+    "net/http_network_session_0x?",
+    "net/http_network_session_0x?/quic_stream_factory",
+    "net/http_network_session_0x?/socket_pool",
+    "net/http_network_session_0x?/spdy_session_pool",
+    "net/http_network_session_0x?/stream_factory",
+    "net/ssl_session_cache",
+    "net/url_request_context",
+    "net/url_request_context/app_request",
+    "net/url_request_context/app_request/0x?",
+    "net/url_request_context/app_request/0x?/cookie_monster",
+    "net/url_request_context/app_request/0x?/cookie_monster/cookies",
+    "net/url_request_context/app_request/0x?/cookie_monster/"
+    "tasks_pending_global",
+    "net/url_request_context/app_request/0x?/cookie_monster/"
+    "tasks_pending_for_key",
+    "net/url_request_context/app_request/0x?/http_cache",
+    "net/url_request_context/app_request/0x?/http_cache/memory_backend",
+    "net/url_request_context/app_request/0x?/http_cache/simple_backend",
+    "net/url_request_context/app_request/0x?/http_network_session",
+    "net/url_request_context/extensions",
+    "net/url_request_context/extensions/0x?",
+    "net/url_request_context/extensions/0x?/cookie_monster",
+    "net/url_request_context/extensions/0x?/cookie_monster/cookies",
+    "net/url_request_context/extensions/0x?/cookie_monster/"
+    "tasks_pending_global",
+    "net/url_request_context/extensions/0x?/cookie_monster/"
+    "tasks_pending_for_key",
+    "net/url_request_context/extensions/0x?/http_cache",
+    "net/url_request_context/extensions/0x?/http_cache/memory_backend",
+    "net/url_request_context/extensions/0x?/http_cache/simple_backend",
+    "net/url_request_context/extensions/0x?/http_network_session",
+    "net/url_request_context/isolated_media",
+    "net/url_request_context/isolated_media/0x?",
+    "net/url_request_context/isolated_media/0x?/cookie_monster",
+    "net/url_request_context/isolated_media/0x?/cookie_monster/cookies",
+    "net/url_request_context/isolated_media/0x?/cookie_monster/"
+    "tasks_pending_global",
+    "net/url_request_context/isolated_media/0x?/cookie_monster/"
+    "tasks_pending_for_key",
+    "net/url_request_context/isolated_media/0x?/http_cache",
+    "net/url_request_context/isolated_media/0x?/http_cache/memory_backend",
+    "net/url_request_context/isolated_media/0x?/http_cache/simple_backend",
+    "net/url_request_context/isolated_media/0x?/http_network_session",
+    "net/url_request_context/main",
+    "net/url_request_context/main/0x?",
+    "net/url_request_context/main/0x?/cookie_monster",
+    "net/url_request_context/main/0x?/cookie_monster/cookies",
+    "net/url_request_context/main/0x?/cookie_monster/tasks_pending_global",
+    "net/url_request_context/main/0x?/cookie_monster/tasks_pending_for_key",
+    "net/url_request_context/main/0x?/http_cache",
+    "net/url_request_context/main/0x?/http_cache/memory_backend",
+    "net/url_request_context/main/0x?/http_cache/simple_backend",
+    "net/url_request_context/main/0x?/http_network_session",
+    "net/url_request_context/main_media",
+    "net/url_request_context/main_media/0x?",
+    "net/url_request_context/main_media/0x?/cookie_monster",
+    "net/url_request_context/main_media/0x?/cookie_monster/cookies",
+    "net/url_request_context/main_media/0x?/cookie_monster/"
+    "tasks_pending_global",
+    "net/url_request_context/main_media/0x?/cookie_monster/"
+    "tasks_pending_for_key",
+    "net/url_request_context/main_media/0x?/http_cache",
+    "net/url_request_context/main_media/0x?/http_cache/memory_backend",
+    "net/url_request_context/main_media/0x?/http_cache/simple_backend",
+    "net/url_request_context/main_media/0x?/http_network_session",
+    "net/url_request_context/proxy",
+    "net/url_request_context/proxy/0x?",
+    "net/url_request_context/proxy/0x?/cookie_monster",
+    "net/url_request_context/proxy/0x?/cookie_monster/cookies",
+    "net/url_request_context/proxy/0x?/cookie_monster/tasks_pending_global",
+    "net/url_request_context/proxy/0x?/cookie_monster/tasks_pending_for_key",
+    "net/url_request_context/proxy/0x?/http_cache",
+    "net/url_request_context/proxy/0x?/http_cache/memory_backend",
+    "net/url_request_context/proxy/0x?/http_cache/simple_backend",
+    "net/url_request_context/proxy/0x?/http_network_session",
+    "net/url_request_context/safe_browsing",
+    "net/url_request_context/safe_browsing/0x?",
+    "net/url_request_context/safe_browsing/0x?/cookie_monster",
+    "net/url_request_context/safe_browsing/0x?/cookie_monster/cookies",
+    "net/url_request_context/safe_browsing/0x?/cookie_monster/"
+    "tasks_pending_global",
+    "net/url_request_context/safe_browsing/0x?/cookie_monster/"
+    "tasks_pending_for_key",
+    "net/url_request_context/safe_browsing/0x?/http_cache",
+    "net/url_request_context/safe_browsing/0x?/http_cache/memory_backend",
+    "net/url_request_context/safe_browsing/0x?/http_cache/simple_backend",
+    "net/url_request_context/safe_browsing/0x?/http_network_session",
+    "net/url_request_context/system",
+    "net/url_request_context/system/0x?",
+    "net/url_request_context/system/0x?/cookie_monster",
+    "net/url_request_context/system/0x?/cookie_monster/cookies",
+    "net/url_request_context/system/0x?/cookie_monster/tasks_pending_global",
+    "net/url_request_context/system/0x?/cookie_monster/tasks_pending_for_key",
+    "net/url_request_context/system/0x?/http_cache",
+    "net/url_request_context/system/0x?/http_cache/memory_backend",
+    "net/url_request_context/system/0x?/http_cache/simple_backend",
+    "net/url_request_context/system/0x?/http_network_session",
+    "net/url_request_context/unknown",
+    "net/url_request_context/unknown/0x?",
+    "net/url_request_context/unknown/0x?/cookie_monster",
+    "net/url_request_context/unknown/0x?/cookie_monster/cookies",
+    "net/url_request_context/unknown/0x?/cookie_monster/tasks_pending_global",
+    "net/url_request_context/unknown/0x?/cookie_monster/tasks_pending_for_key",
+    "net/url_request_context/unknown/0x?/http_cache",
+    "net/url_request_context/unknown/0x?/http_cache/memory_backend",
+    "net/url_request_context/unknown/0x?/http_cache/simple_backend",
+    "net/url_request_context/unknown/0x?/http_network_session",
+    "omnibox/autocomplete_controller/0x?",
+    "omnibox/in_memory_url_index/0x?",
+    "web_cache/Image_resources",
+    "web_cache/CSS stylesheet_resources",
+    "web_cache/Script_resources",
+    "web_cache/XSL stylesheet_resources",
+    "web_cache/Font_resources",
+    "web_cache/Other_resources",
+    "partition_alloc/allocated_objects",
+    "partition_alloc/partitions",
+    "partition_alloc/partitions/array_buffer",
+    "partition_alloc/partitions/buffer",
+    "partition_alloc/partitions/fast_malloc",
+    "partition_alloc/partitions/layout",
+    "skia/sk_glyph_cache",
+    "skia/sk_resource_cache",
+    "sqlite",
+    "ui/resource_manager_0x?/default_resource/0x?",
+    "ui/resource_manager_0x?/tinted_resource",
+    "v8/isolate_0x?/contexts/detached_context",
+    "v8/isolate_0x?/contexts/native_context",
+    "v8/isolate_0x?/heap_spaces",
+    "v8/isolate_0x?/heap_spaces/code_space",
+    "v8/isolate_0x?/heap_spaces/large_object_space",
+    "v8/isolate_0x?/heap_spaces/map_space",
+    "v8/isolate_0x?/heap_spaces/new_space",
+    "v8/isolate_0x?/heap_spaces/new_large_object_space",
+    "v8/isolate_0x?/heap_spaces/old_space",
+    "v8/isolate_0x?/heap_spaces/read_only_space",
+    "v8/isolate_0x?/malloc",
+    "v8/isolate_0x?/zapped_for_debug",
+    "site_storage/blob_storage/0x?",
+    "site_storage/index_db/db_0x?",
+    "site_storage/index_db/memenv_0x?",
+    "site_storage/localstorage/0x?/cache_size",
+    "site_storage/localstorage/0x?/leveldb",
+    "site_storage/session_storage/0x?",
+    "site_storage/session_storage/0x?/cache_size",
+    "sync/0x?/kernel",
+    "sync/0x?/store",
+    "sync/0x?/model_type/APP",
+    "sync/0x?/model_type/APP_LIST",
+    "sync/0x?/model_type/APP_NOTIFICATION",
+    "sync/0x?/model_type/APP_SETTING",
+    "sync/0x?/model_type/ARC_PACKAGE",
+    "sync/0x?/model_type/ARTICLE",
+    "sync/0x?/model_type/AUTOFILL",
+    "sync/0x?/model_type/AUTOFILL_PROFILE",
+    "sync/0x?/model_type/AUTOFILL_WALLET",
+    "sync/0x?/model_type/BOOKMARK",
+    "sync/0x?/model_type/DEVICE_INFO",
+    "sync/0x?/model_type/DICTIONARY",
+    "sync/0x?/model_type/EXPERIMENTS",
+    "sync/0x?/model_type/EXTENSION",
+    "sync/0x?/model_type/EXTENSION_SETTING",
+    "sync/0x?/model_type/FAVICON_IMAGE",
+    "sync/0x?/model_type/FAVICON_TRACKING",
+    "sync/0x?/model_type/HISTORY_DELETE_DIRECTIVE",
+    "sync/0x?/model_type/MANAGED_USER",
+    "sync/0x?/model_type/MANAGED_USER_SETTING",
+    "sync/0x?/model_type/MANAGED_USER_SHARED_SETTING",
+    "sync/0x?/model_type/MANAGED_USER_WHITELIST",
+    "sync/0x?/model_type/MOUNTAIN_SHARE",
+    "sync/0x?/model_type/NIGORI",
+    "sync/0x?/model_type/PASSWORD",
+    "sync/0x?/model_type/PREFERENCE",
+    "sync/0x?/model_type/PRINTER",
+    "sync/0x?/model_type/PRIORITY_PREFERENCE",
+    "sync/0x?/model_type/READING_LIST",
+    "sync/0x?/model_type/SEARCH_ENGINE",
+    "sync/0x?/model_type/SESSION",
+    "sync/0x?/model_type/SYNCED_NOTIFICATION",
+    "sync/0x?/model_type/SYNCED_NOTIFICATION_APP_INFO",
+    "sync/0x?/model_type/THEME",
+    "sync/0x?/model_type/TYPED_URL",
+    "sync/0x?/model_type/USER_CONSENT",
+    "sync/0x?/model_type/USER_EVENT",
+    "sync/0x?/model_type/WALLET_METADATA",
+    "sync/0x?/model_type/WIFI_CREDENTIAL",
+    "tab_restore/service_helper_0x?/entries",
+    "tab_restore/service_helper_0x?/entries/tab_0x?",
+    "tab_restore/service_helper_0x?/entries/window_0x?",
+    "tracing/heap_profiler_blink_gc/AllocationRegister",
+    "tracing/heap_profiler_malloc/AllocationRegister",
+    "tracing/heap_profiler_partition_alloc/AllocationRegister",
+    nullptr  // End of list marker.
+};
+
+const char* const* g_dump_provider_whitelist = kDumpProviderWhitelist;
+const char* const* g_allocator_dump_name_whitelist =
+    kAllocatorDumpNameWhitelist;
+
+bool IsMemoryDumpProviderInList(const char* mdp_name, const char* const* list) {
+  for (size_t i = 0; list[i] != nullptr; ++i) {
+    if (strcmp(mdp_name, list[i]) == 0)
+      return true;
+  }
+  return false;
+}
+
+}  // namespace
+
+bool IsMemoryDumpProviderWhitelisted(const char* mdp_name) {
+  return IsMemoryDumpProviderInList(mdp_name, g_dump_provider_whitelist);
+}
+
+bool IsMemoryAllocatorDumpNameWhitelisted(const std::string& name) {
+  // Global dumps are explicitly whitelisted for background use.
+  if (base::StartsWith(name, "global/", CompareCase::SENSITIVE)) {
+    for (size_t i = strlen("global/"); i < name.size(); i++)
+      if (!base::IsHexDigit(name[i]))
+        return false;
+    return true;
+  }
+
+  if (base::StartsWith(name, "shared_memory/", CompareCase::SENSITIVE)) {
+    for (size_t i = strlen("shared_memory/"); i < name.size(); i++)
+      if (!base::IsHexDigit(name[i]))
+        return false;
+    return true;
+  }
+
+  // Remove special characters, numbers (including hexadecimal which are marked
+  // by '0x') from the given string.
+  const size_t length = name.size();
+  std::string stripped_str;
+  stripped_str.reserve(length);
+  bool parsing_hex = false;
+  for (size_t i = 0; i < length; ++i) {
+    if (parsing_hex && isxdigit(name[i]))
+      continue;
+    parsing_hex = false;
+    if (i + 1 < length && name[i] == '0' && name[i + 1] == 'x') {
+      parsing_hex = true;
+      stripped_str.append("0x?");
+      ++i;
+    } else {
+      stripped_str.push_back(name[i]);
+    }
+  }
+
+  for (size_t i = 0; g_allocator_dump_name_whitelist[i] != nullptr; ++i) {
+    if (stripped_str == g_allocator_dump_name_whitelist[i]) {
+      return true;
+    }
+  }
+  return false;
+}
+
+void SetDumpProviderWhitelistForTesting(const char* const* list) {
+  g_dump_provider_whitelist = list;
+}
+
+void SetAllocatorDumpNameWhitelistForTesting(const char* const* list) {
+  g_allocator_dump_name_whitelist = list;
+}
+
+}  // namespace trace_event
+}  // namespace base
diff --git a/base/trace_event/memory_infra_background_whitelist.h b/base/trace_event/memory_infra_background_whitelist.h
new file mode 100644
index 0000000..b8d704a
--- /dev/null
+++ b/base/trace_event/memory_infra_background_whitelist.h
@@ -0,0 +1,33 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TRACE_EVENT_MEMORY_INFRA_BACKGROUND_WHITELIST_H_
+#define BASE_TRACE_EVENT_MEMORY_INFRA_BACKGROUND_WHITELIST_H_
+
+// This file contains the whitelists for background mode to limit the tracing
+// overhead and remove sensitive information from traces.
+
+#include <string>
+
+#include "base/base_export.h"
+
+namespace base {
+namespace trace_event {
+
+// Checks if the given |mdp_name| is in the whitelist.
+bool BASE_EXPORT IsMemoryDumpProviderWhitelisted(const char* mdp_name);
+
+// Checks if the given |name| matches any of the whitelisted patterns.
+bool BASE_EXPORT IsMemoryAllocatorDumpNameWhitelisted(const std::string& name);
+
+// The whitelist is replaced with the given list for tests. The last element of
+// the list must be nullptr.
+void BASE_EXPORT SetDumpProviderWhitelistForTesting(const char* const* list);
+void BASE_EXPORT
+SetAllocatorDumpNameWhitelistForTesting(const char* const* list);
+
+}  // namespace trace_event
+}  // namespace base
+
+#endif  // BASE_TRACE_EVENT_MEMORY_INFRA_BACKGROUND_WHITELIST_H_
diff --git a/base/trace_event/memory_infra_background_whitelist_unittest.cc b/base/trace_event/memory_infra_background_whitelist_unittest.cc
new file mode 100644
index 0000000..3037eb1
--- /dev/null
+++ b/base/trace_event/memory_infra_background_whitelist_unittest.cc
@@ -0,0 +1,37 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/trace_event/memory_infra_background_whitelist.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+namespace trace_event {
+
+TEST(MemoryInfraBackgroundWhitelist, Whitelist) {
+  // Global dumps that are of hex digits are all whitelisted for background use.
+  EXPECT_TRUE(IsMemoryAllocatorDumpNameWhitelisted("global/01234ABCDEF"));
+  EXPECT_TRUE(
+      IsMemoryAllocatorDumpNameWhitelisted("shared_memory/01234ABCDEF"));
+
+  // Global dumps that contain non-hex digits are not whitelisted.
+  EXPECT_FALSE(IsMemoryAllocatorDumpNameWhitelisted("global/GHIJK"));
+  EXPECT_FALSE(IsMemoryAllocatorDumpNameWhitelisted("shared_memory/GHIJK"));
+
+  // Test a couple that contain pointer values.
+  EXPECT_TRUE(IsMemoryAllocatorDumpNameWhitelisted("net/url_request_context"));
+  EXPECT_TRUE(IsMemoryAllocatorDumpNameWhitelisted(
+      "net/url_request_context/app_request/0x123/cookie_monster"));
+  EXPECT_TRUE(
+      IsMemoryAllocatorDumpNameWhitelisted("net/http_network_session_0x123"));
+  EXPECT_FALSE(
+      IsMemoryAllocatorDumpNameWhitelisted("net/http_network_session/0x123"));
+  EXPECT_TRUE(IsMemoryAllocatorDumpNameWhitelisted(
+      "net/http_network_session_0x123/quic_stream_factory"));
+}
+
+}  // namespace trace_event
+
+}  // namespace base
diff --git a/base/trace_event/memory_usage_estimator.cc b/base/trace_event/memory_usage_estimator.cc
new file mode 100644
index 0000000..c769d5b
--- /dev/null
+++ b/base/trace_event/memory_usage_estimator.cc
@@ -0,0 +1,14 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/trace_event/memory_usage_estimator.h"
+
+namespace base {
+namespace trace_event {
+
+template size_t EstimateMemoryUsage(const std::string&);
+template size_t EstimateMemoryUsage(const string16&);
+
+}  // namespace trace_event
+}  // namespace base
diff --git a/base/trace_event/memory_usage_estimator.h b/base/trace_event/memory_usage_estimator.h
new file mode 100644
index 0000000..214c64a
--- /dev/null
+++ b/base/trace_event/memory_usage_estimator.h
@@ -0,0 +1,654 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TRACE_EVENT_MEMORY_USAGE_ESTIMATOR_H_
+#define BASE_TRACE_EVENT_MEMORY_USAGE_ESTIMATOR_H_
+
+#include <stdint.h>
+
+#include <array>
+#include <deque>
+#include <list>
+#include <map>
+#include <memory>
+#include <queue>
+#include <set>
+#include <stack>
+#include <string>
+#include <type_traits>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+#include "base/base_export.h"
+#include "base/containers/circular_deque.h"
+#include "base/containers/flat_map.h"
+#include "base/containers/flat_set.h"
+#include "base/containers/linked_list.h"
+#include "base/containers/mru_cache.h"
+#include "base/containers/queue.h"
+#include "base/stl_util.h"
+#include "base/strings/string16.h"
+#include "base/template_util.h"
+
+// Composable memory usage estimators.
+//
+// This file defines set of EstimateMemoryUsage(object) functions that return
+// approximate memory usage of their argument.
+//
+// The ultimate goal is to make memory usage estimation for a class simply a
+// matter of aggregating EstimateMemoryUsage() results over all fields.
+//
+// That is achieved via composability: if EstimateMemoryUsage() is defined
+// for T then EstimateMemoryUsage() is also defined for any combination of
+// containers holding T (e.g. std::map<int, std::vector<T>>).
+//
+// There are two ways of defining EstimateMemoryUsage() for a type:
+//
+// 1. As a global function 'size_t EstimateMemoryUsage(T)' in
+//    in base::trace_event namespace.
+//
+// 2. As 'size_t T::EstimateMemoryUsage() const' method. In this case
+//    EstimateMemoryUsage(T) function in base::trace_event namespace is
+//    provided automatically.
+//
+// Here is an example implementation:
+//
+// size_t foo::bar::MyClass::EstimateMemoryUsage() const {
+//   return base::trace_event::EstimateMemoryUsage(name_) +
+//          base::trace_event::EstimateMemoryUsage(id_) +
+//          base::trace_event::EstimateMemoryUsage(items_);
+// }
+//
+// The approach is simple: first call EstimateMemoryUsage() on all members,
+// then recursively fix compilation errors that are caused by types not
+// implementing EstimateMemoryUsage().
+
+namespace base {
+namespace trace_event {
+
+// Declarations
+
+// If T declares 'EstimateMemoryUsage() const' member function, then
+// global function EstimateMemoryUsage(T) is available, and just calls
+// the member function.
+template <class T>
+auto EstimateMemoryUsage(const T& object)
+    -> decltype(object.EstimateMemoryUsage());
+
+// String
+
+template <class C, class T, class A>
+size_t EstimateMemoryUsage(const std::basic_string<C, T, A>& string);
+
+// Arrays
+
+template <class T, size_t N>
+size_t EstimateMemoryUsage(const std::array<T, N>& array);
+
+template <class T, size_t N>
+size_t EstimateMemoryUsage(T (&array)[N]);
+
+template <class T>
+size_t EstimateMemoryUsage(const T* array, size_t array_length);
+
+// std::unique_ptr
+
+template <class T, class D>
+size_t EstimateMemoryUsage(const std::unique_ptr<T, D>& ptr);
+
+template <class T, class D>
+size_t EstimateMemoryUsage(const std::unique_ptr<T[], D>& array,
+                           size_t array_length);
+
+// std::shared_ptr
+
+template <class T>
+size_t EstimateMemoryUsage(const std::shared_ptr<T>& ptr);
+
+// Containers
+
+template <class F, class S>
+size_t EstimateMemoryUsage(const std::pair<F, S>& pair);
+
+template <class T, class A>
+size_t EstimateMemoryUsage(const std::vector<T, A>& vector);
+
+template <class T, class A>
+size_t EstimateMemoryUsage(const std::list<T, A>& list);
+
+template <class T>
+size_t EstimateMemoryUsage(const base::LinkedList<T>& list);
+
+template <class T, class C, class A>
+size_t EstimateMemoryUsage(const std::set<T, C, A>& set);
+
+template <class T, class C, class A>
+size_t EstimateMemoryUsage(const std::multiset<T, C, A>& set);
+
+template <class K, class V, class C, class A>
+size_t EstimateMemoryUsage(const std::map<K, V, C, A>& map);
+
+template <class K, class V, class C, class A>
+size_t EstimateMemoryUsage(const std::multimap<K, V, C, A>& map);
+
+template <class T, class H, class KE, class A>
+size_t EstimateMemoryUsage(const std::unordered_set<T, H, KE, A>& set);
+
+template <class T, class H, class KE, class A>
+size_t EstimateMemoryUsage(const std::unordered_multiset<T, H, KE, A>& set);
+
+template <class K, class V, class H, class KE, class A>
+size_t EstimateMemoryUsage(const std::unordered_map<K, V, H, KE, A>& map);
+
+template <class K, class V, class H, class KE, class A>
+size_t EstimateMemoryUsage(const std::unordered_multimap<K, V, H, KE, A>& map);
+
+template <class T, class A>
+size_t EstimateMemoryUsage(const std::deque<T, A>& deque);
+
+template <class T, class C>
+size_t EstimateMemoryUsage(const std::queue<T, C>& queue);
+
+template <class T, class C>
+size_t EstimateMemoryUsage(const std::priority_queue<T, C>& queue);
+
+template <class T, class C>
+size_t EstimateMemoryUsage(const std::stack<T, C>& stack);
+
+template <class T>
+size_t EstimateMemoryUsage(const base::circular_deque<T>& deque);
+
+template <class T, class C>
+size_t EstimateMemoryUsage(const base::flat_set<T, C>& set);
+
+template <class K, class V, class C>
+size_t EstimateMemoryUsage(const base::flat_map<K, V, C>& map);
+
+template <class Key,
+          class Payload,
+          class HashOrComp,
+          template <typename, typename, typename> class Map>
+size_t EstimateMemoryUsage(const MRUCacheBase<Key, Payload, HashOrComp, Map>&);
+
+// TODO(dskiba):
+//   std::forward_list
+
+// Definitions
+
+namespace internal {
+
+// HasEMU<T>::value is true iff EstimateMemoryUsage(T) is available.
+// (This is the default version, which is false.)
+template <class T, class X = void>
+struct HasEMU : std::false_type {};
+
+// This HasEMU specialization is only picked up if there exists function
+// EstimateMemoryUsage(const T&) that returns size_t. Simpler ways to
+// achieve this don't work on MSVC.
+template <class T>
+struct HasEMU<
+    T,
+    typename std::enable_if<std::is_same<
+        size_t,
+        decltype(EstimateMemoryUsage(std::declval<const T&>()))>::value>::type>
+    : std::true_type {};
+
+// EMUCaller<T> does three things:
+// 1. Defines Call() method that calls EstimateMemoryUsage(T) if it's
+//    available.
+// 2. If EstimateMemoryUsage(T) is not available, but T has trivial dtor
+//    (i.e. it's POD, integer, pointer, enum, etc.) then it defines Call()
+//    method that returns 0. This is useful for containers, which allocate
+//    memory regardless of T (also for cases like std::map<int, MyClass>).
+// 3. Finally, if EstimateMemoryUsage(T) is not available, then it triggers
+//    a static_assert with a helpful message. That cuts numbers of errors
+//    considerably - if you just call EstimateMemoryUsage(T) but it's not
+//    available for T, then compiler will helpfully list *all* possible
+//    variants of it, with an explanation for each.
+template <class T, class X = void>
+struct EMUCaller {
+  // std::is_same<> below makes static_assert depend on T, in order to
+  // prevent it from asserting regardless instantiation.
+  static_assert(std::is_same<T, std::false_type>::value,
+                "Neither global function 'size_t EstimateMemoryUsage(T)' "
+                "nor member function 'size_t T::EstimateMemoryUsage() const' "
+                "is defined for the type.");
+
+  static size_t Call(const T&) { return 0; }
+};
+
+template <class T>
+struct EMUCaller<T, typename std::enable_if<HasEMU<T>::value>::type> {
+  static size_t Call(const T& value) { return EstimateMemoryUsage(value); }
+};
+
+template <template <class...> class Container, class I, class = void>
+struct IsComplexIteratorForContainer : std::false_type {};
+
+template <template <class...> class Container, class I>
+struct IsComplexIteratorForContainer<
+    Container,
+    I,
+    std::enable_if_t<!std::is_pointer<I>::value &&
+                     base::internal::is_iterator<I>::value>> {
+  using value_type = typename std::iterator_traits<I>::value_type;
+  using container_type = Container<value_type>;
+
+  // We use enum instead of static constexpr bool, beause we don't have inline
+  // variables until c++17.
+  //
+  // The downside is - value is not of type bool.
+  enum : bool {
+    value =
+        std::is_same<typename container_type::iterator, I>::value ||
+        std::is_same<typename container_type::const_iterator, I>::value ||
+        std::is_same<typename container_type::reverse_iterator, I>::value ||
+        std::is_same<typename container_type::const_reverse_iterator, I>::value,
+  };
+};
+
+template <class I, template <class...> class... Containers>
+constexpr bool OneOfContainersComplexIterators() {
+  // We are forced to create a temporary variable to workaround a compilation
+  // error in msvs.
+  const bool all_tests[] = {
+      IsComplexIteratorForContainer<Containers, I>::value...};
+  for (bool test : all_tests)
+    if (test)
+      return true;
+  return false;
+}
+
+// std::array has an extra required template argument. We curry it.
+template <class T>
+using array_test_helper = std::array<T, 1>;
+
+template <class I>
+constexpr bool IsStandardContainerComplexIterator() {
+  // TODO(dyaroshev): deal with maps iterators if there is a need.
+  // It requires to parse pairs into keys and values.
+  // TODO(dyaroshev): deal with unordered containers: they do not have reverse
+  // iterators.
+  return OneOfContainersComplexIterators<
+      I, array_test_helper, std::vector, std::deque,
+      /*std::forward_list,*/ std::list, std::set, std::multiset>();
+}
+
+// Work around MSVS bug. For some reason constexpr function doesn't work.
+// However variable template does.
+template <typename T>
+constexpr bool IsKnownNonAllocatingType_v =
+    std::is_trivially_destructible<T>::value ||
+    IsStandardContainerComplexIterator<T>();
+
+template <class T>
+struct EMUCaller<
+    T,
+    std::enable_if_t<!HasEMU<T>::value && IsKnownNonAllocatingType_v<T>>> {
+  static size_t Call(const T& value) { return 0; }
+};
+
+}  // namespace internal
+
+// Proxy that deducts T and calls EMUCaller<T>.
+// To be used by EstimateMemoryUsage() implementations for containers.
+template <class T>
+size_t EstimateItemMemoryUsage(const T& value) {
+  return internal::EMUCaller<T>::Call(value);
+}
+
+template <class I>
+size_t EstimateIterableMemoryUsage(const I& iterable) {
+  size_t memory_usage = 0;
+  for (const auto& item : iterable) {
+    memory_usage += EstimateItemMemoryUsage(item);
+  }
+  return memory_usage;
+}
+
+// Global EstimateMemoryUsage(T) that just calls T::EstimateMemoryUsage().
+template <class T>
+auto EstimateMemoryUsage(const T& object)
+    -> decltype(object.EstimateMemoryUsage()) {
+  static_assert(
+      std::is_same<decltype(object.EstimateMemoryUsage()), size_t>::value,
+      "'T::EstimateMemoryUsage() const' must return size_t.");
+  return object.EstimateMemoryUsage();
+}
+
+// String
+
+template <class C, class T, class A>
+size_t EstimateMemoryUsage(const std::basic_string<C, T, A>& string) {
+  using string_type = std::basic_string<C, T, A>;
+  using value_type = typename string_type::value_type;
+  // C++11 doesn't leave much room for implementors - std::string can
+  // use short string optimization, but that's about it. We detect SSO
+  // by checking that c_str() points inside |string|.
+  const uint8_t* cstr = reinterpret_cast<const uint8_t*>(string.c_str());
+  const uint8_t* inline_cstr = reinterpret_cast<const uint8_t*>(&string);
+  if (cstr >= inline_cstr && cstr < inline_cstr + sizeof(string)) {
+    // SSO string
+    return 0;
+  }
+  return (string.capacity() + 1) * sizeof(value_type);
+}
+
+// Use explicit instantiations from the .cc file (reduces bloat).
+extern template BASE_EXPORT size_t EstimateMemoryUsage(const std::string&);
+extern template BASE_EXPORT size_t EstimateMemoryUsage(const string16&);
+
+// Arrays
+
+template <class T, size_t N>
+size_t EstimateMemoryUsage(const std::array<T, N>& array) {
+  return EstimateIterableMemoryUsage(array);
+}
+
+template <class T, size_t N>
+size_t EstimateMemoryUsage(T (&array)[N]) {
+  return EstimateIterableMemoryUsage(array);
+}
+
+template <class T>
+size_t EstimateMemoryUsage(const T* array, size_t array_length) {
+  size_t memory_usage = sizeof(T) * array_length;
+  for (size_t i = 0; i != array_length; ++i) {
+    memory_usage += EstimateItemMemoryUsage(array[i]);
+  }
+  return memory_usage;
+}
+
+// std::unique_ptr
+
+template <class T, class D>
+size_t EstimateMemoryUsage(const std::unique_ptr<T, D>& ptr) {
+  return ptr ? (sizeof(T) + EstimateItemMemoryUsage(*ptr)) : 0;
+}
+
+template <class T, class D>
+size_t EstimateMemoryUsage(const std::unique_ptr<T[], D>& array,
+                           size_t array_length) {
+  return EstimateMemoryUsage(array.get(), array_length);
+}
+
+// std::shared_ptr
+
+template <class T>
+size_t EstimateMemoryUsage(const std::shared_ptr<T>& ptr) {
+  auto use_count = ptr.use_count();
+  if (use_count == 0) {
+    return 0;
+  }
+  // Model shared_ptr after libc++,
+  // see __shared_ptr_pointer from include/memory
+  struct SharedPointer {
+    void* vtbl;
+    long shared_owners;
+    long shared_weak_owners;
+    T* value;
+  };
+  // If object of size S shared N > S times we prefer to (potentially)
+  // overestimate than to return 0.
+  return sizeof(SharedPointer) +
+         (EstimateItemMemoryUsage(*ptr) + (use_count - 1)) / use_count;
+}
+
+// std::pair
+
+template <class F, class S>
+size_t EstimateMemoryUsage(const std::pair<F, S>& pair) {
+  return EstimateItemMemoryUsage(pair.first) +
+         EstimateItemMemoryUsage(pair.second);
+}
+
+// std::vector
+
+template <class T, class A>
+size_t EstimateMemoryUsage(const std::vector<T, A>& vector) {
+  return sizeof(T) * vector.capacity() + EstimateIterableMemoryUsage(vector);
+}
+
+// std::list
+
+template <class T, class A>
+size_t EstimateMemoryUsage(const std::list<T, A>& list) {
+  using value_type = typename std::list<T, A>::value_type;
+  struct Node {
+    Node* prev;
+    Node* next;
+    value_type value;
+  };
+  return sizeof(Node) * list.size() +
+         EstimateIterableMemoryUsage(list);
+}
+
+template <class T>
+size_t EstimateMemoryUsage(const base::LinkedList<T>& list) {
+  size_t memory_usage = 0u;
+  for (base::LinkNode<T>* node = list.head(); node != list.end();
+       node = node->next()) {
+    // Since we increment by calling node = node->next() we know that node
+    // isn't nullptr.
+    memory_usage += EstimateMemoryUsage(*node->value()) + sizeof(T);
+  }
+  return memory_usage;
+}
+
+// Tree containers
+
+template <class V>
+size_t EstimateTreeMemoryUsage(size_t size) {
+  // Tree containers are modeled after libc++
+  // (__tree_node from include/__tree)
+  struct Node {
+    Node* left;
+    Node* right;
+    Node* parent;
+    bool is_black;
+    V value;
+  };
+  return sizeof(Node) * size;
+}
+
+template <class T, class C, class A>
+size_t EstimateMemoryUsage(const std::set<T, C, A>& set) {
+  using value_type = typename std::set<T, C, A>::value_type;
+  return EstimateTreeMemoryUsage<value_type>(set.size()) +
+         EstimateIterableMemoryUsage(set);
+}
+
+template <class T, class C, class A>
+size_t EstimateMemoryUsage(const std::multiset<T, C, A>& set) {
+  using value_type = typename std::multiset<T, C, A>::value_type;
+  return EstimateTreeMemoryUsage<value_type>(set.size()) +
+         EstimateIterableMemoryUsage(set);
+}
+
+template <class K, class V, class C, class A>
+size_t EstimateMemoryUsage(const std::map<K, V, C, A>& map) {
+  using value_type = typename std::map<K, V, C, A>::value_type;
+  return EstimateTreeMemoryUsage<value_type>(map.size()) +
+         EstimateIterableMemoryUsage(map);
+}
+
+template <class K, class V, class C, class A>
+size_t EstimateMemoryUsage(const std::multimap<K, V, C, A>& map) {
+  using value_type = typename std::multimap<K, V, C, A>::value_type;
+  return EstimateTreeMemoryUsage<value_type>(map.size()) +
+         EstimateIterableMemoryUsage(map);
+}
+
+// HashMap containers
+
+namespace internal {
+
+// While hashtable containers model doesn't depend on STL implementation, one
+// detail still crept in: bucket_count. It's used in size estimation, but its
+// value after inserting N items is not predictable.
+// This function is specialized by unittests to return constant value, thus
+// excluding bucket_count from testing.
+template <class V>
+size_t HashMapBucketCountForTesting(size_t bucket_count) {
+  return bucket_count;
+}
+
+template <class MruCacheType>
+size_t DoEstimateMemoryUsageForMruCache(const MruCacheType& mru_cache) {
+  return EstimateMemoryUsage(mru_cache.ordering_) +
+         EstimateMemoryUsage(mru_cache.index_);
+}
+
+}  // namespace internal
+
+template <class V>
+size_t EstimateHashMapMemoryUsage(size_t bucket_count, size_t size) {
+  // Hashtable containers are modeled after libc++
+  // (__hash_node from include/__hash_table)
+  struct Node {
+    void* next;
+    size_t hash;
+    V value;
+  };
+  using Bucket = void*;
+  bucket_count = internal::HashMapBucketCountForTesting<V>(bucket_count);
+  return sizeof(Bucket) * bucket_count + sizeof(Node) * size;
+}
+
+template <class K, class H, class KE, class A>
+size_t EstimateMemoryUsage(const std::unordered_set<K, H, KE, A>& set) {
+  using value_type = typename std::unordered_set<K, H, KE, A>::value_type;
+  return EstimateHashMapMemoryUsage<value_type>(set.bucket_count(),
+                                                set.size()) +
+         EstimateIterableMemoryUsage(set);
+}
+
+template <class K, class H, class KE, class A>
+size_t EstimateMemoryUsage(const std::unordered_multiset<K, H, KE, A>& set) {
+  using value_type = typename std::unordered_multiset<K, H, KE, A>::value_type;
+  return EstimateHashMapMemoryUsage<value_type>(set.bucket_count(),
+                                                set.size()) +
+         EstimateIterableMemoryUsage(set);
+}
+
+template <class K, class V, class H, class KE, class A>
+size_t EstimateMemoryUsage(const std::unordered_map<K, V, H, KE, A>& map) {
+  using value_type = typename std::unordered_map<K, V, H, KE, A>::value_type;
+  return EstimateHashMapMemoryUsage<value_type>(map.bucket_count(),
+                                                map.size()) +
+         EstimateIterableMemoryUsage(map);
+}
+
+template <class K, class V, class H, class KE, class A>
+size_t EstimateMemoryUsage(const std::unordered_multimap<K, V, H, KE, A>& map) {
+  using value_type =
+      typename std::unordered_multimap<K, V, H, KE, A>::value_type;
+  return EstimateHashMapMemoryUsage<value_type>(map.bucket_count(),
+                                                map.size()) +
+         EstimateIterableMemoryUsage(map);
+}
+
+// std::deque
+
+template <class T, class A>
+size_t EstimateMemoryUsage(const std::deque<T, A>& deque) {
+// Since std::deque implementations are wildly different
+// (see crbug.com/674287), we can't have one "good enough"
+// way to estimate.
+
+// kBlockSize      - minimum size of a block, in bytes
+// kMinBlockLength - number of elements in a block
+//                   if sizeof(T) > kBlockSize
+#if defined(_LIBCPP_VERSION)
+  size_t kBlockSize = 4096;
+  size_t kMinBlockLength = 16;
+#elif defined(__GLIBCXX__)
+  size_t kBlockSize = 512;
+  size_t kMinBlockLength = 1;
+#elif defined(_MSC_VER)
+  size_t kBlockSize = 16;
+  size_t kMinBlockLength = 1;
+#else
+  size_t kBlockSize = 0;
+  size_t kMinBlockLength = 1;
+#endif
+
+  size_t block_length =
+      (sizeof(T) > kBlockSize) ? kMinBlockLength : kBlockSize / sizeof(T);
+
+  size_t blocks = (deque.size() + block_length - 1) / block_length;
+
+#if defined(__GLIBCXX__)
+  // libstdc++: deque always has at least one block
+  if (!blocks)
+    blocks = 1;
+#endif
+
+#if defined(_LIBCPP_VERSION)
+  // libc++: deque keeps at most two blocks when it shrinks,
+  // so even if the size is zero, deque might be holding up
+  // to 4096 * 2 bytes. One way to know whether deque has
+  // ever allocated (and hence has 1 or 2 blocks) is to check
+  // iterator's pointer. Non-zero value means that deque has
+  // at least one block.
+  if (!blocks && deque.begin().operator->())
+    blocks = 1;
+#endif
+
+  return (blocks * block_length * sizeof(T)) +
+         EstimateIterableMemoryUsage(deque);
+}
+
+// Container adapters
+
+template <class T, class C>
+size_t EstimateMemoryUsage(const std::queue<T, C>& queue) {
+  return EstimateMemoryUsage(GetUnderlyingContainer(queue));
+}
+
+template <class T, class C>
+size_t EstimateMemoryUsage(const std::priority_queue<T, C>& queue) {
+  return EstimateMemoryUsage(GetUnderlyingContainer(queue));
+}
+
+template <class T, class C>
+size_t EstimateMemoryUsage(const std::stack<T, C>& stack) {
+  return EstimateMemoryUsage(GetUnderlyingContainer(stack));
+}
+
+// base::circular_deque
+
+template <class T>
+size_t EstimateMemoryUsage(const base::circular_deque<T>& deque) {
+  return sizeof(T) * deque.capacity() + EstimateIterableMemoryUsage(deque);
+}
+
+// Flat containers
+
+template <class T, class C>
+size_t EstimateMemoryUsage(const base::flat_set<T, C>& set) {
+  using value_type = typename base::flat_set<T, C>::value_type;
+  return sizeof(value_type) * set.capacity() + EstimateIterableMemoryUsage(set);
+}
+
+template <class K, class V, class C>
+size_t EstimateMemoryUsage(const base::flat_map<K, V, C>& map) {
+  using value_type = typename base::flat_map<K, V, C>::value_type;
+  return sizeof(value_type) * map.capacity() + EstimateIterableMemoryUsage(map);
+}
+
+template <class Key,
+          class Payload,
+          class HashOrComp,
+          template <typename, typename, typename> class Map>
+size_t EstimateMemoryUsage(
+    const MRUCacheBase<Key, Payload, HashOrComp, Map>& mru_cache) {
+  return internal::DoEstimateMemoryUsageForMruCache(mru_cache);
+}
+
+}  // namespace trace_event
+}  // namespace base
+
+#endif  // BASE_TRACE_EVENT_MEMORY_USAGE_ESTIMATOR_H_
diff --git a/base/trace_event/memory_usage_estimator_unittest.cc b/base/trace_event/memory_usage_estimator_unittest.cc
new file mode 100644
index 0000000..b525990
--- /dev/null
+++ b/base/trace_event/memory_usage_estimator_unittest.cc
@@ -0,0 +1,265 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/trace_event/memory_usage_estimator.h"
+
+#include <stdlib.h>
+
+#include "base/memory/ptr_util.h"
+#include "base/strings/string16.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if defined(ARCH_CPU_64_BITS)
+#define EXPECT_EQ_32_64(_, e, a) EXPECT_EQ(e, a)
+#else
+#define EXPECT_EQ_32_64(e, _, a) EXPECT_EQ(e, a)
+#endif
+
+namespace base {
+namespace trace_event {
+
+namespace {
+
+// Test class with predictable memory usage.
+class Data {
+ public:
+  explicit Data(size_t size = 17): size_(size) {
+  }
+
+  size_t size() const { return size_; }
+
+  size_t EstimateMemoryUsage() const {
+    return size_;
+  }
+
+  bool operator < (const Data& other) const {
+    return size_ < other.size_;
+  }
+  bool operator == (const Data& other) const {
+    return size_ == other.size_;
+  }
+
+  struct Hasher {
+    size_t operator () (const Data& data) const {
+      return data.size();
+    }
+  };
+
+ private:
+  size_t size_;
+};
+
+}  // namespace
+
+namespace internal {
+
+// This kills variance of bucket_count across STL implementations.
+template <>
+size_t HashMapBucketCountForTesting<Data>(size_t) {
+  return 10;
+}
+template <>
+size_t HashMapBucketCountForTesting<std::pair<const Data, short>>(size_t) {
+  return 10;
+}
+
+}  // namespace internal
+
+TEST(EstimateMemoryUsageTest, String) {
+  std::string string(777, 'a');
+  EXPECT_EQ(string.capacity() + 1, EstimateMemoryUsage(string));
+}
+
+TEST(EstimateMemoryUsageTest, String16) {
+  string16 string(777, 'a');
+  EXPECT_EQ(sizeof(char16) * (string.capacity() + 1),
+            EstimateMemoryUsage(string));
+}
+
+TEST(EstimateMemoryUsageTest, Arrays) {
+  // std::array
+  {
+    std::array<Data, 10> array;
+    EXPECT_EQ(170u, EstimateMemoryUsage(array));
+  }
+
+  // T[N]
+  {
+    Data array[10];
+    EXPECT_EQ(170u, EstimateMemoryUsage(array));
+  }
+
+  // C array
+  {
+    struct Item {
+      char payload[10];
+    };
+    Item* array = new Item[7];
+    EXPECT_EQ(70u, EstimateMemoryUsage(array, 7));
+    delete[] array;
+  }
+}
+
+TEST(EstimateMemoryUsageTest, UniquePtr) {
+  // Empty
+  {
+    std::unique_ptr<Data> ptr;
+    EXPECT_EQ(0u, EstimateMemoryUsage(ptr));
+  }
+
+  // Not empty
+  {
+    std::unique_ptr<Data> ptr(new Data());
+    EXPECT_EQ_32_64(21u, 25u, EstimateMemoryUsage(ptr));
+  }
+
+  // With a pointer
+  {
+    std::unique_ptr<Data*> ptr(new Data*());
+    EXPECT_EQ(sizeof(void*), EstimateMemoryUsage(ptr));
+  }
+
+  // With an array
+  {
+    struct Item {
+      uint32_t payload[10];
+    };
+    std::unique_ptr<Item[]> ptr(new Item[7]);
+    EXPECT_EQ(280u, EstimateMemoryUsage(ptr, 7));
+  }
+}
+
+TEST(EstimateMemoryUsageTest, Vector) {
+  std::vector<Data> vector;
+  vector.reserve(1000);
+
+  // For an empty vector we should return memory usage of its buffer
+  size_t capacity = vector.capacity();
+  size_t expected_size = capacity * sizeof(Data);
+  EXPECT_EQ(expected_size, EstimateMemoryUsage(vector));
+
+  // If vector is not empty, its size should also include memory usages
+  // of all elements.
+  for (size_t i = 0; i != capacity / 2; ++i) {
+    vector.push_back(Data(i));
+    expected_size += EstimateMemoryUsage(vector.back());
+  }
+  EXPECT_EQ(expected_size, EstimateMemoryUsage(vector));
+}
+
+TEST(EstimateMemoryUsageTest, List) {
+  struct POD {
+    short data;
+  };
+  std::list<POD> list;
+  for (int i = 0; i != 1000; ++i) {
+    list.push_back(POD());
+  }
+  EXPECT_EQ_32_64(12000u, 24000u, EstimateMemoryUsage(list));
+}
+
+TEST(EstimateMemoryUsageTest, Set) {
+  std::set<std::pair<int, Data>> set;
+  for (int i = 0; i != 1000; ++i) {
+    set.insert({i, Data(i)});
+  }
+  EXPECT_EQ_32_64(523500u, 547500u, EstimateMemoryUsage(set));
+}
+
+TEST(EstimateMemoryUsageTest, MultiSet) {
+  std::multiset<bool> set;
+  for (int i = 0; i != 1000; ++i) {
+    set.insert((i & 1) != 0);
+  }
+  EXPECT_EQ_32_64(16000u, 32000u, EstimateMemoryUsage(set));
+}
+
+TEST(EstimateMemoryUsageTest, Map) {
+  std::map<Data, int> map;
+  for (int i = 0; i != 1000; ++i) {
+    map.insert({Data(i), i});
+  }
+  EXPECT_EQ_32_64(523500u, 547500u, EstimateMemoryUsage(map));
+}
+
+TEST(EstimateMemoryUsageTest, MultiMap) {
+  std::multimap<char, Data> map;
+  for (int i = 0; i != 1000; ++i) {
+    map.insert({static_cast<char>(i), Data(i)});
+  }
+  EXPECT_EQ_32_64(523500u, 547500u, EstimateMemoryUsage(map));
+}
+
+TEST(EstimateMemoryUsageTest, UnorderedSet) {
+  std::unordered_set<Data, Data::Hasher> set;
+  for (int i = 0; i != 1000; ++i) {
+    set.insert(Data(i));
+  }
+  EXPECT_EQ_32_64(511540u, 523580u, EstimateMemoryUsage(set));
+}
+
+TEST(EstimateMemoryUsageTest, UnorderedMultiSet) {
+  std::unordered_multiset<Data, Data::Hasher> set;
+  for (int i = 0; i != 500; ++i) {
+    set.insert(Data(i));
+    set.insert(Data(i));
+  }
+  EXPECT_EQ_32_64(261540u, 273580u, EstimateMemoryUsage(set));
+}
+
+TEST(EstimateMemoryUsageTest, UnorderedMap) {
+  std::unordered_map<Data, short, Data::Hasher> map;
+  for (int i = 0; i != 1000; ++i) {
+    map.insert({Data(i), static_cast<short>(i)});
+  }
+  EXPECT_EQ_32_64(515540u, 531580u, EstimateMemoryUsage(map));
+}
+
+TEST(EstimateMemoryUsageTest, UnorderedMultiMap) {
+  std::unordered_multimap<Data, short, Data::Hasher> map;
+  for (int i = 0; i != 1000; ++i) {
+    map.insert({Data(i), static_cast<short>(i)});
+  }
+  EXPECT_EQ_32_64(515540u, 531580u, EstimateMemoryUsage(map));
+}
+
+TEST(EstimateMemoryUsageTest, Deque) {
+  std::deque<Data> deque;
+
+  // Pick a large value so that platform-specific accounting
+  // for deque's blocks is small compared to usage of all items.
+  constexpr size_t kDataSize = 100000;
+  for (int i = 0; i != 1500; ++i) {
+    deque.push_back(Data(kDataSize));
+  }
+
+  // Compare against a reasonable minimum (i.e. no overhead).
+  size_t min_expected_usage = deque.size() * (sizeof(Data) + kDataSize);
+  EXPECT_LE(min_expected_usage, EstimateMemoryUsage(deque));
+}
+
+TEST(EstimateMemoryUsageTest, IsStandardContainerComplexIteratorTest) {
+  struct abstract {
+    virtual void method() = 0;
+  };
+
+  static_assert(
+      internal::IsStandardContainerComplexIterator<std::list<int>::iterator>(),
+      "");
+  static_assert(internal::IsStandardContainerComplexIterator<
+                    std::list<int>::const_iterator>(),
+                "");
+  static_assert(internal::IsStandardContainerComplexIterator<
+                    std::list<int>::reverse_iterator>(),
+                "");
+  static_assert(internal::IsStandardContainerComplexIterator<
+                    std::list<int>::const_reverse_iterator>(),
+                "");
+  static_assert(!internal::IsStandardContainerComplexIterator<int>(), "");
+  static_assert(!internal::IsStandardContainerComplexIterator<abstract*>(), "");
+}
+
+}  // namespace trace_event
+}  // namespace base
diff --git a/base/trace_event/process_memory_dump.cc b/base/trace_event/process_memory_dump.cc
new file mode 100644
index 0000000..362641c
--- /dev/null
+++ b/base/trace_event/process_memory_dump.cc
@@ -0,0 +1,511 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/trace_event/process_memory_dump.h"
+
+#include <errno.h>
+
+#include <vector>
+
+#include "base/memory/ptr_util.h"
+#include "base/memory/shared_memory_tracker.h"
+#include "base/process/process_metrics.h"
+#include "base/strings/stringprintf.h"
+#include "base/trace_event/memory_infra_background_whitelist.h"
+#include "base/trace_event/trace_event_argument.h"
+#include "base/unguessable_token.h"
+#include "build/build_config.h"
+
+#if defined(OS_IOS)
+#include <mach/vm_page_size.h>
+#endif
+
+#if defined(OS_POSIX) || defined(OS_FUCHSIA)
+#include <sys/mman.h>
+#endif
+
+#if defined(OS_WIN)
+#include <windows.h>  // Must be in front of other Windows header files
+
+#include <Psapi.h>
+#endif
+
+namespace base {
+namespace trace_event {
+
+namespace {
+
+const char kEdgeTypeOwnership[] = "ownership";
+
+std::string GetSharedGlobalAllocatorDumpName(
+    const MemoryAllocatorDumpGuid& guid) {
+  return "global/" + guid.ToString();
+}
+
+#if defined(COUNT_RESIDENT_BYTES_SUPPORTED)
+size_t GetSystemPageCount(size_t mapped_size, size_t page_size) {
+  return (mapped_size + page_size - 1) / page_size;
+}
+#endif
+
+UnguessableToken GetTokenForCurrentProcess() {
+  static UnguessableToken instance = UnguessableToken::Create();
+  return instance;
+}
+
+}  // namespace
+
+// static
+bool ProcessMemoryDump::is_black_hole_non_fatal_for_testing_ = false;
+
+#if defined(COUNT_RESIDENT_BYTES_SUPPORTED)
+// static
+size_t ProcessMemoryDump::GetSystemPageSize() {
+#if defined(OS_IOS)
+  // On iOS, getpagesize() returns the user page sizes, but for allocating
+  // arrays for mincore(), kernel page sizes is needed. Use vm_kernel_page_size
+  // as recommended by Apple, https://forums.developer.apple.com/thread/47532/.
+  // Refer to http://crbug.com/542671 and Apple rdar://23651782
+  return vm_kernel_page_size;
+#else
+  return base::GetPageSize();
+#endif  // defined(OS_IOS)
+}
+
+// static
+size_t ProcessMemoryDump::CountResidentBytes(void* start_address,
+                                             size_t mapped_size) {
+  const size_t page_size = GetSystemPageSize();
+  const uintptr_t start_pointer = reinterpret_cast<uintptr_t>(start_address);
+  DCHECK_EQ(0u, start_pointer % page_size);
+
+  size_t offset = 0;
+  size_t total_resident_pages = 0;
+  bool failure = false;
+
+  // An array as large as number of pages in memory segment needs to be passed
+  // to the query function. To avoid allocating a large array, the given block
+  // of memory is split into chunks of size |kMaxChunkSize|.
+  const size_t kMaxChunkSize = 8 * 1024 * 1024;
+  size_t max_vec_size =
+      GetSystemPageCount(std::min(mapped_size, kMaxChunkSize), page_size);
+#if defined(OS_WIN)
+  std::unique_ptr<PSAPI_WORKING_SET_EX_INFORMATION[]> vec(
+      new PSAPI_WORKING_SET_EX_INFORMATION[max_vec_size]);
+#elif defined(OS_MACOSX)
+  std::unique_ptr<char[]> vec(new char[max_vec_size]);
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+  std::unique_ptr<unsigned char[]> vec(new unsigned char[max_vec_size]);
+#endif
+
+  while (offset < mapped_size) {
+    uintptr_t chunk_start = (start_pointer + offset);
+    const size_t chunk_size = std::min(mapped_size - offset, kMaxChunkSize);
+    const size_t page_count = GetSystemPageCount(chunk_size, page_size);
+    size_t resident_page_count = 0;
+#if defined(OS_WIN)
+    for (size_t i = 0; i < page_count; i++) {
+      vec[i].VirtualAddress =
+          reinterpret_cast<void*>(chunk_start + i * page_size);
+    }
+    DWORD vec_size = static_cast<DWORD>(
+        page_count * sizeof(PSAPI_WORKING_SET_EX_INFORMATION));
+    failure = !QueryWorkingSetEx(GetCurrentProcess(), vec.get(), vec_size);
+
+    for (size_t i = 0; i < page_count; i++)
+      resident_page_count += vec[i].VirtualAttributes.Valid;
+#elif defined(OS_FUCHSIA)
+    // TODO(fuchsia): Port, see https://crbug.com/706592.
+    ALLOW_UNUSED_LOCAL(chunk_start);
+    ALLOW_UNUSED_LOCAL(page_count);
+#elif defined(OS_MACOSX)
+    // mincore in MAC does not fail with EAGAIN.
+    failure =
+        !!mincore(reinterpret_cast<void*>(chunk_start), chunk_size, vec.get());
+    for (size_t i = 0; i < page_count; i++)
+      resident_page_count += vec[i] & MINCORE_INCORE ? 1 : 0;
+#elif defined(OS_POSIX)
+    int error_counter = 0;
+    int result = 0;
+    // HANDLE_EINTR tries for 100 times. So following the same pattern.
+    do {
+      result =
+#if defined(OS_AIX)
+          mincore(reinterpret_cast<char*>(chunk_start), chunk_size,
+                  reinterpret_cast<char*>(vec.get()));
+#else
+          mincore(reinterpret_cast<void*>(chunk_start), chunk_size, vec.get());
+#endif
+    } while (result == -1 && errno == EAGAIN && error_counter++ < 100);
+    failure = !!result;
+
+    for (size_t i = 0; i < page_count; i++)
+      resident_page_count += vec[i] & 1;
+#endif
+
+    if (failure)
+      break;
+
+    total_resident_pages += resident_page_count * page_size;
+    offset += kMaxChunkSize;
+  }
+
+  DCHECK(!failure);
+  if (failure) {
+    total_resident_pages = 0;
+    LOG(ERROR) << "CountResidentBytes failed. The resident size is invalid";
+  }
+  return total_resident_pages;
+}
+
+// static
+base::Optional<size_t> ProcessMemoryDump::CountResidentBytesInSharedMemory(
+    void* start_address,
+    size_t mapped_size) {
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+  // On macOS, use mach_vm_region instead of mincore for performance
+  // (crbug.com/742042).
+  mach_vm_size_t dummy_size = 0;
+  mach_vm_address_t address =
+      reinterpret_cast<mach_vm_address_t>(start_address);
+  vm_region_top_info_data_t info;
+  MachVMRegionResult result =
+      GetTopInfo(mach_task_self(), &dummy_size, &address, &info);
+  if (result == MachVMRegionResult::Error) {
+    LOG(ERROR) << "CountResidentBytesInSharedMemory failed. The resident size "
+                  "is invalid";
+    return base::Optional<size_t>();
+  }
+
+  size_t resident_pages =
+      info.private_pages_resident + info.shared_pages_resident;
+
+  // On macOS, measurements for private memory footprint overcount by
+  // faulted pages in anonymous shared memory. To discount for this, we touch
+  // all the resident pages in anonymous shared memory here, thus making them
+  // faulted as well. This relies on two assumptions:
+  //
+  // 1) Consumers use shared memory from front to back. Thus, if there are
+  // (N) resident pages, those pages represent the first N * PAGE_SIZE bytes in
+  // the shared memory region.
+  //
+  // 2) This logic is run shortly before the logic that calculates
+  // phys_footprint, thus ensuring that the discrepancy between faulted and
+  // resident pages is minimal.
+  //
+  // The performance penalty is expected to be small.
+  //
+  // * Most of the time, we expect the pages to already be resident and faulted,
+  // thus incurring a cache penalty read hit [since we read from each resident
+  // page].
+  //
+  // * Rarely, we expect the pages to be resident but not faulted, resulting in
+  // soft faults + cache penalty.
+  //
+  // * If assumption (1) is invalid, this will potentially fault some
+  // previously non-resident pages, thus increasing memory usage, without fixing
+  // the accounting.
+  //
+  // Sanity check in case the mapped size is less than the total size of the
+  // region.
+  size_t pages_to_fault =
+      std::min(resident_pages, (mapped_size + PAGE_SIZE - 1) / PAGE_SIZE);
+
+  volatile char* base_address = static_cast<char*>(start_address);
+  for (size_t i = 0; i < pages_to_fault; ++i) {
+    // Reading from a volatile is a visible side-effect for the purposes of
+    // optimization. This guarantees that the optimizer will not kill this line.
+    base_address[i * PAGE_SIZE];
+  }
+
+  return resident_pages * PAGE_SIZE;
+#else
+  return CountResidentBytes(start_address, mapped_size);
+#endif  // defined(OS_MACOSX) && !defined(OS_IOS)
+}
+
+#endif  // defined(COUNT_RESIDENT_BYTES_SUPPORTED)
+
+ProcessMemoryDump::ProcessMemoryDump(
+    const MemoryDumpArgs& dump_args)
+    : process_token_(GetTokenForCurrentProcess()),
+      dump_args_(dump_args) {}
+
+ProcessMemoryDump::~ProcessMemoryDump() = default;
+ProcessMemoryDump::ProcessMemoryDump(ProcessMemoryDump&& other) = default;
+ProcessMemoryDump& ProcessMemoryDump::operator=(ProcessMemoryDump&& other) =
+    default;
+
+MemoryAllocatorDump* ProcessMemoryDump::CreateAllocatorDump(
+    const std::string& absolute_name) {
+  return AddAllocatorDumpInternal(std::make_unique<MemoryAllocatorDump>(
+      absolute_name, dump_args_.level_of_detail, GetDumpId(absolute_name)));
+}
+
+MemoryAllocatorDump* ProcessMemoryDump::CreateAllocatorDump(
+    const std::string& absolute_name,
+    const MemoryAllocatorDumpGuid& guid) {
+  return AddAllocatorDumpInternal(std::make_unique<MemoryAllocatorDump>(
+      absolute_name, dump_args_.level_of_detail, guid));
+}
+
+MemoryAllocatorDump* ProcessMemoryDump::AddAllocatorDumpInternal(
+    std::unique_ptr<MemoryAllocatorDump> mad) {
+  // In background mode return the black hole dump, if invalid dump name is
+  // given.
+  if (dump_args_.level_of_detail == MemoryDumpLevelOfDetail::BACKGROUND &&
+      !IsMemoryAllocatorDumpNameWhitelisted(mad->absolute_name())) {
+    return GetBlackHoleMad();
+  }
+
+  auto insertion_result = allocator_dumps_.insert(
+      std::make_pair(mad->absolute_name(), std::move(mad)));
+  MemoryAllocatorDump* inserted_mad = insertion_result.first->second.get();
+  DCHECK(insertion_result.second) << "Duplicate name: "
+                                  << inserted_mad->absolute_name();
+  return inserted_mad;
+}
+
+MemoryAllocatorDump* ProcessMemoryDump::GetAllocatorDump(
+    const std::string& absolute_name) const {
+  auto it = allocator_dumps_.find(absolute_name);
+  if (it != allocator_dumps_.end())
+    return it->second.get();
+  return nullptr;
+}
+
+MemoryAllocatorDump* ProcessMemoryDump::GetOrCreateAllocatorDump(
+    const std::string& absolute_name) {
+  MemoryAllocatorDump* mad = GetAllocatorDump(absolute_name);
+  return mad ? mad : CreateAllocatorDump(absolute_name);
+}
+
+MemoryAllocatorDump* ProcessMemoryDump::CreateSharedGlobalAllocatorDump(
+    const MemoryAllocatorDumpGuid& guid) {
+  // A shared allocator dump can be shared within a process and the guid could
+  // have been created already.
+  MemoryAllocatorDump* mad = GetSharedGlobalAllocatorDump(guid);
+  if (mad && mad != black_hole_mad_.get()) {
+    // The weak flag is cleared because this method should create a non-weak
+    // dump.
+    mad->clear_flags(MemoryAllocatorDump::Flags::WEAK);
+    return mad;
+  }
+  return CreateAllocatorDump(GetSharedGlobalAllocatorDumpName(guid), guid);
+}
+
+MemoryAllocatorDump* ProcessMemoryDump::CreateWeakSharedGlobalAllocatorDump(
+    const MemoryAllocatorDumpGuid& guid) {
+  MemoryAllocatorDump* mad = GetSharedGlobalAllocatorDump(guid);
+  if (mad && mad != black_hole_mad_.get())
+    return mad;
+  mad = CreateAllocatorDump(GetSharedGlobalAllocatorDumpName(guid), guid);
+  mad->set_flags(MemoryAllocatorDump::Flags::WEAK);
+  return mad;
+}
+
+MemoryAllocatorDump* ProcessMemoryDump::GetSharedGlobalAllocatorDump(
+    const MemoryAllocatorDumpGuid& guid) const {
+  return GetAllocatorDump(GetSharedGlobalAllocatorDumpName(guid));
+}
+
+void ProcessMemoryDump::DumpHeapUsage(
+    const std::unordered_map<base::trace_event::AllocationContext,
+                             base::trace_event::AllocationMetrics>&
+        metrics_by_context,
+    base::trace_event::TraceEventMemoryOverhead& overhead,
+    const char* allocator_name) {
+  std::string base_name = base::StringPrintf("tracing/heap_profiler_%s",
+                                             allocator_name);
+  overhead.DumpInto(base_name.c_str(), this);
+}
+
+void ProcessMemoryDump::SetAllocatorDumpsForSerialization(
+    std::vector<std::unique_ptr<MemoryAllocatorDump>> dumps) {
+  DCHECK(allocator_dumps_.empty());
+  for (std::unique_ptr<MemoryAllocatorDump>& dump : dumps)
+    AddAllocatorDumpInternal(std::move(dump));
+}
+
+std::vector<ProcessMemoryDump::MemoryAllocatorDumpEdge>
+ProcessMemoryDump::GetAllEdgesForSerialization() const {
+  std::vector<MemoryAllocatorDumpEdge> edges;
+  edges.reserve(allocator_dumps_edges_.size());
+  for (const auto& it : allocator_dumps_edges_)
+    edges.push_back(it.second);
+  return edges;
+}
+
+void ProcessMemoryDump::SetAllEdgesForSerialization(
+    const std::vector<ProcessMemoryDump::MemoryAllocatorDumpEdge>& edges) {
+  DCHECK(allocator_dumps_edges_.empty());
+  for (const MemoryAllocatorDumpEdge& edge : edges) {
+    auto it_and_inserted = allocator_dumps_edges_.emplace(edge.source, edge);
+    DCHECK(it_and_inserted.second);
+  }
+}
+
+void ProcessMemoryDump::Clear() {
+  allocator_dumps_.clear();
+  allocator_dumps_edges_.clear();
+}
+
+void ProcessMemoryDump::TakeAllDumpsFrom(ProcessMemoryDump* other) {
+  // Moves the ownership of all MemoryAllocatorDump(s) contained in |other|
+  // into this ProcessMemoryDump, checking for duplicates.
+  for (auto& it : other->allocator_dumps_)
+    AddAllocatorDumpInternal(std::move(it.second));
+  other->allocator_dumps_.clear();
+
+  // Move all the edges.
+  allocator_dumps_edges_.insert(other->allocator_dumps_edges_.begin(),
+                                other->allocator_dumps_edges_.end());
+  other->allocator_dumps_edges_.clear();
+}
+
+void ProcessMemoryDump::SerializeAllocatorDumpsInto(TracedValue* value) const {
+  if (allocator_dumps_.size() > 0) {
+    value->BeginDictionary("allocators");
+    for (const auto& allocator_dump_it : allocator_dumps_)
+      allocator_dump_it.second->AsValueInto(value);
+    value->EndDictionary();
+  }
+
+  value->BeginArray("allocators_graph");
+  for (const auto& it : allocator_dumps_edges_) {
+    const MemoryAllocatorDumpEdge& edge = it.second;
+    value->BeginDictionary();
+    value->SetString("source", edge.source.ToString());
+    value->SetString("target", edge.target.ToString());
+    value->SetInteger("importance", edge.importance);
+    value->SetString("type", kEdgeTypeOwnership);
+    value->EndDictionary();
+  }
+  value->EndArray();
+}
+
+void ProcessMemoryDump::AddOwnershipEdge(const MemoryAllocatorDumpGuid& source,
+                                         const MemoryAllocatorDumpGuid& target,
+                                         int importance) {
+  // This will either override an existing edge or create a new one.
+  auto it = allocator_dumps_edges_.find(source);
+  int max_importance = importance;
+  if (it != allocator_dumps_edges_.end()) {
+    DCHECK_EQ(target.ToUint64(), it->second.target.ToUint64());
+    max_importance = std::max(importance, it->second.importance);
+  }
+  allocator_dumps_edges_[source] = {source, target, max_importance,
+                                    false /* overridable */};
+}
+
+void ProcessMemoryDump::AddOwnershipEdge(
+    const MemoryAllocatorDumpGuid& source,
+    const MemoryAllocatorDumpGuid& target) {
+  AddOwnershipEdge(source, target, 0 /* importance */);
+}
+
+void ProcessMemoryDump::AddOverridableOwnershipEdge(
+    const MemoryAllocatorDumpGuid& source,
+    const MemoryAllocatorDumpGuid& target,
+    int importance) {
+  if (allocator_dumps_edges_.count(source) == 0) {
+    allocator_dumps_edges_[source] = {source, target, importance,
+                                      true /* overridable */};
+  } else {
+    // An edge between the source and target already exits. So, do nothing here
+    // since the new overridable edge is implicitly overridden by a strong edge
+    // which was created earlier.
+    DCHECK(!allocator_dumps_edges_[source].overridable);
+  }
+}
+
+void ProcessMemoryDump::CreateSharedMemoryOwnershipEdge(
+    const MemoryAllocatorDumpGuid& client_local_dump_guid,
+    const UnguessableToken& shared_memory_guid,
+    int importance) {
+  CreateSharedMemoryOwnershipEdgeInternal(client_local_dump_guid,
+                                          shared_memory_guid, importance,
+                                          false /*is_weak*/);
+}
+
+void ProcessMemoryDump::CreateWeakSharedMemoryOwnershipEdge(
+    const MemoryAllocatorDumpGuid& client_local_dump_guid,
+    const UnguessableToken& shared_memory_guid,
+    int importance) {
+  CreateSharedMemoryOwnershipEdgeInternal(
+      client_local_dump_guid, shared_memory_guid, importance, true /*is_weak*/);
+}
+
+void ProcessMemoryDump::CreateSharedMemoryOwnershipEdgeInternal(
+    const MemoryAllocatorDumpGuid& client_local_dump_guid,
+    const UnguessableToken& shared_memory_guid,
+    int importance,
+    bool is_weak) {
+  DCHECK(!shared_memory_guid.is_empty());
+  // New model where the global dumps created by SharedMemoryTracker are used
+  // for the clients.
+
+  // The guid of the local dump created by SharedMemoryTracker for the memory
+  // segment.
+  auto local_shm_guid =
+      GetDumpId(SharedMemoryTracker::GetDumpNameForTracing(shared_memory_guid));
+
+  // The dump guid of the global dump created by the tracker for the memory
+  // segment.
+  auto global_shm_guid =
+      SharedMemoryTracker::GetGlobalDumpIdForTracing(shared_memory_guid);
+
+  // Create an edge between local dump of the client and the local dump of the
+  // SharedMemoryTracker. Do not need to create the dumps here since the tracker
+  // would create them. The importance is also required here for the case of
+  // single process mode.
+  AddOwnershipEdge(client_local_dump_guid, local_shm_guid, importance);
+
+  // TODO(ssid): Handle the case of weak dumps here. This needs a new function
+  // GetOrCreaetGlobalDump() in PMD since we need to change the behavior of the
+  // created global dump.
+  // Create an edge that overrides the edge created by SharedMemoryTracker.
+  AddOwnershipEdge(local_shm_guid, global_shm_guid, importance);
+}
+
+void ProcessMemoryDump::AddSuballocation(const MemoryAllocatorDumpGuid& source,
+                                         const std::string& target_node_name) {
+  // Do not create new dumps for suballocations in background mode.
+  if (dump_args_.level_of_detail == MemoryDumpLevelOfDetail::BACKGROUND)
+    return;
+
+  std::string child_mad_name = target_node_name + "/__" + source.ToString();
+  MemoryAllocatorDump* target_child_mad = CreateAllocatorDump(child_mad_name);
+  AddOwnershipEdge(source, target_child_mad->guid());
+}
+
+MemoryAllocatorDump* ProcessMemoryDump::GetBlackHoleMad() {
+  DCHECK(is_black_hole_non_fatal_for_testing_);
+  if (!black_hole_mad_) {
+    std::string name = "discarded";
+    black_hole_mad_.reset(new MemoryAllocatorDump(
+        name, dump_args_.level_of_detail, GetDumpId(name)));
+  }
+  return black_hole_mad_.get();
+}
+
+MemoryAllocatorDumpGuid ProcessMemoryDump::GetDumpId(
+    const std::string& absolute_name) {
+  return MemoryAllocatorDumpGuid(StringPrintf(
+      "%s:%s", process_token().ToString().c_str(), absolute_name.c_str()));
+}
+
+bool ProcessMemoryDump::MemoryAllocatorDumpEdge::operator==(
+    const MemoryAllocatorDumpEdge& other) const {
+  return source == other.source && target == other.target &&
+         importance == other.importance && overridable == other.overridable;
+}
+
+bool ProcessMemoryDump::MemoryAllocatorDumpEdge::operator!=(
+    const MemoryAllocatorDumpEdge& other) const {
+  return !(*this == other);
+}
+
+}  // namespace trace_event
+}  // namespace base
diff --git a/base/trace_event/process_memory_dump.h b/base/trace_event/process_memory_dump.h
new file mode 100644
index 0000000..e2457b7
--- /dev/null
+++ b/base/trace_event/process_memory_dump.h
@@ -0,0 +1,284 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TRACE_EVENT_PROCESS_MEMORY_DUMP_H_
+#define BASE_TRACE_EVENT_PROCESS_MEMORY_DUMP_H_
+
+#include <stddef.h>
+
+#include <map>
+#include <unordered_map>
+#include <vector>
+
+#include "base/base_export.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/trace_event/heap_profiler_allocation_context.h"
+#include "base/trace_event/memory_allocator_dump.h"
+#include "base/trace_event/memory_allocator_dump_guid.h"
+#include "base/trace_event/memory_dump_request_args.h"
+#include "build/build_config.h"
+
+// Define COUNT_RESIDENT_BYTES_SUPPORTED if platform supports counting of the
+// resident memory.
+#if !defined(OS_NACL)
+#define COUNT_RESIDENT_BYTES_SUPPORTED
+#endif
+
+namespace base {
+
+class SharedMemory;
+class UnguessableToken;
+
+namespace trace_event {
+
+class TracedValue;
+
+// ProcessMemoryDump is as a strongly typed container which holds the dumps
+// produced by the MemoryDumpProvider(s) for a specific process.
+class BASE_EXPORT ProcessMemoryDump {
+ public:
+  struct BASE_EXPORT MemoryAllocatorDumpEdge {
+    bool operator==(const MemoryAllocatorDumpEdge&) const;
+    bool operator!=(const MemoryAllocatorDumpEdge&) const;
+
+    MemoryAllocatorDumpGuid source;
+    MemoryAllocatorDumpGuid target;
+    int importance = 0;
+    bool overridable = false;
+  };
+
+  // Maps allocator dumps absolute names (allocator_name/heap/subheap) to
+  // MemoryAllocatorDump instances.
+  using AllocatorDumpsMap =
+      std::map<std::string, std::unique_ptr<MemoryAllocatorDump>>;
+
+  // Stores allocator dump edges indexed by source allocator dump GUID.
+  using AllocatorDumpEdgesMap =
+      std::map<MemoryAllocatorDumpGuid, MemoryAllocatorDumpEdge>;
+
+#if defined(COUNT_RESIDENT_BYTES_SUPPORTED)
+  // Returns the number of bytes in a kernel memory page. Some platforms may
+  // have a different value for kernel page sizes from user page sizes. It is
+  // important to use kernel memory page sizes for resident bytes calculation.
+  // In most cases, the two are the same.
+  static size_t GetSystemPageSize();
+
+  // Returns the total bytes resident for a virtual address range, with given
+  // |start_address| and |mapped_size|. |mapped_size| is specified in bytes. The
+  // value returned is valid only if the given range is currently mmapped by the
+  // process. The |start_address| must be page-aligned.
+  static size_t CountResidentBytes(void* start_address, size_t mapped_size);
+
+  // The same as above, but the given mapped range should belong to the
+  // shared_memory's mapped region.
+  static base::Optional<size_t> CountResidentBytesInSharedMemory(
+      void* start_address,
+      size_t mapped_size);
+#endif
+
+  explicit ProcessMemoryDump(const MemoryDumpArgs& dump_args);
+  ProcessMemoryDump(ProcessMemoryDump&&);
+  ~ProcessMemoryDump();
+
+  ProcessMemoryDump& operator=(ProcessMemoryDump&&);
+
+  // Creates a new MemoryAllocatorDump with the given name and returns the
+  // empty object back to the caller.
+  // Arguments:
+  //   absolute_name: a name that uniquely identifies allocator dumps produced
+  //       by this provider. It is possible to specify nesting by using a
+  //       path-like string (e.g., v8/isolate1/heap1, v8/isolate1/heap2).
+  //       Leading or trailing slashes are not allowed.
+  //   guid: an optional identifier, unique among all processes within the
+  //       scope of a global dump. This is only relevant when using
+  //       AddOwnershipEdge() to express memory sharing. If omitted,
+  //       it will be automatically generated.
+  // ProcessMemoryDump handles the memory ownership of its MemoryAllocatorDumps.
+  MemoryAllocatorDump* CreateAllocatorDump(const std::string& absolute_name);
+  MemoryAllocatorDump* CreateAllocatorDump(const std::string& absolute_name,
+                                           const MemoryAllocatorDumpGuid& guid);
+
+  // Looks up a MemoryAllocatorDump given its allocator and heap names, or
+  // nullptr if not found.
+  MemoryAllocatorDump* GetAllocatorDump(const std::string& absolute_name) const;
+
+  // Do NOT use this method. All dump providers should use
+  // CreateAllocatorDump(). Tries to create a new MemoryAllocatorDump only if it
+  // doesn't already exist. Creating multiple dumps with same name using
+  // GetOrCreateAllocatorDump() would override the existing scalars in MAD and
+  // cause misreporting. This method is used only in rare cases multiple
+  // components create allocator dumps with same name and only one of them adds
+  // size.
+  MemoryAllocatorDump* GetOrCreateAllocatorDump(
+      const std::string& absolute_name);
+
+  // Creates a shared MemoryAllocatorDump, to express cross-process sharing.
+  // Shared allocator dumps are allowed to have duplicate guids within the
+  // global scope, in order to reference the same dump from multiple processes.
+  // See the design doc goo.gl/keU6Bf for reference usage patterns.
+  MemoryAllocatorDump* CreateSharedGlobalAllocatorDump(
+      const MemoryAllocatorDumpGuid& guid);
+
+  // Creates a shared MemoryAllocatorDump as CreateSharedGlobalAllocatorDump,
+  // but with a WEAK flag. A weak dump will be discarded unless a non-weak dump
+  // is created using CreateSharedGlobalAllocatorDump by at least one process.
+  // The WEAK flag does not apply if a non-weak dump with the same GUID already
+  // exists or is created later. All owners and children of the discarded dump
+  // will also be discarded transitively.
+  MemoryAllocatorDump* CreateWeakSharedGlobalAllocatorDump(
+      const MemoryAllocatorDumpGuid& guid);
+
+  // Looks up a shared MemoryAllocatorDump given its guid.
+  MemoryAllocatorDump* GetSharedGlobalAllocatorDump(
+      const MemoryAllocatorDumpGuid& guid) const;
+
+  // Returns the map of the MemoryAllocatorDumps added to this dump.
+  const AllocatorDumpsMap& allocator_dumps() const { return allocator_dumps_; }
+
+  AllocatorDumpsMap* mutable_allocator_dumps_for_serialization() const {
+    // Mojo takes a const input argument even for move-only types that can be
+    // mutate while serializing (like this one). Hence the const_cast.
+    return const_cast<AllocatorDumpsMap*>(&allocator_dumps_);
+  }
+  void SetAllocatorDumpsForSerialization(
+      std::vector<std::unique_ptr<MemoryAllocatorDump>>);
+
+  // Only for mojo serialization.
+  std::vector<MemoryAllocatorDumpEdge> GetAllEdgesForSerialization() const;
+  void SetAllEdgesForSerialization(const std::vector<MemoryAllocatorDumpEdge>&);
+
+  // Dumps heap usage with |allocator_name|.
+  void DumpHeapUsage(
+      const std::unordered_map<base::trace_event::AllocationContext,
+                               base::trace_event::AllocationMetrics>&
+          metrics_by_context,
+      base::trace_event::TraceEventMemoryOverhead& overhead,
+      const char* allocator_name);
+
+  // Adds an ownership relationship between two MemoryAllocatorDump(s) with the
+  // semantics: |source| owns |target|, and has the effect of attributing
+  // the memory usage of |target| to |source|. |importance| is optional and
+  // relevant only for the cases of co-ownership, where it acts as a z-index:
+  // the owner with the highest importance will be attributed |target|'s memory.
+  // If an edge is present, its importance will not be updated unless
+  // |importance| is larger.
+  void AddOwnershipEdge(const MemoryAllocatorDumpGuid& source,
+                        const MemoryAllocatorDumpGuid& target,
+                        int importance);
+  void AddOwnershipEdge(const MemoryAllocatorDumpGuid& source,
+                        const MemoryAllocatorDumpGuid& target);
+
+  // Adds edges that can be overriden by a later or earlier call to
+  // AddOwnershipEdge() with the same source and target with a different
+  // |importance| value.
+  void AddOverridableOwnershipEdge(const MemoryAllocatorDumpGuid& source,
+                                   const MemoryAllocatorDumpGuid& target,
+                                   int importance);
+
+  // Creates ownership edges for memory backed by base::SharedMemory. Handles
+  // the case of cross process sharing and importnace of ownership for the case
+  // with and without the base::SharedMemory dump provider. The new version
+  // should just use global dumps created by SharedMemoryTracker and this
+  // function handles the transition until we get SharedMemory IDs through mojo
+  // channel crbug.com/713763. The weak version creates a weak global dump.
+  // |client_local_dump_guid| The guid of the local dump created by the client
+  // of base::SharedMemory.
+  // |shared_memory_guid| The ID of the base::SharedMemory that is assigned
+  // globally, used to create global dump edges in the new model.
+  // |importance| Importance of the global dump edges to say if the current
+  // process owns the memory segment.
+  void CreateSharedMemoryOwnershipEdge(
+      const MemoryAllocatorDumpGuid& client_local_dump_guid,
+      const UnguessableToken& shared_memory_guid,
+      int importance);
+  void CreateWeakSharedMemoryOwnershipEdge(
+      const MemoryAllocatorDumpGuid& client_local_dump_guid,
+      const UnguessableToken& shared_memory_guid,
+      int importance);
+
+  const AllocatorDumpEdgesMap& allocator_dumps_edges() const {
+    return allocator_dumps_edges_;
+  }
+
+  // Utility method to add a suballocation relationship with the following
+  // semantics: |source| is suballocated from |target_node_name|.
+  // This creates a child node of |target_node_name| and adds an ownership edge
+  // between |source| and the new child node. As a result, the UI will not
+  // account the memory of |source| in the target node.
+  void AddSuballocation(const MemoryAllocatorDumpGuid& source,
+                        const std::string& target_node_name);
+
+  // Removes all the MemoryAllocatorDump(s) contained in this instance. This
+  // ProcessMemoryDump can be safely reused as if it was new once this returns.
+  void Clear();
+
+  // Merges all MemoryAllocatorDump(s) contained in |other| inside this
+  // ProcessMemoryDump, transferring their ownership to this instance.
+  // |other| will be an empty ProcessMemoryDump after this method returns.
+  // This is to allow dump providers to pre-populate ProcessMemoryDump instances
+  // and later move their contents into the ProcessMemoryDump passed as argument
+  // of the MemoryDumpProvider::OnMemoryDump(ProcessMemoryDump*) callback.
+  void TakeAllDumpsFrom(ProcessMemoryDump* other);
+
+  // Populate the traced value with information about the memory allocator
+  // dumps.
+  void SerializeAllocatorDumpsInto(TracedValue* value) const;
+
+  const MemoryDumpArgs& dump_args() const { return dump_args_; }
+
+ private:
+  FRIEND_TEST_ALL_PREFIXES(ProcessMemoryDumpTest, BackgroundModeTest);
+  FRIEND_TEST_ALL_PREFIXES(ProcessMemoryDumpTest, SharedMemoryOwnershipTest);
+  FRIEND_TEST_ALL_PREFIXES(ProcessMemoryDumpTest, GuidsTest);
+
+  MemoryAllocatorDump* AddAllocatorDumpInternal(
+      std::unique_ptr<MemoryAllocatorDump> mad);
+
+  // A per-process token, valid throughout all the lifetime of the current
+  // process, used to disambiguate dumps with the same name generated in
+  // different processes.
+  const UnguessableToken& process_token() const { return process_token_; }
+  void set_process_token_for_testing(UnguessableToken token) {
+    process_token_ = token;
+  };
+
+  // Returns the Guid of the dump for the given |absolute_name| for
+  // for the given process' token. |process_token| is used to disambiguate GUIDs
+  // derived from the same name under different processes.
+  MemoryAllocatorDumpGuid GetDumpId(const std::string& absolute_name);
+
+  void CreateSharedMemoryOwnershipEdgeInternal(
+      const MemoryAllocatorDumpGuid& client_local_dump_guid,
+      const UnguessableToken& shared_memory_guid,
+      int importance,
+      bool is_weak);
+
+  MemoryAllocatorDump* GetBlackHoleMad();
+
+  UnguessableToken process_token_;
+  AllocatorDumpsMap allocator_dumps_;
+
+  // Keeps track of relationships between MemoryAllocatorDump(s).
+  AllocatorDumpEdgesMap allocator_dumps_edges_;
+
+  // Level of detail of the current dump.
+  MemoryDumpArgs dump_args_;
+
+  // This allocator dump is returned when an invalid dump is created in
+  // background mode. The attributes of the dump are ignored and not added to
+  // the trace.
+  std::unique_ptr<MemoryAllocatorDump> black_hole_mad_;
+
+  // When set to true, the DCHECK(s) for invalid dump creations on the
+  // background mode are disabled for testing.
+  static bool is_black_hole_non_fatal_for_testing_;
+
+  DISALLOW_COPY_AND_ASSIGN(ProcessMemoryDump);
+};
+
+}  // namespace trace_event
+}  // namespace base
+
+#endif  // BASE_TRACE_EVENT_PROCESS_MEMORY_DUMP_H_
diff --git a/base/trace_event/process_memory_dump_unittest.cc b/base/trace_event/process_memory_dump_unittest.cc
new file mode 100644
index 0000000..d5f771d
--- /dev/null
+++ b/base/trace_event/process_memory_dump_unittest.cc
@@ -0,0 +1,565 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/trace_event/process_memory_dump.h"
+
+#include <stddef.h>
+
+#include "base/memory/aligned_memory.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/shared_memory_tracker.h"
+#include "base/process/process_metrics.h"
+#include "base/trace_event/memory_allocator_dump_guid.h"
+#include "base/trace_event/memory_infra_background_whitelist.h"
+#include "base/trace_event/trace_event_argument.h"
+#include "base/trace_event/trace_log.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#include "winbase.h"
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+#include <sys/mman.h>
+#endif
+
+#if defined(OS_IOS)
+#include "base/ios/ios_util.h"
+#endif
+
+namespace base {
+namespace trace_event {
+
+namespace {
+
+const MemoryDumpArgs kDetailedDumpArgs = {MemoryDumpLevelOfDetail::DETAILED};
+const char* const kTestDumpNameWhitelist[] = {
+    "Whitelisted/TestName", "Whitelisted/TestName_0x?",
+    "Whitelisted/0x?/TestName", "Whitelisted/0x?", nullptr};
+
+void* Map(size_t size) {
+#if defined(OS_WIN)
+  return ::VirtualAlloc(nullptr, size, MEM_RESERVE | MEM_COMMIT,
+                        PAGE_READWRITE);
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+  return ::mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON,
+                0, 0);
+#endif
+}
+
+void Unmap(void* addr, size_t size) {
+#if defined(OS_WIN)
+  ::VirtualFree(addr, 0, MEM_DECOMMIT);
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+  ::munmap(addr, size);
+#else
+#error This architecture is not (yet) supported.
+#endif
+}
+
+}  // namespace
+
+TEST(ProcessMemoryDumpTest, MoveConstructor) {
+  ProcessMemoryDump pmd1 = ProcessMemoryDump(kDetailedDumpArgs);
+  pmd1.CreateAllocatorDump("mad1");
+  pmd1.CreateAllocatorDump("mad2");
+  pmd1.AddOwnershipEdge(MemoryAllocatorDumpGuid(42),
+                        MemoryAllocatorDumpGuid(4242));
+
+  ProcessMemoryDump pmd2(std::move(pmd1));
+
+  EXPECT_EQ(1u, pmd2.allocator_dumps().count("mad1"));
+  EXPECT_EQ(1u, pmd2.allocator_dumps().count("mad2"));
+  EXPECT_EQ(MemoryDumpLevelOfDetail::DETAILED,
+            pmd2.dump_args().level_of_detail);
+  EXPECT_EQ(1u, pmd2.allocator_dumps_edges().size());
+
+  // Check that calling serialization routines doesn't cause a crash.
+  auto traced_value = std::make_unique<TracedValue>();
+  pmd2.SerializeAllocatorDumpsInto(traced_value.get());
+}
+
+TEST(ProcessMemoryDumpTest, MoveAssignment) {
+  ProcessMemoryDump pmd1 = ProcessMemoryDump(kDetailedDumpArgs);
+  pmd1.CreateAllocatorDump("mad1");
+  pmd1.CreateAllocatorDump("mad2");
+  pmd1.AddOwnershipEdge(MemoryAllocatorDumpGuid(42),
+                        MemoryAllocatorDumpGuid(4242));
+
+  ProcessMemoryDump pmd2({MemoryDumpLevelOfDetail::BACKGROUND});
+  pmd2.CreateAllocatorDump("malloc");
+
+  pmd2 = std::move(pmd1);
+  EXPECT_EQ(1u, pmd2.allocator_dumps().count("mad1"));
+  EXPECT_EQ(1u, pmd2.allocator_dumps().count("mad2"));
+  EXPECT_EQ(0u, pmd2.allocator_dumps().count("mad3"));
+  EXPECT_EQ(MemoryDumpLevelOfDetail::DETAILED,
+            pmd2.dump_args().level_of_detail);
+  EXPECT_EQ(1u, pmd2.allocator_dumps_edges().size());
+
+  // Check that calling serialization routines doesn't cause a crash.
+  auto traced_value = std::make_unique<TracedValue>();
+  pmd2.SerializeAllocatorDumpsInto(traced_value.get());
+}
+
+TEST(ProcessMemoryDumpTest, Clear) {
+  std::unique_ptr<ProcessMemoryDump> pmd1(
+      new ProcessMemoryDump(kDetailedDumpArgs));
+  pmd1->CreateAllocatorDump("mad1");
+  pmd1->CreateAllocatorDump("mad2");
+  ASSERT_FALSE(pmd1->allocator_dumps().empty());
+
+  pmd1->AddOwnershipEdge(MemoryAllocatorDumpGuid(42),
+                         MemoryAllocatorDumpGuid(4242));
+
+  MemoryAllocatorDumpGuid shared_mad_guid1(1);
+  MemoryAllocatorDumpGuid shared_mad_guid2(2);
+  pmd1->CreateSharedGlobalAllocatorDump(shared_mad_guid1);
+  pmd1->CreateSharedGlobalAllocatorDump(shared_mad_guid2);
+
+  pmd1->Clear();
+  ASSERT_TRUE(pmd1->allocator_dumps().empty());
+  ASSERT_TRUE(pmd1->allocator_dumps_edges().empty());
+  ASSERT_EQ(nullptr, pmd1->GetAllocatorDump("mad1"));
+  ASSERT_EQ(nullptr, pmd1->GetAllocatorDump("mad2"));
+  ASSERT_EQ(nullptr, pmd1->GetSharedGlobalAllocatorDump(shared_mad_guid1));
+  ASSERT_EQ(nullptr, pmd1->GetSharedGlobalAllocatorDump(shared_mad_guid2));
+
+  // Check that calling serialization routines doesn't cause a crash.
+  auto traced_value = std::make_unique<TracedValue>();
+  pmd1->SerializeAllocatorDumpsInto(traced_value.get());
+
+  // Check that the pmd can be reused and behaves as expected.
+  auto* mad1 = pmd1->CreateAllocatorDump("mad1");
+  auto* mad3 = pmd1->CreateAllocatorDump("mad3");
+  auto* shared_mad1 = pmd1->CreateSharedGlobalAllocatorDump(shared_mad_guid1);
+  auto* shared_mad2 =
+      pmd1->CreateWeakSharedGlobalAllocatorDump(shared_mad_guid2);
+  ASSERT_EQ(4u, pmd1->allocator_dumps().size());
+  ASSERT_EQ(mad1, pmd1->GetAllocatorDump("mad1"));
+  ASSERT_EQ(nullptr, pmd1->GetAllocatorDump("mad2"));
+  ASSERT_EQ(mad3, pmd1->GetAllocatorDump("mad3"));
+  ASSERT_EQ(shared_mad1, pmd1->GetSharedGlobalAllocatorDump(shared_mad_guid1));
+  ASSERT_EQ(MemoryAllocatorDump::Flags::DEFAULT, shared_mad1->flags());
+  ASSERT_EQ(shared_mad2, pmd1->GetSharedGlobalAllocatorDump(shared_mad_guid2));
+  ASSERT_EQ(MemoryAllocatorDump::Flags::WEAK, shared_mad2->flags());
+
+  traced_value.reset(new TracedValue);
+  pmd1->SerializeAllocatorDumpsInto(traced_value.get());
+
+  pmd1.reset();
+}
+
+TEST(ProcessMemoryDumpTest, TakeAllDumpsFrom) {
+  std::unique_ptr<TracedValue> traced_value(new TracedValue);
+  std::unordered_map<AllocationContext, AllocationMetrics> metrics_by_context;
+  metrics_by_context[AllocationContext()] = {1, 1};
+  TraceEventMemoryOverhead overhead;
+
+  std::unique_ptr<ProcessMemoryDump> pmd1(
+      new ProcessMemoryDump(kDetailedDumpArgs));
+  auto* mad1_1 = pmd1->CreateAllocatorDump("pmd1/mad1");
+  auto* mad1_2 = pmd1->CreateAllocatorDump("pmd1/mad2");
+  pmd1->AddOwnershipEdge(mad1_1->guid(), mad1_2->guid());
+  pmd1->DumpHeapUsage(metrics_by_context, overhead, "pmd1/heap_dump1");
+  pmd1->DumpHeapUsage(metrics_by_context, overhead, "pmd1/heap_dump2");
+
+  std::unique_ptr<ProcessMemoryDump> pmd2(
+      new ProcessMemoryDump(kDetailedDumpArgs));
+  auto* mad2_1 = pmd2->CreateAllocatorDump("pmd2/mad1");
+  auto* mad2_2 = pmd2->CreateAllocatorDump("pmd2/mad2");
+  pmd2->AddOwnershipEdge(mad2_1->guid(), mad2_2->guid());
+  pmd2->DumpHeapUsage(metrics_by_context, overhead, "pmd2/heap_dump1");
+  pmd2->DumpHeapUsage(metrics_by_context, overhead, "pmd2/heap_dump2");
+
+  MemoryAllocatorDumpGuid shared_mad_guid1(1);
+  MemoryAllocatorDumpGuid shared_mad_guid2(2);
+  auto* shared_mad1 = pmd2->CreateSharedGlobalAllocatorDump(shared_mad_guid1);
+  auto* shared_mad2 =
+      pmd2->CreateWeakSharedGlobalAllocatorDump(shared_mad_guid2);
+
+  pmd1->TakeAllDumpsFrom(pmd2.get());
+
+  // Make sure that pmd2 is empty but still usable after it has been emptied.
+  ASSERT_TRUE(pmd2->allocator_dumps().empty());
+  ASSERT_TRUE(pmd2->allocator_dumps_edges().empty());
+  pmd2->CreateAllocatorDump("pmd2/this_mad_stays_with_pmd2");
+  ASSERT_EQ(1u, pmd2->allocator_dumps().size());
+  ASSERT_EQ(1u, pmd2->allocator_dumps().count("pmd2/this_mad_stays_with_pmd2"));
+  pmd2->AddOwnershipEdge(MemoryAllocatorDumpGuid(42),
+                         MemoryAllocatorDumpGuid(4242));
+
+  // Check that calling serialization routines doesn't cause a crash.
+  pmd2->SerializeAllocatorDumpsInto(traced_value.get());
+
+  // Free the |pmd2| to check that the memory ownership of the two MAD(s)
+  // has been transferred to |pmd1|.
+  pmd2.reset();
+
+  // Now check that |pmd1| has been effectively merged.
+  ASSERT_EQ(6u, pmd1->allocator_dumps().size());
+  ASSERT_EQ(1u, pmd1->allocator_dumps().count("pmd1/mad1"));
+  ASSERT_EQ(1u, pmd1->allocator_dumps().count("pmd1/mad2"));
+  ASSERT_EQ(1u, pmd1->allocator_dumps().count("pmd2/mad1"));
+  ASSERT_EQ(1u, pmd1->allocator_dumps().count("pmd1/mad2"));
+  ASSERT_EQ(2u, pmd1->allocator_dumps_edges().size());
+  ASSERT_EQ(shared_mad1, pmd1->GetSharedGlobalAllocatorDump(shared_mad_guid1));
+  ASSERT_EQ(shared_mad2, pmd1->GetSharedGlobalAllocatorDump(shared_mad_guid2));
+  ASSERT_TRUE(MemoryAllocatorDump::Flags::WEAK & shared_mad2->flags());
+
+  // Check that calling serialization routines doesn't cause a crash.
+  traced_value.reset(new TracedValue);
+  pmd1->SerializeAllocatorDumpsInto(traced_value.get());
+
+  pmd1.reset();
+}
+
+TEST(ProcessMemoryDumpTest, OverrideOwnershipEdge) {
+  std::unique_ptr<ProcessMemoryDump> pmd(
+      new ProcessMemoryDump(kDetailedDumpArgs));
+
+  auto* shm_dump1 = pmd->CreateAllocatorDump("shared_mem/seg1");
+  auto* shm_dump2 = pmd->CreateAllocatorDump("shared_mem/seg2");
+  auto* shm_dump3 = pmd->CreateAllocatorDump("shared_mem/seg3");
+  auto* shm_dump4 = pmd->CreateAllocatorDump("shared_mem/seg4");
+
+  // Create one allocation with an auto-assigned guid and mark it as a
+  // suballocation of "fakealloc/allocated_objects".
+  auto* child1_dump = pmd->CreateAllocatorDump("shared_mem/child/seg1");
+  pmd->AddOverridableOwnershipEdge(child1_dump->guid(), shm_dump1->guid(),
+                                   0 /* importance */);
+  auto* child2_dump = pmd->CreateAllocatorDump("shared_mem/child/seg2");
+  pmd->AddOwnershipEdge(child2_dump->guid(), shm_dump2->guid(),
+                        3 /* importance */);
+  MemoryAllocatorDumpGuid shared_mad_guid(1);
+  pmd->CreateSharedGlobalAllocatorDump(shared_mad_guid);
+  pmd->AddOverridableOwnershipEdge(shm_dump3->guid(), shared_mad_guid,
+                                   0 /* importance */);
+  auto* child4_dump = pmd->CreateAllocatorDump("shared_mem/child/seg4");
+  pmd->AddOverridableOwnershipEdge(child4_dump->guid(), shm_dump4->guid(),
+                                   4 /* importance */);
+
+  const ProcessMemoryDump::AllocatorDumpEdgesMap& edges =
+      pmd->allocator_dumps_edges();
+  EXPECT_EQ(4u, edges.size());
+  EXPECT_EQ(shm_dump1->guid(), edges.find(child1_dump->guid())->second.target);
+  EXPECT_EQ(0, edges.find(child1_dump->guid())->second.importance);
+  EXPECT_TRUE(edges.find(child1_dump->guid())->second.overridable);
+  EXPECT_EQ(shm_dump2->guid(), edges.find(child2_dump->guid())->second.target);
+  EXPECT_EQ(3, edges.find(child2_dump->guid())->second.importance);
+  EXPECT_FALSE(edges.find(child2_dump->guid())->second.overridable);
+  EXPECT_EQ(shared_mad_guid, edges.find(shm_dump3->guid())->second.target);
+  EXPECT_EQ(0, edges.find(shm_dump3->guid())->second.importance);
+  EXPECT_TRUE(edges.find(shm_dump3->guid())->second.overridable);
+  EXPECT_EQ(shm_dump4->guid(), edges.find(child4_dump->guid())->second.target);
+  EXPECT_EQ(4, edges.find(child4_dump->guid())->second.importance);
+  EXPECT_TRUE(edges.find(child4_dump->guid())->second.overridable);
+
+  // These should override old edges:
+  pmd->AddOwnershipEdge(child1_dump->guid(), shm_dump1->guid(),
+                        1 /* importance */);
+  pmd->AddOwnershipEdge(shm_dump3->guid(), shared_mad_guid, 2 /* importance */);
+  // This should not change the old edges.
+  pmd->AddOverridableOwnershipEdge(child2_dump->guid(), shm_dump2->guid(),
+                                   0 /* importance */);
+  pmd->AddOwnershipEdge(child4_dump->guid(), shm_dump4->guid(),
+                        0 /* importance */);
+
+  EXPECT_EQ(4u, edges.size());
+  EXPECT_EQ(shm_dump1->guid(), edges.find(child1_dump->guid())->second.target);
+  EXPECT_EQ(1, edges.find(child1_dump->guid())->second.importance);
+  EXPECT_FALSE(edges.find(child1_dump->guid())->second.overridable);
+  EXPECT_EQ(shm_dump2->guid(), edges.find(child2_dump->guid())->second.target);
+  EXPECT_EQ(3, edges.find(child2_dump->guid())->second.importance);
+  EXPECT_FALSE(edges.find(child2_dump->guid())->second.overridable);
+  EXPECT_EQ(shared_mad_guid, edges.find(shm_dump3->guid())->second.target);
+  EXPECT_EQ(2, edges.find(shm_dump3->guid())->second.importance);
+  EXPECT_FALSE(edges.find(shm_dump3->guid())->second.overridable);
+  EXPECT_EQ(shm_dump4->guid(), edges.find(child4_dump->guid())->second.target);
+  EXPECT_EQ(4, edges.find(child4_dump->guid())->second.importance);
+  EXPECT_FALSE(edges.find(child4_dump->guid())->second.overridable);
+}
+
+TEST(ProcessMemoryDumpTest, Suballocations) {
+  std::unique_ptr<ProcessMemoryDump> pmd(
+      new ProcessMemoryDump(kDetailedDumpArgs));
+  const std::string allocator_dump_name = "fakealloc/allocated_objects";
+  pmd->CreateAllocatorDump(allocator_dump_name);
+
+  // Create one allocation with an auto-assigned guid and mark it as a
+  // suballocation of "fakealloc/allocated_objects".
+  auto* pic1_dump = pmd->CreateAllocatorDump("picturemanager/picture1");
+  pmd->AddSuballocation(pic1_dump->guid(), allocator_dump_name);
+
+  // Same here, but this time create an allocation with an explicit guid.
+  auto* pic2_dump = pmd->CreateAllocatorDump("picturemanager/picture2",
+                                            MemoryAllocatorDumpGuid(0x42));
+  pmd->AddSuballocation(pic2_dump->guid(), allocator_dump_name);
+
+  // Now check that AddSuballocation() has created anonymous child dumps under
+  // "fakealloc/allocated_objects".
+  auto anon_node_1_it = pmd->allocator_dumps().find(
+      allocator_dump_name + "/__" + pic1_dump->guid().ToString());
+  ASSERT_NE(pmd->allocator_dumps().end(), anon_node_1_it);
+
+  auto anon_node_2_it =
+      pmd->allocator_dumps().find(allocator_dump_name + "/__42");
+  ASSERT_NE(pmd->allocator_dumps().end(), anon_node_2_it);
+
+  // Finally check that AddSuballocation() has created also the
+  // edges between the pictures and the anonymous allocator child dumps.
+  bool found_edge[2]{false, false};
+  for (const auto& e : pmd->allocator_dumps_edges()) {
+    found_edge[0] |= (e.first == pic1_dump->guid() &&
+                      e.second.target == anon_node_1_it->second->guid());
+    found_edge[1] |= (e.first == pic2_dump->guid() &&
+                      e.second.target == anon_node_2_it->second->guid());
+  }
+  ASSERT_TRUE(found_edge[0]);
+  ASSERT_TRUE(found_edge[1]);
+
+  // Check that calling serialization routines doesn't cause a crash.
+  std::unique_ptr<TracedValue> traced_value(new TracedValue);
+  pmd->SerializeAllocatorDumpsInto(traced_value.get());
+
+  pmd.reset();
+}
+
+TEST(ProcessMemoryDumpTest, GlobalAllocatorDumpTest) {
+  std::unique_ptr<ProcessMemoryDump> pmd(
+      new ProcessMemoryDump(kDetailedDumpArgs));
+  MemoryAllocatorDumpGuid shared_mad_guid(1);
+  auto* shared_mad1 = pmd->CreateWeakSharedGlobalAllocatorDump(shared_mad_guid);
+  ASSERT_EQ(shared_mad_guid, shared_mad1->guid());
+  ASSERT_EQ(MemoryAllocatorDump::Flags::WEAK, shared_mad1->flags());
+
+  auto* shared_mad2 = pmd->GetSharedGlobalAllocatorDump(shared_mad_guid);
+  ASSERT_EQ(shared_mad1, shared_mad2);
+  ASSERT_EQ(MemoryAllocatorDump::Flags::WEAK, shared_mad1->flags());
+
+  auto* shared_mad3 = pmd->CreateWeakSharedGlobalAllocatorDump(shared_mad_guid);
+  ASSERT_EQ(shared_mad1, shared_mad3);
+  ASSERT_EQ(MemoryAllocatorDump::Flags::WEAK, shared_mad1->flags());
+
+  auto* shared_mad4 = pmd->CreateSharedGlobalAllocatorDump(shared_mad_guid);
+  ASSERT_EQ(shared_mad1, shared_mad4);
+  ASSERT_EQ(MemoryAllocatorDump::Flags::DEFAULT, shared_mad1->flags());
+
+  auto* shared_mad5 = pmd->CreateWeakSharedGlobalAllocatorDump(shared_mad_guid);
+  ASSERT_EQ(shared_mad1, shared_mad5);
+  ASSERT_EQ(MemoryAllocatorDump::Flags::DEFAULT, shared_mad1->flags());
+}
+
+TEST(ProcessMemoryDumpTest, SharedMemoryOwnershipTest) {
+  std::unique_ptr<ProcessMemoryDump> pmd(
+      new ProcessMemoryDump(kDetailedDumpArgs));
+  const ProcessMemoryDump::AllocatorDumpEdgesMap& edges =
+      pmd->allocator_dumps_edges();
+
+  auto* client_dump2 = pmd->CreateAllocatorDump("discardable/segment2");
+  auto shm_token2 = UnguessableToken::Create();
+  MemoryAllocatorDumpGuid shm_local_guid2 =
+      pmd->GetDumpId(SharedMemoryTracker::GetDumpNameForTracing(shm_token2));
+  MemoryAllocatorDumpGuid shm_global_guid2 =
+      SharedMemoryTracker::GetGlobalDumpIdForTracing(shm_token2);
+  pmd->AddOverridableOwnershipEdge(shm_local_guid2, shm_global_guid2,
+                                   0 /* importance */);
+
+  pmd->CreateSharedMemoryOwnershipEdge(client_dump2->guid(), shm_token2,
+                                       1 /* importance */);
+  EXPECT_EQ(2u, edges.size());
+
+  EXPECT_EQ(shm_global_guid2, edges.find(shm_local_guid2)->second.target);
+  EXPECT_EQ(1, edges.find(shm_local_guid2)->second.importance);
+  EXPECT_FALSE(edges.find(shm_local_guid2)->second.overridable);
+  EXPECT_EQ(shm_local_guid2, edges.find(client_dump2->guid())->second.target);
+  EXPECT_EQ(1, edges.find(client_dump2->guid())->second.importance);
+  EXPECT_FALSE(edges.find(client_dump2->guid())->second.overridable);
+}
+
+TEST(ProcessMemoryDumpTest, BackgroundModeTest) {
+  MemoryDumpArgs background_args = {MemoryDumpLevelOfDetail::BACKGROUND};
+  std::unique_ptr<ProcessMemoryDump> pmd(
+      new ProcessMemoryDump(background_args));
+  ProcessMemoryDump::is_black_hole_non_fatal_for_testing_ = true;
+  SetAllocatorDumpNameWhitelistForTesting(kTestDumpNameWhitelist);
+  MemoryAllocatorDump* black_hole_mad = pmd->GetBlackHoleMad();
+
+  // GetAllocatorDump works for uncreated dumps.
+  EXPECT_EQ(nullptr, pmd->GetAllocatorDump("NotWhitelisted/TestName"));
+  EXPECT_EQ(nullptr, pmd->GetAllocatorDump("Whitelisted/TestName"));
+
+  // Invalid dump names.
+  EXPECT_EQ(black_hole_mad,
+            pmd->CreateAllocatorDump("NotWhitelisted/TestName"));
+  EXPECT_EQ(black_hole_mad, pmd->CreateAllocatorDump("TestName"));
+  EXPECT_EQ(black_hole_mad, pmd->CreateAllocatorDump("Whitelisted/Test"));
+  EXPECT_EQ(black_hole_mad,
+            pmd->CreateAllocatorDump("Not/Whitelisted/TestName"));
+  EXPECT_EQ(black_hole_mad,
+            pmd->CreateAllocatorDump("Whitelisted/TestName/Google"));
+  EXPECT_EQ(black_hole_mad,
+            pmd->CreateAllocatorDump("Whitelisted/TestName/0x1a2Google"));
+  EXPECT_EQ(black_hole_mad,
+            pmd->CreateAllocatorDump("Whitelisted/TestName/__12/Google"));
+
+  // Suballocations.
+  MemoryAllocatorDumpGuid guid(1);
+  pmd->AddSuballocation(guid, "malloc/allocated_objects");
+  EXPECT_EQ(0u, pmd->allocator_dumps_edges_.size());
+  EXPECT_EQ(0u, pmd->allocator_dumps_.size());
+
+  // Global dumps.
+  EXPECT_NE(black_hole_mad, pmd->CreateSharedGlobalAllocatorDump(guid));
+  EXPECT_NE(black_hole_mad, pmd->CreateWeakSharedGlobalAllocatorDump(guid));
+  EXPECT_NE(black_hole_mad, pmd->GetSharedGlobalAllocatorDump(guid));
+
+  // Valid dump names.
+  EXPECT_NE(black_hole_mad, pmd->CreateAllocatorDump("Whitelisted/TestName"));
+  EXPECT_NE(black_hole_mad,
+            pmd->CreateAllocatorDump("Whitelisted/TestName_0xA1b2"));
+  EXPECT_NE(black_hole_mad,
+            pmd->CreateAllocatorDump("Whitelisted/0xaB/TestName"));
+
+  // GetAllocatorDump is consistent.
+  EXPECT_EQ(nullptr, pmd->GetAllocatorDump("NotWhitelisted/TestName"));
+  EXPECT_NE(black_hole_mad, pmd->GetAllocatorDump("Whitelisted/TestName"));
+
+  // Test whitelisted entries.
+  ASSERT_TRUE(IsMemoryAllocatorDumpNameWhitelisted("Whitelisted/TestName"));
+
+  // Global dumps should be whitelisted.
+  ASSERT_TRUE(IsMemoryAllocatorDumpNameWhitelisted("global/13456"));
+
+  // Global dumps with non-guids should not be.
+  ASSERT_FALSE(IsMemoryAllocatorDumpNameWhitelisted("global/random"));
+
+  // Random names should not.
+  ASSERT_FALSE(IsMemoryAllocatorDumpNameWhitelisted("NotWhitelisted/TestName"));
+
+  // Check hex processing.
+  ASSERT_TRUE(IsMemoryAllocatorDumpNameWhitelisted("Whitelisted/0xA1b2"));
+}
+
+TEST(ProcessMemoryDumpTest, GuidsTest) {
+  MemoryDumpArgs dump_args = {MemoryDumpLevelOfDetail::DETAILED};
+
+  const auto process_token_one = UnguessableToken::Create();
+  const auto process_token_two = UnguessableToken::Create();
+
+  ProcessMemoryDump pmd1(dump_args);
+  pmd1.set_process_token_for_testing(process_token_one);
+  MemoryAllocatorDump* mad1 = pmd1.CreateAllocatorDump("foo");
+
+  ProcessMemoryDump pmd2(dump_args);
+  pmd2.set_process_token_for_testing(process_token_one);
+  MemoryAllocatorDump* mad2 = pmd2.CreateAllocatorDump("foo");
+
+  // If we don't pass the argument we get a random PMD:
+  ProcessMemoryDump pmd3(dump_args);
+  MemoryAllocatorDump* mad3 = pmd3.CreateAllocatorDump("foo");
+
+  // PMD's for different processes produce different GUIDs even for the same
+  // names:
+  ProcessMemoryDump pmd4(dump_args);
+  pmd4.set_process_token_for_testing(process_token_two);
+  MemoryAllocatorDump* mad4 = pmd4.CreateAllocatorDump("foo");
+
+  ASSERT_EQ(mad1->guid(), mad2->guid());
+
+  ASSERT_NE(mad2->guid(), mad3->guid());
+  ASSERT_NE(mad3->guid(), mad4->guid());
+  ASSERT_NE(mad4->guid(), mad2->guid());
+
+  ASSERT_EQ(mad1->guid(), pmd1.GetDumpId("foo"));
+}
+
+#if defined(COUNT_RESIDENT_BYTES_SUPPORTED)
+#if defined(OS_FUCHSIA)
+// TODO(crbug.com/851760): Counting resident bytes is not supported on Fuchsia.
+#define MAYBE_CountResidentBytes DISABLED_CountResidentBytes
+#else
+#define MAYBE_CountResidentBytes CountResidentBytes
+#endif
+TEST(ProcessMemoryDumpTest, MAYBE_CountResidentBytes) {
+  const size_t page_size = ProcessMemoryDump::GetSystemPageSize();
+
+  // Allocate few page of dirty memory and check if it is resident.
+  const size_t size1 = 5 * page_size;
+  void* memory1 = Map(size1);
+  memset(memory1, 0, size1);
+  size_t res1 = ProcessMemoryDump::CountResidentBytes(memory1, size1);
+  ASSERT_EQ(res1, size1);
+  Unmap(memory1, size1);
+
+  // Allocate a large memory segment (> 8Mib).
+  const size_t kVeryLargeMemorySize = 15 * 1024 * 1024;
+  void* memory2 = Map(kVeryLargeMemorySize);
+  memset(memory2, 0, kVeryLargeMemorySize);
+  size_t res2 =
+      ProcessMemoryDump::CountResidentBytes(memory2, kVeryLargeMemorySize);
+  ASSERT_EQ(res2, kVeryLargeMemorySize);
+  Unmap(memory2, kVeryLargeMemorySize);
+}
+
+#if defined(OS_FUCHSIA)
+// TODO(crbug.com/851760): Counting resident bytes is not supported on Fuchsia.
+#define MAYBE_CountResidentBytesInSharedMemory \
+  DISABLED_CountResidentBytesInSharedMemory
+#else
+#define MAYBE_CountResidentBytesInSharedMemory CountResidentBytesInSharedMemory
+#endif
+TEST(ProcessMemoryDumpTest, MAYBE_CountResidentBytesInSharedMemory) {
+#if defined(OS_IOS)
+  // TODO(crbug.com/748410): Reenable this test.
+  if (!base::ios::IsRunningOnIOS10OrLater()) {
+    return;
+  }
+#endif
+
+  const size_t page_size = ProcessMemoryDump::GetSystemPageSize();
+
+  // Allocate few page of dirty memory and check if it is resident.
+  const size_t size1 = 5 * page_size;
+  SharedMemory shared_memory1;
+  shared_memory1.CreateAndMapAnonymous(size1);
+  memset(shared_memory1.memory(), 0, size1);
+  base::Optional<size_t> res1 =
+      ProcessMemoryDump::CountResidentBytesInSharedMemory(
+          shared_memory1.memory(), shared_memory1.mapped_size());
+  ASSERT_TRUE(res1.has_value());
+  ASSERT_EQ(res1.value(), size1);
+  shared_memory1.Unmap();
+  shared_memory1.Close();
+
+  // Allocate a large memory segment (> 8Mib).
+  const size_t kVeryLargeMemorySize = 15 * 1024 * 1024;
+  SharedMemory shared_memory2;
+  shared_memory2.CreateAndMapAnonymous(kVeryLargeMemorySize);
+  memset(shared_memory2.memory(), 0, kVeryLargeMemorySize);
+  base::Optional<size_t> res2 =
+      ProcessMemoryDump::CountResidentBytesInSharedMemory(
+          shared_memory2.memory(), shared_memory2.mapped_size());
+  ASSERT_TRUE(res2.has_value());
+  ASSERT_EQ(res2.value(), kVeryLargeMemorySize);
+  shared_memory2.Unmap();
+  shared_memory2.Close();
+
+  // Allocate a large memory segment, but touch about half of all pages.
+  const size_t kTouchedMemorySize = 7 * 1024 * 1024;
+  SharedMemory shared_memory3;
+  shared_memory3.CreateAndMapAnonymous(kVeryLargeMemorySize);
+  memset(shared_memory3.memory(), 0, kTouchedMemorySize);
+  base::Optional<size_t> res3 =
+      ProcessMemoryDump::CountResidentBytesInSharedMemory(
+          shared_memory3.memory(), shared_memory3.mapped_size());
+  ASSERT_TRUE(res3.has_value());
+  ASSERT_EQ(res3.value(), kTouchedMemorySize);
+  shared_memory3.Unmap();
+  shared_memory3.Close();
+}
+#endif  // defined(COUNT_RESIDENT_BYTES_SUPPORTED)
+
+}  // namespace trace_event
+}  // namespace base
diff --git a/base/trace_event/trace_buffer.cc b/base/trace_event/trace_buffer.cc
new file mode 100644
index 0000000..8de470f
--- /dev/null
+++ b/base/trace_event/trace_buffer.cc
@@ -0,0 +1,347 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/trace_event/trace_buffer.h"
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/trace_event/heap_profiler.h"
+#include "base/trace_event/trace_event_impl.h"
+
+namespace base {
+namespace trace_event {
+
+namespace {
+
+class TraceBufferRingBuffer : public TraceBuffer {
+ public:
+  TraceBufferRingBuffer(size_t max_chunks)
+      : max_chunks_(max_chunks),
+        recyclable_chunks_queue_(new size_t[queue_capacity()]),
+        queue_head_(0),
+        queue_tail_(max_chunks),
+        current_iteration_index_(0),
+        current_chunk_seq_(1) {
+    chunks_.reserve(max_chunks);
+    for (size_t i = 0; i < max_chunks; ++i)
+      recyclable_chunks_queue_[i] = i;
+  }
+
+  std::unique_ptr<TraceBufferChunk> GetChunk(size_t* index) override {
+    HEAP_PROFILER_SCOPED_IGNORE;
+
+    // Because the number of threads is much less than the number of chunks,
+    // the queue should never be empty.
+    DCHECK(!QueueIsEmpty());
+
+    *index = recyclable_chunks_queue_[queue_head_];
+    queue_head_ = NextQueueIndex(queue_head_);
+    current_iteration_index_ = queue_head_;
+
+    if (*index >= chunks_.size())
+      chunks_.resize(*index + 1);
+
+    TraceBufferChunk* chunk = chunks_[*index].release();
+    chunks_[*index] = nullptr;  // Put nullptr in the slot of a in-flight chunk.
+    if (chunk)
+      chunk->Reset(current_chunk_seq_++);
+    else
+      chunk = new TraceBufferChunk(current_chunk_seq_++);
+
+    return std::unique_ptr<TraceBufferChunk>(chunk);
+  }
+
+  void ReturnChunk(size_t index,
+                   std::unique_ptr<TraceBufferChunk> chunk) override {
+    // When this method is called, the queue should not be full because it
+    // can contain all chunks including the one to be returned.
+    DCHECK(!QueueIsFull());
+    DCHECK(chunk);
+    DCHECK_LT(index, chunks_.size());
+    DCHECK(!chunks_[index]);
+    chunks_[index] = std::move(chunk);
+    recyclable_chunks_queue_[queue_tail_] = index;
+    queue_tail_ = NextQueueIndex(queue_tail_);
+  }
+
+  bool IsFull() const override { return false; }
+
+  size_t Size() const override {
+    // This is approximate because not all of the chunks are full.
+    return chunks_.size() * TraceBufferChunk::kTraceBufferChunkSize;
+  }
+
+  size_t Capacity() const override {
+    return max_chunks_ * TraceBufferChunk::kTraceBufferChunkSize;
+  }
+
+  TraceEvent* GetEventByHandle(TraceEventHandle handle) override {
+    if (handle.chunk_index >= chunks_.size())
+      return nullptr;
+    TraceBufferChunk* chunk = chunks_[handle.chunk_index].get();
+    if (!chunk || chunk->seq() != handle.chunk_seq)
+      return nullptr;
+    return chunk->GetEventAt(handle.event_index);
+  }
+
+  const TraceBufferChunk* NextChunk() override {
+    if (chunks_.empty())
+      return nullptr;
+
+    while (current_iteration_index_ != queue_tail_) {
+      size_t chunk_index = recyclable_chunks_queue_[current_iteration_index_];
+      current_iteration_index_ = NextQueueIndex(current_iteration_index_);
+      if (chunk_index >= chunks_.size())  // Skip uninitialized chunks.
+        continue;
+      DCHECK(chunks_[chunk_index]);
+      return chunks_[chunk_index].get();
+    }
+    return nullptr;
+  }
+
+  void EstimateTraceMemoryOverhead(
+      TraceEventMemoryOverhead* overhead) override {
+    overhead->Add(TraceEventMemoryOverhead::kTraceBuffer, sizeof(*this));
+    for (size_t queue_index = queue_head_; queue_index != queue_tail_;
+         queue_index = NextQueueIndex(queue_index)) {
+      size_t chunk_index = recyclable_chunks_queue_[queue_index];
+      if (chunk_index >= chunks_.size())  // Skip uninitialized chunks.
+        continue;
+      chunks_[chunk_index]->EstimateTraceMemoryOverhead(overhead);
+    }
+  }
+
+ private:
+  bool QueueIsEmpty() const { return queue_head_ == queue_tail_; }
+
+  size_t QueueSize() const {
+    return queue_tail_ > queue_head_
+               ? queue_tail_ - queue_head_
+               : queue_tail_ + queue_capacity() - queue_head_;
+  }
+
+  bool QueueIsFull() const { return QueueSize() == queue_capacity() - 1; }
+
+  size_t queue_capacity() const {
+    // One extra space to help distinguish full state and empty state.
+    return max_chunks_ + 1;
+  }
+
+  size_t NextQueueIndex(size_t index) const {
+    index++;
+    if (index >= queue_capacity())
+      index = 0;
+    return index;
+  }
+
+  size_t max_chunks_;
+  std::vector<std::unique_ptr<TraceBufferChunk>> chunks_;
+
+  std::unique_ptr<size_t[]> recyclable_chunks_queue_;
+  size_t queue_head_;
+  size_t queue_tail_;
+
+  size_t current_iteration_index_;
+  uint32_t current_chunk_seq_;
+
+  DISALLOW_COPY_AND_ASSIGN(TraceBufferRingBuffer);
+};
+
+class TraceBufferVector : public TraceBuffer {
+ public:
+  TraceBufferVector(size_t max_chunks)
+      : in_flight_chunk_count_(0),
+        current_iteration_index_(0),
+        max_chunks_(max_chunks) {
+    chunks_.reserve(max_chunks_);
+  }
+
+  std::unique_ptr<TraceBufferChunk> GetChunk(size_t* index) override {
+    HEAP_PROFILER_SCOPED_IGNORE;
+
+    // This function may be called when adding normal events or indirectly from
+    // AddMetadataEventsWhileLocked(). We can not DECHECK(!IsFull()) because we
+    // have to add the metadata events and flush thread-local buffers even if
+    // the buffer is full.
+    *index = chunks_.size();
+    // Put nullptr in the slot of a in-flight chunk.
+    chunks_.push_back(nullptr);
+    ++in_flight_chunk_count_;
+    // + 1 because zero chunk_seq is not allowed.
+    return std::unique_ptr<TraceBufferChunk>(
+        new TraceBufferChunk(static_cast<uint32_t>(*index) + 1));
+  }
+
+  void ReturnChunk(size_t index,
+                   std::unique_ptr<TraceBufferChunk> chunk) override {
+    DCHECK_GT(in_flight_chunk_count_, 0u);
+    DCHECK_LT(index, chunks_.size());
+    DCHECK(!chunks_[index]);
+    --in_flight_chunk_count_;
+    chunks_[index] = std::move(chunk);
+  }
+
+  bool IsFull() const override { return chunks_.size() >= max_chunks_; }
+
+  size_t Size() const override {
+    // This is approximate because not all of the chunks are full.
+    return chunks_.size() * TraceBufferChunk::kTraceBufferChunkSize;
+  }
+
+  size_t Capacity() const override {
+    return max_chunks_ * TraceBufferChunk::kTraceBufferChunkSize;
+  }
+
+  TraceEvent* GetEventByHandle(TraceEventHandle handle) override {
+    if (handle.chunk_index >= chunks_.size())
+      return nullptr;
+    TraceBufferChunk* chunk = chunks_[handle.chunk_index].get();
+    if (!chunk || chunk->seq() != handle.chunk_seq)
+      return nullptr;
+    return chunk->GetEventAt(handle.event_index);
+  }
+
+  const TraceBufferChunk* NextChunk() override {
+    while (current_iteration_index_ < chunks_.size()) {
+      // Skip in-flight chunks.
+      const TraceBufferChunk* chunk = chunks_[current_iteration_index_++].get();
+      if (chunk)
+        return chunk;
+    }
+    return nullptr;
+  }
+
+  void EstimateTraceMemoryOverhead(
+      TraceEventMemoryOverhead* overhead) override {
+    const size_t chunks_ptr_vector_allocated_size =
+        sizeof(*this) + max_chunks_ * sizeof(decltype(chunks_)::value_type);
+    const size_t chunks_ptr_vector_resident_size =
+        sizeof(*this) + chunks_.size() * sizeof(decltype(chunks_)::value_type);
+    overhead->Add(TraceEventMemoryOverhead::kTraceBuffer,
+                  chunks_ptr_vector_allocated_size,
+                  chunks_ptr_vector_resident_size);
+    for (size_t i = 0; i < chunks_.size(); ++i) {
+      TraceBufferChunk* chunk = chunks_[i].get();
+      // Skip the in-flight (nullptr) chunks. They will be accounted by the
+      // per-thread-local dumpers, see ThreadLocalEventBuffer::OnMemoryDump.
+      if (chunk)
+        chunk->EstimateTraceMemoryOverhead(overhead);
+    }
+  }
+
+ private:
+  size_t in_flight_chunk_count_;
+  size_t current_iteration_index_;
+  size_t max_chunks_;
+  std::vector<std::unique_ptr<TraceBufferChunk>> chunks_;
+
+  DISALLOW_COPY_AND_ASSIGN(TraceBufferVector);
+};
+
+}  // namespace
+
+TraceBufferChunk::TraceBufferChunk(uint32_t seq) : next_free_(0), seq_(seq) {}
+
+TraceBufferChunk::~TraceBufferChunk() = default;
+
+void TraceBufferChunk::Reset(uint32_t new_seq) {
+  for (size_t i = 0; i < next_free_; ++i)
+    chunk_[i].Reset();
+  next_free_ = 0;
+  seq_ = new_seq;
+  cached_overhead_estimate_.reset();
+}
+
+TraceEvent* TraceBufferChunk::AddTraceEvent(size_t* event_index) {
+  DCHECK(!IsFull());
+  *event_index = next_free_++;
+  return &chunk_[*event_index];
+}
+
+void TraceBufferChunk::EstimateTraceMemoryOverhead(
+    TraceEventMemoryOverhead* overhead) {
+  if (!cached_overhead_estimate_) {
+    cached_overhead_estimate_.reset(new TraceEventMemoryOverhead);
+
+    // When estimating the size of TraceBufferChunk, exclude the array of trace
+    // events, as they are computed individually below.
+    cached_overhead_estimate_->Add(TraceEventMemoryOverhead::kTraceBufferChunk,
+                                   sizeof(*this) - sizeof(chunk_));
+  }
+
+  const size_t num_cached_estimated_events =
+      cached_overhead_estimate_->GetCount(
+          TraceEventMemoryOverhead::kTraceEvent);
+  DCHECK_LE(num_cached_estimated_events, size());
+
+  if (IsFull() && num_cached_estimated_events == size()) {
+    overhead->Update(*cached_overhead_estimate_);
+    return;
+  }
+
+  for (size_t i = num_cached_estimated_events; i < size(); ++i)
+    chunk_[i].EstimateTraceMemoryOverhead(cached_overhead_estimate_.get());
+
+  if (IsFull()) {
+    cached_overhead_estimate_->AddSelf();
+  } else {
+    // The unused TraceEvents in |chunks_| are not cached. They will keep
+    // changing as new TraceEvents are added to this chunk, so they are
+    // computed on the fly.
+    const size_t num_unused_trace_events = capacity() - size();
+    overhead->Add(TraceEventMemoryOverhead::kUnusedTraceEvent,
+                  num_unused_trace_events * sizeof(TraceEvent));
+  }
+
+  overhead->Update(*cached_overhead_estimate_);
+}
+
+TraceResultBuffer::OutputCallback
+TraceResultBuffer::SimpleOutput::GetCallback() {
+  return Bind(&SimpleOutput::Append, Unretained(this));
+}
+
+void TraceResultBuffer::SimpleOutput::Append(
+    const std::string& json_trace_output) {
+  json_output += json_trace_output;
+}
+
+TraceResultBuffer::TraceResultBuffer() : append_comma_(false) {}
+
+TraceResultBuffer::~TraceResultBuffer() = default;
+
+void TraceResultBuffer::SetOutputCallback(
+    const OutputCallback& json_chunk_callback) {
+  output_callback_ = json_chunk_callback;
+}
+
+void TraceResultBuffer::Start() {
+  append_comma_ = false;
+  output_callback_.Run("[");
+}
+
+void TraceResultBuffer::AddFragment(const std::string& trace_fragment) {
+  if (append_comma_)
+    output_callback_.Run(",");
+  append_comma_ = true;
+  output_callback_.Run(trace_fragment);
+}
+
+void TraceResultBuffer::Finish() {
+  output_callback_.Run("]");
+}
+
+TraceBuffer* TraceBuffer::CreateTraceBufferRingBuffer(size_t max_chunks) {
+  return new TraceBufferRingBuffer(max_chunks);
+}
+
+TraceBuffer* TraceBuffer::CreateTraceBufferVectorOfSize(size_t max_chunks) {
+  return new TraceBufferVector(max_chunks);
+}
+
+}  // namespace trace_event
+}  // namespace base
diff --git a/base/trace_event/trace_buffer.h b/base/trace_event/trace_buffer.h
new file mode 100644
index 0000000..3d6465f
--- /dev/null
+++ b/base/trace_event/trace_buffer.h
@@ -0,0 +1,130 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TRACE_EVENT_TRACE_BUFFER_H_
+#define BASE_TRACE_EVENT_TRACE_BUFFER_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "base/base_export.h"
+#include "base/trace_event/trace_event.h"
+#include "base/trace_event/trace_event_impl.h"
+
+namespace base {
+
+namespace trace_event {
+
+// TraceBufferChunk is the basic unit of TraceBuffer.
+class BASE_EXPORT TraceBufferChunk {
+ public:
+  explicit TraceBufferChunk(uint32_t seq);
+  ~TraceBufferChunk();
+
+  void Reset(uint32_t new_seq);
+  TraceEvent* AddTraceEvent(size_t* event_index);
+  bool IsFull() const { return next_free_ == kTraceBufferChunkSize; }
+
+  uint32_t seq() const { return seq_; }
+  size_t capacity() const { return kTraceBufferChunkSize; }
+  size_t size() const { return next_free_; }
+
+  TraceEvent* GetEventAt(size_t index) {
+    DCHECK(index < size());
+    return &chunk_[index];
+  }
+  const TraceEvent* GetEventAt(size_t index) const {
+    DCHECK(index < size());
+    return &chunk_[index];
+  }
+
+  void EstimateTraceMemoryOverhead(TraceEventMemoryOverhead* overhead);
+
+  // These values must be kept consistent with the numbers of bits of
+  // chunk_index and event_index fields in TraceEventHandle
+  // (in trace_event_impl.h).
+  static const size_t kMaxChunkIndex = (1u << 26) - 1;
+  static const size_t kTraceBufferChunkSize = 64;
+
+ private:
+  size_t next_free_;
+  std::unique_ptr<TraceEventMemoryOverhead> cached_overhead_estimate_;
+  TraceEvent chunk_[kTraceBufferChunkSize];
+  uint32_t seq_;
+};
+
+// TraceBuffer holds the events as they are collected.
+class BASE_EXPORT TraceBuffer {
+ public:
+  virtual ~TraceBuffer() = default;
+
+  virtual std::unique_ptr<TraceBufferChunk> GetChunk(size_t* index) = 0;
+  virtual void ReturnChunk(size_t index,
+                           std::unique_ptr<TraceBufferChunk> chunk) = 0;
+
+  virtual bool IsFull() const = 0;
+  virtual size_t Size() const = 0;
+  virtual size_t Capacity() const = 0;
+  virtual TraceEvent* GetEventByHandle(TraceEventHandle handle) = 0;
+
+  // For iteration. Each TraceBuffer can only be iterated once.
+  virtual const TraceBufferChunk* NextChunk() = 0;
+
+
+  // Computes an estimate of the size of the buffer, including all the retained
+  // objects.
+  virtual void EstimateTraceMemoryOverhead(
+      TraceEventMemoryOverhead* overhead) = 0;
+
+  static TraceBuffer* CreateTraceBufferRingBuffer(size_t max_chunks);
+  static TraceBuffer* CreateTraceBufferVectorOfSize(size_t max_chunks);
+};
+
+// TraceResultBuffer collects and converts trace fragments returned by TraceLog
+// to JSON output.
+class BASE_EXPORT TraceResultBuffer {
+ public:
+  typedef base::Callback<void(const std::string&)> OutputCallback;
+
+  // If you don't need to stream JSON chunks out efficiently, and just want to
+  // get a complete JSON string after calling Finish, use this struct to collect
+  // JSON trace output.
+  struct BASE_EXPORT SimpleOutput {
+    OutputCallback GetCallback();
+    void Append(const std::string& json_string);
+
+    // Do what you want with the json_output_ string after calling
+    // TraceResultBuffer::Finish.
+    std::string json_output;
+  };
+
+  TraceResultBuffer();
+  ~TraceResultBuffer();
+
+  // Set callback. The callback will be called during Start with the initial
+  // JSON output and during AddFragment and Finish with following JSON output
+  // chunks. The callback target must live past the last calls to
+  // TraceResultBuffer::Start/AddFragment/Finish.
+  void SetOutputCallback(const OutputCallback& json_chunk_callback);
+
+  // Start JSON output. This resets all internal state, so you can reuse
+  // the TraceResultBuffer by calling Start.
+  void Start();
+
+  // Call AddFragment 0 or more times to add trace fragments from TraceLog.
+  void AddFragment(const std::string& trace_fragment);
+
+  // When all fragments have been added, call Finish to complete the JSON
+  // formatted output.
+  void Finish();
+
+ private:
+  OutputCallback output_callback_;
+  bool append_comma_;
+};
+
+}  // namespace trace_event
+}  // namespace base
+
+#endif  // BASE_TRACE_EVENT_TRACE_BUFFER_H_
diff --git a/base/trace_event/trace_category.h b/base/trace_event/trace_category.h
new file mode 100644
index 0000000..792bc5e
--- /dev/null
+++ b/base/trace_event/trace_category.h
@@ -0,0 +1,109 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TRACE_EVENT_TRACE_CATEGORY_H_
+#define BASE_TRACE_EVENT_TRACE_CATEGORY_H_
+
+#include <stdint.h>
+
+namespace base {
+namespace trace_event {
+
+// Captures the state of an invidivual trace category. Nothing except tracing
+// internals (e.g., TraceLog) is supposed to have non-const Category pointers.
+struct TraceCategory {
+  // The TRACE_EVENT macros should only use this value as a bool.
+  // These enum values are effectively a public API and third_party projects
+  // depend on their value. Hence, never remove or recycle existing bits, unless
+  // you are sure that all the third-party projects that depend on this have
+  // been updated.
+  enum StateFlags : uint8_t {
+    ENABLED_FOR_RECORDING = 1 << 0,
+
+    // Not used anymore.
+    DEPRECATED_ENABLED_FOR_MONITORING = 1 << 1,
+    DEPRECATED_ENABLED_FOR_EVENT_CALLBACK = 1 << 2,
+
+    ENABLED_FOR_ETW_EXPORT = 1 << 3,
+    ENABLED_FOR_FILTERING = 1 << 4
+  };
+
+  static const TraceCategory* FromStatePtr(const uint8_t* state_ptr) {
+    static_assert(
+        offsetof(TraceCategory, state_) == 0,
+        "|state_| must be the first field of the TraceCategory class.");
+    return reinterpret_cast<const TraceCategory*>(state_ptr);
+  }
+
+  bool is_valid() const { return name_ != nullptr; }
+  void set_name(const char* name) { name_ = name; }
+  const char* name() const {
+    DCHECK(is_valid());
+    return name_;
+  }
+
+  // TODO(primiano): This is an intermediate solution to deal with the fact that
+  // today TRACE_EVENT* macros cache the state ptr. They should just cache the
+  // full TraceCategory ptr, which is immutable, and use these helper function
+  // here. This will get rid of the need of this awkward ptr getter completely.
+  const uint8_t* state_ptr() const {
+    return const_cast<const uint8_t*>(&state_);
+  }
+
+  uint8_t state() const {
+    return *const_cast<volatile const uint8_t*>(&state_);
+  }
+
+  bool is_enabled() const { return state() != 0; }
+
+  void set_state(uint8_t state) {
+    *const_cast<volatile uint8_t*>(&state_) = state;
+  }
+
+  void clear_state_flag(StateFlags flag) { set_state(state() & (~flag)); }
+  void set_state_flag(StateFlags flag) { set_state(state() | flag); }
+
+  uint32_t enabled_filters() const {
+    return *const_cast<volatile const uint32_t*>(&enabled_filters_);
+  }
+
+  bool is_filter_enabled(size_t index) const {
+    DCHECK(index < sizeof(enabled_filters_) * 8);
+    return (enabled_filters() & (1 << index)) != 0;
+  }
+
+  void set_enabled_filters(uint32_t enabled_filters) {
+    *const_cast<volatile uint32_t*>(&enabled_filters_) = enabled_filters;
+  }
+
+  void reset_for_testing() {
+    set_state(0);
+    set_enabled_filters(0);
+  }
+
+  // These fields should not be accessed directly, not even by tracing code.
+  // The only reason why these are not private is because it makes it impossible
+  // to have a global array of TraceCategory in category_registry.cc without
+  // creating initializers. See discussion on goo.gl/qhZN94 and
+  // crbug.com/{660967,660828}.
+
+  // The enabled state. TRACE_EVENT* macros will capture events if any of the
+  // flags here are set. Since TRACE_EVENTx macros are used in a lot of
+  // fast-paths, accesses to this field are non-barriered and racy by design.
+  // This field is mutated when starting/stopping tracing and we don't care
+  // about missing some events.
+  uint8_t state_;
+
+  // When ENABLED_FOR_FILTERING is set, this contains a bitmap to the
+  // corresponding filter (see event_filters.h).
+  uint32_t enabled_filters_;
+
+  // TraceCategory group names are long lived static strings.
+  const char* name_;
+};
+
+}  // namespace trace_event
+}  // namespace base
+
+#endif  // BASE_TRACE_EVENT_TRACE_CATEGORY_H_
diff --git a/base/trace_event/trace_category_unittest.cc b/base/trace_event/trace_category_unittest.cc
new file mode 100644
index 0000000..1370f5e
--- /dev/null
+++ b/base/trace_event/trace_category_unittest.cc
@@ -0,0 +1,155 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <string.h>
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/lazy_instance.h"
+#include "base/synchronization/lock.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread.h"
+#include "base/trace_event/category_registry.h"
+#include "base/trace_event/trace_category.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace trace_event {
+
+// Static initializers are generally forbidden. However, in the past we ran in
+// the case of some test using tracing in a static initializer. This test checks
+// That the category registry doesn't rely on static initializers itself and is
+// functional even if called from another static initializer.
+bool Initializer() {
+  return CategoryRegistry::kCategoryMetadata &&
+         CategoryRegistry::kCategoryMetadata->is_valid();
+}
+bool g_initializer_check = Initializer();
+
+class TraceCategoryTest : public testing::Test {
+ public:
+  void SetUp() override { CategoryRegistry::Initialize(); }
+
+  void TearDown() override { CategoryRegistry::ResetForTesting(); }
+
+  static bool GetOrCreateCategoryByName(const char* name, TraceCategory** cat) {
+    static LazyInstance<Lock>::Leaky g_lock = LAZY_INSTANCE_INITIALIZER;
+    bool is_new_cat = false;
+    *cat = CategoryRegistry::GetCategoryByName(name);
+    if (!*cat) {
+      AutoLock lock(g_lock.Get());
+      is_new_cat = CategoryRegistry::GetOrCreateCategoryLocked(
+          name, [](TraceCategory*) {}, cat);
+    }
+    return is_new_cat;
+  };
+
+  static CategoryRegistry::Range GetAllCategories() {
+    return CategoryRegistry::GetAllCategories();
+  }
+
+  static void TestRaceThreadMain(WaitableEvent* event) {
+    TraceCategory* cat = nullptr;
+    event->Wait();
+    GetOrCreateCategoryByName("__test_race", &cat);
+    EXPECT_NE(nullptr, cat);
+  }
+};
+
+TEST_F(TraceCategoryTest, Basic) {
+  ASSERT_NE(nullptr, CategoryRegistry::kCategoryMetadata);
+  ASSERT_TRUE(CategoryRegistry::kCategoryMetadata->is_valid());
+  ASSERT_FALSE(CategoryRegistry::kCategoryMetadata->is_enabled());
+
+  // Metadata category is built-in and should create a new category.
+  TraceCategory* cat_meta = nullptr;
+  const char* kMetadataName = CategoryRegistry::kCategoryMetadata->name();
+  ASSERT_FALSE(GetOrCreateCategoryByName(kMetadataName, &cat_meta));
+  ASSERT_EQ(CategoryRegistry::kCategoryMetadata, cat_meta);
+
+  TraceCategory* cat_1 = nullptr;
+  ASSERT_TRUE(GetOrCreateCategoryByName("__test_basic_ab", &cat_1));
+  ASSERT_FALSE(cat_1->is_enabled());
+  ASSERT_EQ(0u, cat_1->enabled_filters());
+  cat_1->set_state_flag(TraceCategory::ENABLED_FOR_RECORDING);
+  cat_1->set_state_flag(TraceCategory::ENABLED_FOR_FILTERING);
+  ASSERT_EQ(TraceCategory::ENABLED_FOR_RECORDING |
+                TraceCategory::ENABLED_FOR_FILTERING,
+            cat_1->state());
+
+  cat_1->set_enabled_filters(129);
+  ASSERT_EQ(129u, cat_1->enabled_filters());
+  ASSERT_EQ(cat_1, CategoryRegistry::GetCategoryByStatePtr(cat_1->state_ptr()));
+
+  cat_1->clear_state_flag(TraceCategory::ENABLED_FOR_FILTERING);
+  ASSERT_EQ(TraceCategory::ENABLED_FOR_RECORDING, cat_1->state());
+  ASSERT_EQ(TraceCategory::ENABLED_FOR_RECORDING, *cat_1->state_ptr());
+  ASSERT_TRUE(cat_1->is_enabled());
+
+  TraceCategory* cat_2 = nullptr;
+  ASSERT_TRUE(GetOrCreateCategoryByName("__test_basic_a", &cat_2));
+  ASSERT_FALSE(cat_2->is_enabled());
+  cat_2->set_state_flag(TraceCategory::ENABLED_FOR_RECORDING);
+
+  TraceCategory* cat_2_copy = nullptr;
+  ASSERT_FALSE(GetOrCreateCategoryByName("__test_basic_a", &cat_2_copy));
+  ASSERT_EQ(cat_2, cat_2_copy);
+
+  TraceCategory* cat_3 = nullptr;
+  ASSERT_TRUE(
+      GetOrCreateCategoryByName("__test_basic_ab,__test_basic_a", &cat_3));
+  ASSERT_FALSE(cat_3->is_enabled());
+  ASSERT_EQ(0u, cat_3->enabled_filters());
+
+  int num_test_categories_seen = 0;
+  for (const TraceCategory& cat : GetAllCategories()) {
+    if (strcmp(cat.name(), kMetadataName) == 0)
+      ASSERT_TRUE(CategoryRegistry::IsBuiltinCategory(&cat));
+
+    if (strncmp(cat.name(), "__test_basic_", 13) == 0) {
+      ASSERT_FALSE(CategoryRegistry::IsBuiltinCategory(&cat));
+      num_test_categories_seen++;
+    }
+  }
+  ASSERT_EQ(3, num_test_categories_seen);
+  ASSERT_TRUE(g_initializer_check);
+}
+
+// Tries to cover the case of multiple threads creating the same category
+// simultaneously. Should never end up with distinct entries with the same name.
+#if defined(OS_FUCHSIA)
+// TODO(crbug.com/738275): This is flaky on Fuchsia.
+#define MAYBE_ThreadRaces DISABLED_ThreadRaces
+#else
+#define MAYBE_ThreadRaces ThreadRaces
+#endif
+TEST_F(TraceCategoryTest, MAYBE_ThreadRaces) {
+  const int kNumThreads = 32;
+  std::unique_ptr<Thread> threads[kNumThreads];
+  for (int i = 0; i < kNumThreads; i++) {
+    threads[i].reset(new Thread("test thread"));
+    threads[i]->Start();
+  }
+  WaitableEvent sync_event(WaitableEvent::ResetPolicy::MANUAL,
+                           WaitableEvent::InitialState::NOT_SIGNALED);
+  for (int i = 0; i < kNumThreads; i++) {
+    threads[i]->task_runner()->PostTask(
+        FROM_HERE, BindOnce(&TestRaceThreadMain, Unretained(&sync_event)));
+  }
+  sync_event.Signal();
+  for (int i = 0; i < kNumThreads; i++)
+    threads[i]->Stop();
+
+  int num_times_seen = 0;
+  for (const TraceCategory& cat : GetAllCategories()) {
+    if (strcmp(cat.name(), "__test_race") == 0)
+      num_times_seen++;
+  }
+  ASSERT_EQ(1, num_times_seen);
+}
+
+}  // namespace trace_event
+}  // namespace base
diff --git a/base/trace_event/trace_config.cc b/base/trace_event/trace_config.cc
new file mode 100644
index 0000000..9d6a9d4
--- /dev/null
+++ b/base/trace_event/trace_config.cc
@@ -0,0 +1,618 @@
+// Copyright (c) 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/trace_event/trace_config.h"
+
+#include <stddef.h>
+
+#include <utility>
+
+#include "base/json/json_reader.h"
+#include "base/json/json_writer.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/string_split.h"
+#include "base/trace_event/memory_dump_manager.h"
+#include "base/trace_event/memory_dump_request_args.h"
+#include "base/trace_event/trace_event.h"
+
+namespace base {
+namespace trace_event {
+
+namespace {
+
+// String options that can be used to initialize TraceOptions.
+const char kRecordUntilFull[] = "record-until-full";
+const char kRecordContinuously[] = "record-continuously";
+const char kRecordAsMuchAsPossible[] = "record-as-much-as-possible";
+const char kTraceToConsole[] = "trace-to-console";
+const char kEnableSystrace[] = "enable-systrace";
+const char kEnableArgumentFilter[] = "enable-argument-filter";
+
+// String parameters that can be used to parse the trace config string.
+const char kRecordModeParam[] = "record_mode";
+const char kEnableSystraceParam[] = "enable_systrace";
+const char kEnableArgumentFilterParam[] = "enable_argument_filter";
+
+// String parameters that is used to parse memory dump config in trace config
+// string.
+const char kMemoryDumpConfigParam[] = "memory_dump_config";
+const char kAllowedDumpModesParam[] = "allowed_dump_modes";
+const char kTriggersParam[] = "triggers";
+const char kTriggerModeParam[] = "mode";
+const char kMinTimeBetweenDumps[] = "min_time_between_dumps_ms";
+const char kTriggerTypeParam[] = "type";
+const char kPeriodicIntervalLegacyParam[] = "periodic_interval_ms";
+const char kHeapProfilerOptions[] = "heap_profiler_options";
+const char kBreakdownThresholdBytes[] = "breakdown_threshold_bytes";
+
+// String parameters used to parse category event filters.
+const char kEventFiltersParam[] = "event_filters";
+const char kFilterPredicateParam[] = "filter_predicate";
+const char kFilterArgsParam[] = "filter_args";
+
+// String parameter used to parse process filter.
+const char kIncludedProcessesParam[] = "included_process_ids";
+
+class ConvertableTraceConfigToTraceFormat
+    : public base::trace_event::ConvertableToTraceFormat {
+ public:
+  explicit ConvertableTraceConfigToTraceFormat(const TraceConfig& trace_config)
+      : trace_config_(trace_config) {}
+
+  ~ConvertableTraceConfigToTraceFormat() override = default;
+
+  void AppendAsTraceFormat(std::string* out) const override {
+    out->append(trace_config_.ToString());
+  }
+
+ private:
+  const TraceConfig trace_config_;
+};
+
+std::set<MemoryDumpLevelOfDetail> GetDefaultAllowedMemoryDumpModes() {
+  std::set<MemoryDumpLevelOfDetail> all_modes;
+  for (uint32_t mode = static_cast<uint32_t>(MemoryDumpLevelOfDetail::FIRST);
+       mode <= static_cast<uint32_t>(MemoryDumpLevelOfDetail::LAST); mode++) {
+    all_modes.insert(static_cast<MemoryDumpLevelOfDetail>(mode));
+  }
+  return all_modes;
+}
+
+}  // namespace
+
+TraceConfig::MemoryDumpConfig::HeapProfiler::HeapProfiler()
+    : breakdown_threshold_bytes(kDefaultBreakdownThresholdBytes) {}
+
+void TraceConfig::MemoryDumpConfig::HeapProfiler::Clear() {
+  breakdown_threshold_bytes = kDefaultBreakdownThresholdBytes;
+}
+
+void TraceConfig::ResetMemoryDumpConfig(
+    const TraceConfig::MemoryDumpConfig& memory_dump_config) {
+  memory_dump_config_.Clear();
+  memory_dump_config_ = memory_dump_config;
+}
+
+TraceConfig::MemoryDumpConfig::MemoryDumpConfig() = default;
+
+TraceConfig::MemoryDumpConfig::MemoryDumpConfig(
+    const MemoryDumpConfig& other) = default;
+
+TraceConfig::MemoryDumpConfig::~MemoryDumpConfig() = default;
+
+void TraceConfig::MemoryDumpConfig::Clear() {
+  allowed_dump_modes.clear();
+  triggers.clear();
+  heap_profiler_options.Clear();
+}
+
+void TraceConfig::MemoryDumpConfig::Merge(
+    const TraceConfig::MemoryDumpConfig& config) {
+  triggers.insert(triggers.end(), config.triggers.begin(),
+                  config.triggers.end());
+  allowed_dump_modes.insert(config.allowed_dump_modes.begin(),
+                            config.allowed_dump_modes.end());
+  heap_profiler_options.breakdown_threshold_bytes =
+      std::min(heap_profiler_options.breakdown_threshold_bytes,
+               config.heap_profiler_options.breakdown_threshold_bytes);
+}
+
+TraceConfig::ProcessFilterConfig::ProcessFilterConfig() = default;
+
+TraceConfig::ProcessFilterConfig::ProcessFilterConfig(
+    const ProcessFilterConfig& other) = default;
+
+TraceConfig::ProcessFilterConfig::ProcessFilterConfig(
+    const std::unordered_set<base::ProcessId>& included_process_ids)
+    : included_process_ids_(included_process_ids) {}
+
+TraceConfig::ProcessFilterConfig::~ProcessFilterConfig() = default;
+
+void TraceConfig::ProcessFilterConfig::Clear() {
+  included_process_ids_.clear();
+}
+
+void TraceConfig::ProcessFilterConfig::Merge(
+    const ProcessFilterConfig& config) {
+  included_process_ids_.insert(config.included_process_ids_.begin(),
+                               config.included_process_ids_.end());
+}
+
+void TraceConfig::ProcessFilterConfig::InitializeFromConfigDict(
+    const base::DictionaryValue& dict) {
+  included_process_ids_.clear();
+  const Value* value =
+      dict.FindKeyOfType(kIncludedProcessesParam, Value::Type::LIST);
+  if (!value)
+    return;
+  for (auto& pid_value : value->GetList()) {
+    if (pid_value.is_int())
+      included_process_ids_.insert(pid_value.GetInt());
+  }
+}
+
+void TraceConfig::ProcessFilterConfig::ToDict(DictionaryValue* dict) const {
+  if (included_process_ids_.empty())
+    return;
+  Value* list = dict->SetKey(kIncludedProcessesParam, Value(Value::Type::LIST));
+  std::set<base::ProcessId> ordered_set(included_process_ids_.begin(),
+                                        included_process_ids_.end());
+  for (auto process_id : ordered_set)
+    list->GetList().emplace_back(static_cast<int>(process_id));
+}
+
+bool TraceConfig::ProcessFilterConfig::IsEnabled(
+    base::ProcessId process_id) const {
+  return included_process_ids_.empty() ||
+         included_process_ids_.count(process_id);
+}
+
+TraceConfig::EventFilterConfig::EventFilterConfig(
+    const std::string& predicate_name)
+    : predicate_name_(predicate_name) {}
+
+TraceConfig::EventFilterConfig::~EventFilterConfig() = default;
+
+TraceConfig::EventFilterConfig::EventFilterConfig(const EventFilterConfig& tc) {
+  *this = tc;
+}
+
+TraceConfig::EventFilterConfig& TraceConfig::EventFilterConfig::operator=(
+    const TraceConfig::EventFilterConfig& rhs) {
+  if (this == &rhs)
+    return *this;
+
+  predicate_name_ = rhs.predicate_name_;
+  category_filter_ = rhs.category_filter_;
+
+  if (rhs.args_)
+    args_ = rhs.args_->CreateDeepCopy();
+
+  return *this;
+}
+
+void TraceConfig::EventFilterConfig::InitializeFromConfigDict(
+    const base::DictionaryValue* event_filter) {
+  category_filter_.InitializeFromConfigDict(*event_filter);
+
+  const base::DictionaryValue* args_dict = nullptr;
+  if (event_filter->GetDictionary(kFilterArgsParam, &args_dict))
+    args_ = args_dict->CreateDeepCopy();
+}
+
+void TraceConfig::EventFilterConfig::SetCategoryFilter(
+    const TraceConfigCategoryFilter& category_filter) {
+  category_filter_ = category_filter;
+}
+
+void TraceConfig::EventFilterConfig::ToDict(
+    DictionaryValue* filter_dict) const {
+  filter_dict->SetString(kFilterPredicateParam, predicate_name());
+
+  category_filter_.ToDict(filter_dict);
+
+  if (args_)
+    filter_dict->Set(kFilterArgsParam, args_->CreateDeepCopy());
+}
+
+bool TraceConfig::EventFilterConfig::GetArgAsSet(
+    const char* key,
+    std::unordered_set<std::string>* out_set) const {
+  const ListValue* list = nullptr;
+  if (!args_->GetList(key, &list))
+    return false;
+  for (size_t i = 0; i < list->GetSize(); ++i) {
+    std::string value;
+    if (list->GetString(i, &value))
+      out_set->insert(value);
+  }
+  return true;
+}
+
+bool TraceConfig::EventFilterConfig::IsCategoryGroupEnabled(
+    const StringPiece& category_group_name) const {
+  return category_filter_.IsCategoryGroupEnabled(category_group_name);
+}
+
+// static
+std::string TraceConfig::TraceRecordModeToStr(TraceRecordMode record_mode) {
+  switch (record_mode) {
+    case RECORD_UNTIL_FULL:
+      return kRecordUntilFull;
+    case RECORD_CONTINUOUSLY:
+      return kRecordContinuously;
+    case RECORD_AS_MUCH_AS_POSSIBLE:
+      return kRecordAsMuchAsPossible;
+    case ECHO_TO_CONSOLE:
+      return kTraceToConsole;
+    default:
+      NOTREACHED();
+  }
+  return kRecordUntilFull;
+}
+
+TraceConfig::TraceConfig() {
+  InitializeDefault();
+}
+
+TraceConfig::TraceConfig(StringPiece category_filter_string,
+                         StringPiece trace_options_string) {
+  InitializeFromStrings(category_filter_string, trace_options_string);
+}
+
+TraceConfig::TraceConfig(StringPiece category_filter_string,
+                         TraceRecordMode record_mode) {
+  InitializeFromStrings(category_filter_string,
+                        TraceConfig::TraceRecordModeToStr(record_mode));
+}
+
+TraceConfig::TraceConfig(const DictionaryValue& config) {
+  InitializeFromConfigDict(config);
+}
+
+TraceConfig::TraceConfig(StringPiece config_string) {
+  if (!config_string.empty())
+    InitializeFromConfigString(config_string);
+  else
+    InitializeDefault();
+}
+
+TraceConfig::TraceConfig(const TraceConfig& tc) = default;
+
+TraceConfig::~TraceConfig() = default;
+
+TraceConfig& TraceConfig::operator=(const TraceConfig& rhs) {
+  if (this == &rhs)
+    return *this;
+
+  record_mode_ = rhs.record_mode_;
+  enable_systrace_ = rhs.enable_systrace_;
+  enable_argument_filter_ = rhs.enable_argument_filter_;
+  category_filter_ = rhs.category_filter_;
+  process_filter_config_ = rhs.process_filter_config_;
+  memory_dump_config_ = rhs.memory_dump_config_;
+  event_filters_ = rhs.event_filters_;
+  return *this;
+}
+
+std::string TraceConfig::ToString() const {
+  std::unique_ptr<DictionaryValue> dict = ToDict();
+  std::string json;
+  JSONWriter::Write(*dict, &json);
+  return json;
+}
+
+std::unique_ptr<ConvertableToTraceFormat>
+TraceConfig::AsConvertableToTraceFormat() const {
+  return std::make_unique<ConvertableTraceConfigToTraceFormat>(*this);
+}
+
+std::string TraceConfig::ToCategoryFilterString() const {
+  return category_filter_.ToFilterString();
+}
+
+bool TraceConfig::IsCategoryGroupEnabled(
+    const StringPiece& category_group_name) const {
+  // TraceLog should call this method only as part of enabling/disabling
+  // categories.
+  return category_filter_.IsCategoryGroupEnabled(category_group_name);
+}
+
+void TraceConfig::Merge(const TraceConfig& config) {
+  if (record_mode_ != config.record_mode_
+      || enable_systrace_ != config.enable_systrace_
+      || enable_argument_filter_ != config.enable_argument_filter_) {
+    DLOG(ERROR) << "Attempting to merge trace config with a different "
+                << "set of options.";
+  }
+
+  category_filter_.Merge(config.category_filter_);
+  memory_dump_config_.Merge(config.memory_dump_config_);
+  process_filter_config_.Merge(config.process_filter_config_);
+
+  event_filters_.insert(event_filters_.end(), config.event_filters().begin(),
+                        config.event_filters().end());
+}
+
+void TraceConfig::Clear() {
+  record_mode_ = RECORD_UNTIL_FULL;
+  enable_systrace_ = false;
+  enable_argument_filter_ = false;
+  category_filter_.Clear();
+  memory_dump_config_.Clear();
+  process_filter_config_.Clear();
+  event_filters_.clear();
+}
+
+void TraceConfig::InitializeDefault() {
+  record_mode_ = RECORD_UNTIL_FULL;
+  enable_systrace_ = false;
+  enable_argument_filter_ = false;
+}
+
+void TraceConfig::InitializeFromConfigDict(const DictionaryValue& dict) {
+  record_mode_ = RECORD_UNTIL_FULL;
+  std::string record_mode;
+  if (dict.GetString(kRecordModeParam, &record_mode)) {
+    if (record_mode == kRecordUntilFull) {
+      record_mode_ = RECORD_UNTIL_FULL;
+    } else if (record_mode == kRecordContinuously) {
+      record_mode_ = RECORD_CONTINUOUSLY;
+    } else if (record_mode == kTraceToConsole) {
+      record_mode_ = ECHO_TO_CONSOLE;
+    } else if (record_mode == kRecordAsMuchAsPossible) {
+      record_mode_ = RECORD_AS_MUCH_AS_POSSIBLE;
+    }
+  }
+
+  bool val;
+  enable_systrace_ = dict.GetBoolean(kEnableSystraceParam, &val) ? val : false;
+  enable_argument_filter_ =
+      dict.GetBoolean(kEnableArgumentFilterParam, &val) ? val : false;
+
+  category_filter_.InitializeFromConfigDict(dict);
+  process_filter_config_.InitializeFromConfigDict(dict);
+
+  const base::ListValue* category_event_filters = nullptr;
+  if (dict.GetList(kEventFiltersParam, &category_event_filters))
+    SetEventFiltersFromConfigList(*category_event_filters);
+
+  if (category_filter_.IsCategoryEnabled(MemoryDumpManager::kTraceCategory)) {
+    // If dump triggers not set, the client is using the legacy with just
+    // category enabled. So, use the default periodic dump config.
+    const DictionaryValue* memory_dump_config = nullptr;
+    if (dict.GetDictionary(kMemoryDumpConfigParam, &memory_dump_config))
+      SetMemoryDumpConfigFromConfigDict(*memory_dump_config);
+    else
+      SetDefaultMemoryDumpConfig();
+  }
+}
+
+void TraceConfig::InitializeFromConfigString(StringPiece config_string) {
+  auto dict = DictionaryValue::From(JSONReader::Read(config_string));
+  if (dict)
+    InitializeFromConfigDict(*dict);
+  else
+    InitializeDefault();
+}
+
+void TraceConfig::InitializeFromStrings(StringPiece category_filter_string,
+                                        StringPiece trace_options_string) {
+  if (!category_filter_string.empty())
+    category_filter_.InitializeFromString(category_filter_string);
+
+  record_mode_ = RECORD_UNTIL_FULL;
+  enable_systrace_ = false;
+  enable_argument_filter_ = false;
+  if (!trace_options_string.empty()) {
+    std::vector<std::string> split =
+        SplitString(trace_options_string, ",", TRIM_WHITESPACE, SPLIT_WANT_ALL);
+    for (const std::string& token : split) {
+      if (token == kRecordUntilFull) {
+        record_mode_ = RECORD_UNTIL_FULL;
+      } else if (token == kRecordContinuously) {
+        record_mode_ = RECORD_CONTINUOUSLY;
+      } else if (token == kTraceToConsole) {
+        record_mode_ = ECHO_TO_CONSOLE;
+      } else if (token == kRecordAsMuchAsPossible) {
+        record_mode_ = RECORD_AS_MUCH_AS_POSSIBLE;
+      } else if (token == kEnableSystrace) {
+        enable_systrace_ = true;
+      } else if (token == kEnableArgumentFilter) {
+        enable_argument_filter_ = true;
+      }
+    }
+  }
+
+  if (category_filter_.IsCategoryEnabled(MemoryDumpManager::kTraceCategory)) {
+    SetDefaultMemoryDumpConfig();
+  }
+}
+
+void TraceConfig::SetMemoryDumpConfigFromConfigDict(
+    const DictionaryValue& memory_dump_config) {
+  // Set allowed dump modes.
+  memory_dump_config_.allowed_dump_modes.clear();
+  const ListValue* allowed_modes_list;
+  if (memory_dump_config.GetList(kAllowedDumpModesParam, &allowed_modes_list)) {
+    for (size_t i = 0; i < allowed_modes_list->GetSize(); ++i) {
+      std::string level_of_detail_str;
+      allowed_modes_list->GetString(i, &level_of_detail_str);
+      memory_dump_config_.allowed_dump_modes.insert(
+          StringToMemoryDumpLevelOfDetail(level_of_detail_str));
+    }
+  } else {
+    // If allowed modes param is not given then allow all modes by default.
+    memory_dump_config_.allowed_dump_modes = GetDefaultAllowedMemoryDumpModes();
+  }
+
+  // Set triggers
+  memory_dump_config_.triggers.clear();
+  const ListValue* trigger_list = nullptr;
+  if (memory_dump_config.GetList(kTriggersParam, &trigger_list) &&
+      trigger_list->GetSize() > 0) {
+    for (size_t i = 0; i < trigger_list->GetSize(); ++i) {
+      const DictionaryValue* trigger = nullptr;
+      if (!trigger_list->GetDictionary(i, &trigger))
+        continue;
+
+      MemoryDumpConfig::Trigger dump_config;
+      int interval = 0;
+      if (!trigger->GetInteger(kMinTimeBetweenDumps, &interval)) {
+        // If "min_time_between_dumps_ms" param was not given, then the trace
+        // config uses old format where only periodic dumps are supported.
+        trigger->GetInteger(kPeriodicIntervalLegacyParam, &interval);
+        dump_config.trigger_type = MemoryDumpType::PERIODIC_INTERVAL;
+      } else {
+        std::string trigger_type_str;
+        trigger->GetString(kTriggerTypeParam, &trigger_type_str);
+        dump_config.trigger_type = StringToMemoryDumpType(trigger_type_str);
+      }
+      DCHECK_GT(interval, 0);
+      dump_config.min_time_between_dumps_ms = static_cast<uint32_t>(interval);
+
+      std::string level_of_detail_str;
+      trigger->GetString(kTriggerModeParam, &level_of_detail_str);
+      dump_config.level_of_detail =
+          StringToMemoryDumpLevelOfDetail(level_of_detail_str);
+
+      memory_dump_config_.triggers.push_back(dump_config);
+    }
+  }
+
+  // Set heap profiler options
+  const DictionaryValue* heap_profiler_options = nullptr;
+  if (memory_dump_config.GetDictionary(kHeapProfilerOptions,
+                                       &heap_profiler_options)) {
+    int min_size_bytes = 0;
+    if (heap_profiler_options->GetInteger(kBreakdownThresholdBytes,
+                                         &min_size_bytes)
+        && min_size_bytes >= 0) {
+      memory_dump_config_.heap_profiler_options.breakdown_threshold_bytes =
+          static_cast<size_t>(min_size_bytes);
+    } else {
+      memory_dump_config_.heap_profiler_options.breakdown_threshold_bytes =
+          MemoryDumpConfig::HeapProfiler::kDefaultBreakdownThresholdBytes;
+    }
+  }
+}
+
+void TraceConfig::SetDefaultMemoryDumpConfig() {
+  memory_dump_config_.Clear();
+  memory_dump_config_.allowed_dump_modes = GetDefaultAllowedMemoryDumpModes();
+}
+
+void TraceConfig::SetProcessFilterConfig(const ProcessFilterConfig& config) {
+  process_filter_config_ = config;
+}
+
+void TraceConfig::SetEventFiltersFromConfigList(
+    const base::ListValue& category_event_filters) {
+  event_filters_.clear();
+
+  for (size_t event_filter_index = 0;
+       event_filter_index < category_event_filters.GetSize();
+       ++event_filter_index) {
+    const base::DictionaryValue* event_filter = nullptr;
+    if (!category_event_filters.GetDictionary(event_filter_index,
+                                              &event_filter))
+      continue;
+
+    std::string predicate_name;
+    CHECK(event_filter->GetString(kFilterPredicateParam, &predicate_name))
+        << "Invalid predicate name in category event filter.";
+
+    EventFilterConfig new_config(predicate_name);
+    new_config.InitializeFromConfigDict(event_filter);
+    event_filters_.push_back(new_config);
+  }
+}
+
+std::unique_ptr<DictionaryValue> TraceConfig::ToDict() const {
+  auto dict = std::make_unique<DictionaryValue>();
+  dict->SetString(kRecordModeParam,
+                  TraceConfig::TraceRecordModeToStr(record_mode_));
+  dict->SetBoolean(kEnableSystraceParam, enable_systrace_);
+  dict->SetBoolean(kEnableArgumentFilterParam, enable_argument_filter_);
+
+  category_filter_.ToDict(dict.get());
+  process_filter_config_.ToDict(dict.get());
+
+  if (!event_filters_.empty()) {
+    std::unique_ptr<base::ListValue> filter_list(new base::ListValue());
+    for (const EventFilterConfig& filter : event_filters_) {
+      std::unique_ptr<base::DictionaryValue> filter_dict(
+          new base::DictionaryValue());
+      filter.ToDict(filter_dict.get());
+      filter_list->Append(std::move(filter_dict));
+    }
+    dict->Set(kEventFiltersParam, std::move(filter_list));
+  }
+
+  if (category_filter_.IsCategoryEnabled(MemoryDumpManager::kTraceCategory)) {
+    auto allowed_modes = std::make_unique<ListValue>();
+    for (auto dump_mode : memory_dump_config_.allowed_dump_modes)
+      allowed_modes->AppendString(MemoryDumpLevelOfDetailToString(dump_mode));
+
+    auto memory_dump_config = std::make_unique<DictionaryValue>();
+    memory_dump_config->Set(kAllowedDumpModesParam, std::move(allowed_modes));
+
+    auto triggers_list = std::make_unique<ListValue>();
+    for (const auto& config : memory_dump_config_.triggers) {
+      auto trigger_dict = std::make_unique<DictionaryValue>();
+      trigger_dict->SetString(kTriggerTypeParam,
+                              MemoryDumpTypeToString(config.trigger_type));
+      trigger_dict->SetInteger(
+          kMinTimeBetweenDumps,
+          static_cast<int>(config.min_time_between_dumps_ms));
+      trigger_dict->SetString(
+          kTriggerModeParam,
+          MemoryDumpLevelOfDetailToString(config.level_of_detail));
+      triggers_list->Append(std::move(trigger_dict));
+    }
+
+    // Empty triggers will still be specified explicitly since it means that
+    // the periodic dumps are not enabled.
+    memory_dump_config->Set(kTriggersParam, std::move(triggers_list));
+
+    if (memory_dump_config_.heap_profiler_options.breakdown_threshold_bytes !=
+        MemoryDumpConfig::HeapProfiler::kDefaultBreakdownThresholdBytes) {
+      auto options = std::make_unique<DictionaryValue>();
+      options->SetInteger(
+          kBreakdownThresholdBytes,
+          memory_dump_config_.heap_profiler_options.breakdown_threshold_bytes);
+      memory_dump_config->Set(kHeapProfilerOptions, std::move(options));
+    }
+    dict->Set(kMemoryDumpConfigParam, std::move(memory_dump_config));
+  }
+  return dict;
+}
+
+std::string TraceConfig::ToTraceOptionsString() const {
+  std::string ret;
+  switch (record_mode_) {
+    case RECORD_UNTIL_FULL:
+      ret = kRecordUntilFull;
+      break;
+    case RECORD_CONTINUOUSLY:
+      ret = kRecordContinuously;
+      break;
+    case RECORD_AS_MUCH_AS_POSSIBLE:
+      ret = kRecordAsMuchAsPossible;
+      break;
+    case ECHO_TO_CONSOLE:
+      ret = kTraceToConsole;
+      break;
+    default:
+      NOTREACHED();
+  }
+  if (enable_systrace_)
+    ret = ret + "," + kEnableSystrace;
+  if (enable_argument_filter_)
+    ret = ret + "," + kEnableArgumentFilter;
+  return ret;
+}
+
+}  // namespace trace_event
+}  // namespace base
diff --git a/base/trace_event/trace_config.h b/base/trace_event/trace_config.h
new file mode 100644
index 0000000..b1d809b
--- /dev/null
+++ b/base/trace_event/trace_config.h
@@ -0,0 +1,321 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TRACE_EVENT_TRACE_CONFIG_H_
+#define BASE_TRACE_EVENT_TRACE_CONFIG_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <set>
+#include <string>
+#include <unordered_set>
+#include <vector>
+
+#include "base/base_export.h"
+#include "base/gtest_prod_util.h"
+#include "base/strings/string_piece.h"
+#include "base/trace_event/memory_dump_request_args.h"
+#include "base/trace_event/trace_config_category_filter.h"
+#include "base/values.h"
+
+namespace base {
+namespace trace_event {
+
+class ConvertableToTraceFormat;
+
+// Options determines how the trace buffer stores data.
+// A Java counterpart will be generated for this enum.
+// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.base
+enum TraceRecordMode {
+  // Record until the trace buffer is full.
+  RECORD_UNTIL_FULL,
+
+  // Record until the user ends the trace. The trace buffer is a fixed size
+  // and we use it as a ring buffer during recording.
+  RECORD_CONTINUOUSLY,
+
+  // Record until the trace buffer is full, but with a huge buffer size.
+  RECORD_AS_MUCH_AS_POSSIBLE,
+
+  // Echo to console. Events are discarded.
+  ECHO_TO_CONSOLE,
+};
+
+class BASE_EXPORT TraceConfig {
+ public:
+  using StringList = std::vector<std::string>;
+
+  // Specifies the memory dump config for tracing.
+  // Used only when "memory-infra" category is enabled.
+  struct BASE_EXPORT MemoryDumpConfig {
+    MemoryDumpConfig();
+    MemoryDumpConfig(const MemoryDumpConfig& other);
+    ~MemoryDumpConfig();
+
+    // Specifies the triggers in the memory dump config.
+    struct Trigger {
+      uint32_t min_time_between_dumps_ms;
+      MemoryDumpLevelOfDetail level_of_detail;
+      MemoryDumpType trigger_type;
+    };
+
+    // Specifies the configuration options for the heap profiler.
+    struct HeapProfiler {
+      // Default value for |breakdown_threshold_bytes|.
+      enum { kDefaultBreakdownThresholdBytes = 1024 };
+
+      HeapProfiler();
+
+      // Reset the options to default.
+      void Clear();
+
+      uint32_t breakdown_threshold_bytes;
+    };
+
+    // Reset the values in the config.
+    void Clear();
+
+    void Merge(const MemoryDumpConfig& config);
+
+    // Set of memory dump modes allowed for the tracing session. The explicitly
+    // triggered dumps will be successful only if the dump mode is allowed in
+    // the config.
+    std::set<MemoryDumpLevelOfDetail> allowed_dump_modes;
+
+    std::vector<Trigger> triggers;
+    HeapProfiler heap_profiler_options;
+  };
+
+  class BASE_EXPORT ProcessFilterConfig {
+   public:
+    ProcessFilterConfig();
+    explicit ProcessFilterConfig(
+        const std::unordered_set<base::ProcessId>& included_process_ids);
+    ProcessFilterConfig(const ProcessFilterConfig&);
+    ~ProcessFilterConfig();
+
+    bool empty() const { return included_process_ids_.empty(); }
+
+    void Clear();
+    void Merge(const ProcessFilterConfig&);
+
+    void InitializeFromConfigDict(const base::DictionaryValue&);
+    void ToDict(DictionaryValue*) const;
+
+    bool IsEnabled(base::ProcessId) const;
+
+    bool operator==(const ProcessFilterConfig& other) const {
+      return included_process_ids_ == other.included_process_ids_;
+    }
+
+   private:
+    std::unordered_set<base::ProcessId> included_process_ids_;
+  };
+
+  class BASE_EXPORT EventFilterConfig {
+   public:
+    EventFilterConfig(const std::string& predicate_name);
+    EventFilterConfig(const EventFilterConfig& tc);
+
+    ~EventFilterConfig();
+
+    EventFilterConfig& operator=(const EventFilterConfig& rhs);
+
+    void InitializeFromConfigDict(const base::DictionaryValue* event_filter);
+
+    void SetCategoryFilter(const TraceConfigCategoryFilter& category_filter);
+
+    void ToDict(DictionaryValue* filter_dict) const;
+
+    bool GetArgAsSet(const char* key, std::unordered_set<std::string>*) const;
+
+    bool IsCategoryGroupEnabled(const StringPiece& category_group_name) const;
+
+    const std::string& predicate_name() const { return predicate_name_; }
+    base::DictionaryValue* filter_args() const { return args_.get(); }
+    const TraceConfigCategoryFilter& category_filter() const {
+      return category_filter_;
+    }
+
+   private:
+    std::string predicate_name_;
+    TraceConfigCategoryFilter category_filter_;
+    std::unique_ptr<base::DictionaryValue> args_;
+  };
+  typedef std::vector<EventFilterConfig> EventFilters;
+
+  static std::string TraceRecordModeToStr(TraceRecordMode record_mode);
+
+  TraceConfig();
+
+  // Create TraceConfig object from category filter and trace options strings.
+  //
+  // |category_filter_string| is a comma-delimited list of category wildcards.
+  // A category can have an optional '-' prefix to make it an excluded category.
+  // All the same rules apply above, so for example, having both included and
+  // excluded categories in the same list would not be supported.
+  //
+  // |trace_options_string| is a comma-delimited list of trace options.
+  // Possible options are: "record-until-full", "record-continuously",
+  // "record-as-much-as-possible", "trace-to-console", "enable-systrace" and
+  // "enable-argument-filter".
+  // The first 4 options are trace recoding modes and hence
+  // mutually exclusive. If more than one trace recording modes appear in the
+  // options_string, the last one takes precedence. If none of the trace
+  // recording mode is specified, recording mode is RECORD_UNTIL_FULL.
+  //
+  // The trace option will first be reset to the default option
+  // (record_mode set to RECORD_UNTIL_FULL, enable_systrace and
+  // enable_argument_filter set to false) before options parsed from
+  // |trace_options_string| are applied on it. If |trace_options_string| is
+  // invalid, the final state of trace options is undefined.
+  //
+  // Example: TraceConfig("test_MyTest*", "record-until-full");
+  // Example: TraceConfig("test_MyTest*,test_OtherStuff",
+  //                      "record-continuously");
+  // Example: TraceConfig("-excluded_category1,-excluded_category2",
+  //                      "record-until-full, trace-to-console");
+  //          would set ECHO_TO_CONSOLE as the recording mode.
+  // Example: TraceConfig("-*,webkit", "");
+  //          would disable everything but webkit; and use default options.
+  // Example: TraceConfig("-webkit", "");
+  //          would enable everything but webkit; and use default options.
+  TraceConfig(StringPiece category_filter_string,
+              StringPiece trace_options_string);
+
+  TraceConfig(StringPiece category_filter_string, TraceRecordMode record_mode);
+
+  // Create TraceConfig object from the trace config string.
+  //
+  // |config_string| is a dictionary formatted as a JSON string, containing both
+  // category filters and trace options.
+  //
+  // Example:
+  //   {
+  //     "record_mode": "record-continuously",
+  //     "enable_systrace": true,
+  //     "enable_argument_filter": true,
+  //     "included_categories": ["included",
+  //                             "inc_pattern*",
+  //                             "disabled-by-default-memory-infra"],
+  //     "excluded_categories": ["excluded", "exc_pattern*"],
+  //     "memory_dump_config": {
+  //       "triggers": [
+  //         {
+  //           "mode": "detailed",
+  //           "periodic_interval_ms": 2000
+  //         }
+  //       ]
+  //     }
+  //   }
+  //
+  // Note: memory_dump_config can be specified only if
+  // disabled-by-default-memory-infra category is enabled.
+  explicit TraceConfig(StringPiece config_string);
+
+  // Functionally identical to the above, but takes a parsed dictionary as input
+  // instead of its JSON serialization.
+  explicit TraceConfig(const DictionaryValue& config);
+
+  TraceConfig(const TraceConfig& tc);
+
+  ~TraceConfig();
+
+  TraceConfig& operator=(const TraceConfig& rhs);
+
+  TraceRecordMode GetTraceRecordMode() const { return record_mode_; }
+  bool IsSystraceEnabled() const { return enable_systrace_; }
+  bool IsArgumentFilterEnabled() const { return enable_argument_filter_; }
+
+  void SetTraceRecordMode(TraceRecordMode mode) { record_mode_ = mode; }
+  void EnableSystrace() { enable_systrace_ = true; }
+  void EnableArgumentFilter() { enable_argument_filter_ = true; }
+
+  // Writes the string representation of the TraceConfig. The string is JSON
+  // formatted.
+  std::string ToString() const;
+
+  // Returns a copy of the TraceConfig wrapped in a ConvertableToTraceFormat
+  std::unique_ptr<ConvertableToTraceFormat> AsConvertableToTraceFormat() const;
+
+  // Write the string representation of the CategoryFilter part.
+  std::string ToCategoryFilterString() const;
+
+  // Returns true if at least one category in the list is enabled by this
+  // trace config. This is used to determine if the category filters are
+  // enabled in the TRACE_* macros.
+  bool IsCategoryGroupEnabled(const StringPiece& category_group_name) const;
+
+  // Merges config with the current TraceConfig
+  void Merge(const TraceConfig& config);
+
+  void Clear();
+
+  // Clears and resets the memory dump config.
+  void ResetMemoryDumpConfig(const MemoryDumpConfig& memory_dump_config);
+
+  const TraceConfigCategoryFilter& category_filter() const {
+    return category_filter_;
+  }
+
+  const MemoryDumpConfig& memory_dump_config() const {
+    return memory_dump_config_;
+  }
+
+  const ProcessFilterConfig& process_filter_config() const {
+    return process_filter_config_;
+  }
+  void SetProcessFilterConfig(const ProcessFilterConfig&);
+
+  const EventFilters& event_filters() const { return event_filters_; }
+  void SetEventFilters(const EventFilters& filter_configs) {
+    event_filters_ = filter_configs;
+  }
+
+ private:
+  FRIEND_TEST_ALL_PREFIXES(TraceConfigTest, TraceConfigFromValidLegacyFormat);
+  FRIEND_TEST_ALL_PREFIXES(TraceConfigTest,
+                           TraceConfigFromInvalidLegacyStrings);
+
+  // The default trace config, used when none is provided.
+  // Allows all non-disabled-by-default categories through, except if they end
+  // in the suffix 'Debug' or 'Test'.
+  void InitializeDefault();
+
+  // Initialize from a config dictionary.
+  void InitializeFromConfigDict(const DictionaryValue& dict);
+
+  // Initialize from a config string.
+  void InitializeFromConfigString(StringPiece config_string);
+
+  // Initialize from category filter and trace options strings
+  void InitializeFromStrings(StringPiece category_filter_string,
+                             StringPiece trace_options_string);
+
+  void SetMemoryDumpConfigFromConfigDict(
+      const DictionaryValue& memory_dump_config);
+  void SetDefaultMemoryDumpConfig();
+
+  void SetEventFiltersFromConfigList(const base::ListValue& event_filters);
+  std::unique_ptr<DictionaryValue> ToDict() const;
+
+  std::string ToTraceOptionsString() const;
+
+  TraceRecordMode record_mode_;
+  bool enable_systrace_ : 1;
+  bool enable_argument_filter_ : 1;
+
+  TraceConfigCategoryFilter category_filter_;
+
+  MemoryDumpConfig memory_dump_config_;
+  ProcessFilterConfig process_filter_config_;
+
+  EventFilters event_filters_;
+};
+
+}  // namespace trace_event
+}  // namespace base
+
+#endif  // BASE_TRACE_EVENT_TRACE_CONFIG_H_
diff --git a/base/trace_event/trace_config_category_filter.cc b/base/trace_event/trace_config_category_filter.cc
new file mode 100644
index 0000000..d188430
--- /dev/null
+++ b/base/trace_event/trace_config_category_filter.cc
@@ -0,0 +1,235 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/trace_event/trace_config_category_filter.h"
+
+#include "base/memory/ptr_util.h"
+#include "base/strings/pattern.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_tokenizer.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/trace_event/trace_event.h"
+
+namespace base {
+namespace trace_event {
+
+namespace {
+const char kIncludedCategoriesParam[] = "included_categories";
+const char kExcludedCategoriesParam[] = "excluded_categories";
+}
+
+TraceConfigCategoryFilter::TraceConfigCategoryFilter() = default;
+
+TraceConfigCategoryFilter::TraceConfigCategoryFilter(
+    const TraceConfigCategoryFilter& other) = default;
+
+TraceConfigCategoryFilter::~TraceConfigCategoryFilter() = default;
+
+TraceConfigCategoryFilter& TraceConfigCategoryFilter::operator=(
+    const TraceConfigCategoryFilter& rhs) = default;
+
+void TraceConfigCategoryFilter::InitializeFromString(
+    const StringPiece& category_filter_string) {
+  std::vector<StringPiece> split = SplitStringPiece(
+      category_filter_string, ",", TRIM_WHITESPACE, SPLIT_WANT_ALL);
+  for (const StringPiece& category : split) {
+    // Ignore empty categories.
+    if (category.empty())
+      continue;
+    if (category.front() == '-') {
+      // Excluded categories start with '-'.
+      // Remove '-' from category string.
+      excluded_categories_.push_back(category.substr(1).as_string());
+    } else if (category.starts_with(TRACE_DISABLED_BY_DEFAULT(""))) {
+      disabled_categories_.push_back(category.as_string());
+    } else {
+      included_categories_.push_back(category.as_string());
+    }
+  }
+}
+
+void TraceConfigCategoryFilter::InitializeFromConfigDict(
+    const DictionaryValue& dict) {
+  const ListValue* category_list = nullptr;
+  if (dict.GetList(kIncludedCategoriesParam, &category_list))
+    SetCategoriesFromIncludedList(*category_list);
+  if (dict.GetList(kExcludedCategoriesParam, &category_list))
+    SetCategoriesFromExcludedList(*category_list);
+}
+
+bool TraceConfigCategoryFilter::IsCategoryGroupEnabled(
+    const StringPiece& category_group_name) const {
+  bool had_enabled_by_default = false;
+  DCHECK(!category_group_name.empty());
+  CStringTokenizer category_group_tokens(category_group_name.begin(),
+                                         category_group_name.end(), ",");
+  while (category_group_tokens.GetNext()) {
+    StringPiece category_group_token = category_group_tokens.token_piece();
+    // Don't allow empty tokens, nor tokens with leading or trailing space.
+    DCHECK(IsCategoryNameAllowed(category_group_token))
+        << "Disallowed category string";
+    if (IsCategoryEnabled(category_group_token))
+      return true;
+
+    if (!MatchPattern(category_group_token, TRACE_DISABLED_BY_DEFAULT("*")))
+      had_enabled_by_default = true;
+  }
+  // Do a second pass to check for explicitly disabled categories
+  // (those explicitly enabled have priority due to first pass).
+  category_group_tokens.Reset();
+  bool category_group_disabled = false;
+  while (category_group_tokens.GetNext()) {
+    StringPiece category_group_token = category_group_tokens.token_piece();
+    for (const std::string& category : excluded_categories_) {
+      if (MatchPattern(category_group_token, category)) {
+        // Current token of category_group_name is present in excluded_list.
+        // Flag the exclusion and proceed further to check if any of the
+        // remaining categories of category_group_name is not present in the
+        // excluded_ list.
+        category_group_disabled = true;
+        break;
+      }
+      // One of the category of category_group_name is not present in
+      // excluded_ list. So, if it's not a disabled-by-default category,
+      // it has to be included_ list. Enable the category_group_name
+      // for recording.
+      if (!MatchPattern(category_group_token, TRACE_DISABLED_BY_DEFAULT("*")))
+        category_group_disabled = false;
+    }
+    // One of the categories present in category_group_name is not present in
+    // excluded_ list. Implies this category_group_name group can be enabled
+    // for recording, since one of its groups is enabled for recording.
+    if (!category_group_disabled)
+      break;
+  }
+  // If the category group is not excluded, and there are no included patterns
+  // we consider this category group enabled, as long as it had categories
+  // other than disabled-by-default.
+  return !category_group_disabled && had_enabled_by_default &&
+         included_categories_.empty();
+}
+
+bool TraceConfigCategoryFilter::IsCategoryEnabled(
+    const StringPiece& category_name) const {
+  // Check the disabled- filters and the disabled-* wildcard first so that a
+  // "*" filter does not include the disabled.
+  for (const std::string& category : disabled_categories_) {
+    if (MatchPattern(category_name, category))
+      return true;
+  }
+
+  if (MatchPattern(category_name, TRACE_DISABLED_BY_DEFAULT("*")))
+    return false;
+
+  for (const std::string& category : included_categories_) {
+    if (MatchPattern(category_name, category))
+      return true;
+  }
+
+  return false;
+}
+
+void TraceConfigCategoryFilter::Merge(const TraceConfigCategoryFilter& config) {
+  // Keep included patterns only if both filters have an included entry.
+  // Otherwise, one of the filter was specifying "*" and we want to honor the
+  // broadest filter.
+  if (!included_categories_.empty() && !config.included_categories_.empty()) {
+    included_categories_.insert(included_categories_.end(),
+                                config.included_categories_.begin(),
+                                config.included_categories_.end());
+  } else {
+    included_categories_.clear();
+  }
+
+  disabled_categories_.insert(disabled_categories_.end(),
+                              config.disabled_categories_.begin(),
+                              config.disabled_categories_.end());
+  excluded_categories_.insert(excluded_categories_.end(),
+                              config.excluded_categories_.begin(),
+                              config.excluded_categories_.end());
+}
+
+void TraceConfigCategoryFilter::Clear() {
+  included_categories_.clear();
+  disabled_categories_.clear();
+  excluded_categories_.clear();
+}
+
+void TraceConfigCategoryFilter::ToDict(DictionaryValue* dict) const {
+  StringList categories(included_categories_);
+  categories.insert(categories.end(), disabled_categories_.begin(),
+                    disabled_categories_.end());
+  AddCategoriesToDict(categories, kIncludedCategoriesParam, dict);
+  AddCategoriesToDict(excluded_categories_, kExcludedCategoriesParam, dict);
+}
+
+std::string TraceConfigCategoryFilter::ToFilterString() const {
+  std::string filter_string;
+  WriteCategoryFilterString(included_categories_, &filter_string, true);
+  WriteCategoryFilterString(disabled_categories_, &filter_string, true);
+  WriteCategoryFilterString(excluded_categories_, &filter_string, false);
+  return filter_string;
+}
+
+void TraceConfigCategoryFilter::SetCategoriesFromIncludedList(
+    const ListValue& included_list) {
+  included_categories_.clear();
+  for (size_t i = 0; i < included_list.GetSize(); ++i) {
+    std::string category;
+    if (!included_list.GetString(i, &category))
+      continue;
+    if (category.compare(0, strlen(TRACE_DISABLED_BY_DEFAULT("")),
+                         TRACE_DISABLED_BY_DEFAULT("")) == 0) {
+      disabled_categories_.push_back(category);
+    } else {
+      included_categories_.push_back(category);
+    }
+  }
+}
+
+void TraceConfigCategoryFilter::SetCategoriesFromExcludedList(
+    const ListValue& excluded_list) {
+  excluded_categories_.clear();
+  for (size_t i = 0; i < excluded_list.GetSize(); ++i) {
+    std::string category;
+    if (excluded_list.GetString(i, &category))
+      excluded_categories_.push_back(category);
+  }
+}
+
+void TraceConfigCategoryFilter::AddCategoriesToDict(
+    const StringList& categories,
+    const char* param,
+    DictionaryValue* dict) const {
+  if (categories.empty())
+    return;
+
+  auto list = std::make_unique<ListValue>();
+  for (const std::string& category : categories)
+    list->AppendString(category);
+  dict->Set(param, std::move(list));
+}
+
+void TraceConfigCategoryFilter::WriteCategoryFilterString(
+    const StringList& values,
+    std::string* out,
+    bool included) const {
+  bool prepend_comma = !out->empty();
+  int token_cnt = 0;
+  for (const std::string& category : values) {
+    if (token_cnt > 0 || prepend_comma)
+      StringAppendF(out, ",");
+    StringAppendF(out, "%s%s", (included ? "" : "-"), category.c_str());
+    ++token_cnt;
+  }
+}
+
+// static
+bool TraceConfigCategoryFilter::IsCategoryNameAllowed(StringPiece str) {
+  return !str.empty() && str.front() != ' ' && str.back() != ' ';
+}
+
+}  // namespace trace_event
+}  // namespace base
diff --git a/base/trace_event/trace_config_category_filter.h b/base/trace_event/trace_config_category_filter.h
new file mode 100644
index 0000000..0140c1d
--- /dev/null
+++ b/base/trace_event/trace_config_category_filter.h
@@ -0,0 +1,81 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TRACE_EVENT_TRACE_CONFIG_CATEGORY_FILTER_H_
+#define BASE_TRACE_EVENT_TRACE_CONFIG_CATEGORY_FILTER_H_
+
+#include <string>
+#include <vector>
+
+#include "base/base_export.h"
+#include "base/strings/string_piece.h"
+#include "base/values.h"
+
+namespace base {
+namespace trace_event {
+
+// Configuration of categories enabled and disabled in TraceConfig.
+class BASE_EXPORT TraceConfigCategoryFilter {
+ public:
+  using StringList = std::vector<std::string>;
+
+  TraceConfigCategoryFilter();
+  TraceConfigCategoryFilter(const TraceConfigCategoryFilter& other);
+  ~TraceConfigCategoryFilter();
+
+  TraceConfigCategoryFilter& operator=(const TraceConfigCategoryFilter& rhs);
+
+  // Initializes from category filter string. See TraceConfig constructor for
+  // description of how to write category filter string.
+  void InitializeFromString(const StringPiece& category_filter_string);
+
+  // Initializes TraceConfigCategoryFilter object from the config dictionary.
+  void InitializeFromConfigDict(const DictionaryValue& dict);
+
+  // Merges this with category filter config.
+  void Merge(const TraceConfigCategoryFilter& config);
+  void Clear();
+
+  // Returns true if at least one category in the list is enabled by this
+  // trace config. This is used to determine if the category filters are
+  // enabled in the TRACE_* macros.
+  bool IsCategoryGroupEnabled(const StringPiece& category_group_name) const;
+
+  // Returns true if the category is enabled according to this trace config.
+  // This tells whether a category is enabled from the TraceConfig's
+  // perspective. Please refer to IsCategoryGroupEnabled() to determine if a
+  // category is enabled from the tracing runtime's perspective.
+  bool IsCategoryEnabled(const StringPiece& category_name) const;
+
+  void ToDict(DictionaryValue* dict) const;
+
+  std::string ToFilterString() const;
+
+  // Returns true if category name is a valid string.
+  static bool IsCategoryNameAllowed(StringPiece str);
+
+  const StringList& included_categories() const { return included_categories_; }
+  const StringList& excluded_categories() const { return excluded_categories_; }
+
+ private:
+  void SetCategoriesFromIncludedList(const ListValue& included_list);
+  void SetCategoriesFromExcludedList(const ListValue& excluded_list);
+
+  void AddCategoriesToDict(const StringList& categories,
+                           const char* param,
+                           DictionaryValue* dict) const;
+
+  void WriteCategoryFilterString(const StringList& values,
+                                 std::string* out,
+                                 bool included) const;
+
+  StringList included_categories_;
+  StringList disabled_categories_;
+  StringList excluded_categories_;
+};
+
+}  // namespace trace_event
+}  // namespace base
+
+#endif  // BASE_TRACE_EVENT_TRACE_CONFIG_CATEGORY_FILTER_H_
diff --git a/base/trace_event/trace_config_memory_test_util.h b/base/trace_event/trace_config_memory_test_util.h
new file mode 100644
index 0000000..cc49a65
--- /dev/null
+++ b/base/trace_event/trace_config_memory_test_util.h
@@ -0,0 +1,152 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TRACE_EVENT_TRACE_CONFIG_MEMORY_TEST_UTIL_H_
+#define BASE_TRACE_EVENT_TRACE_CONFIG_MEMORY_TEST_UTIL_H_
+
+#include "base/strings/stringprintf.h"
+#include "base/trace_event/memory_dump_manager.h"
+
+namespace base {
+namespace trace_event {
+
+class TraceConfigMemoryTestUtil {
+ public:
+  static std::string GetTraceConfig_LegacyPeriodicTriggers(int light_period,
+                                                           int heavy_period) {
+    return StringPrintf(
+        "{"
+        "\"enable_argument_filter\":false,"
+        "\"enable_systrace\":false,"
+        "\"excluded_categories\":["
+        "\"*\""
+        "],"
+        "\"included_categories\":["
+        "\"%s\""
+        "],"
+        "\"memory_dump_config\":{"
+        "\"allowed_dump_modes\":[\"background\",\"light\",\"detailed\"],"
+        "\"heap_profiler_options\":{"
+        "\"breakdown_threshold_bytes\":2048"
+        "},"
+        "\"triggers\":["
+        "{"
+        "\"mode\":\"light\","
+        "\"periodic_interval_ms\":%d"
+        "},"
+        "{"
+        "\"mode\":\"detailed\","
+        "\"periodic_interval_ms\":%d"
+        "}"
+        "]"
+        "},"
+        "\"record_mode\":\"record-until-full\""
+        "}",
+        MemoryDumpManager::kTraceCategory, light_period, heavy_period);
+    ;
+  }
+
+  static std::string GetTraceConfig_PeriodicTriggers(int light_period,
+                                                     int heavy_period) {
+    return StringPrintf(
+        "{"
+        "\"enable_argument_filter\":false,"
+        "\"enable_systrace\":false,"
+        "\"excluded_categories\":["
+        "\"*\""
+        "],"
+        "\"included_categories\":["
+        "\"%s\""
+        "],"
+        "\"memory_dump_config\":{"
+        "\"allowed_dump_modes\":[\"background\",\"light\",\"detailed\"],"
+        "\"heap_profiler_options\":{"
+        "\"breakdown_threshold_bytes\":2048"
+        "},"
+        "\"triggers\":["
+        "{"
+        "\"min_time_between_dumps_ms\":%d,"
+        "\"mode\":\"light\","
+        "\"type\":\"periodic_interval\""
+        "},"
+        "{"
+        "\"min_time_between_dumps_ms\":%d,"
+        "\"mode\":\"detailed\","
+        "\"type\":\"periodic_interval\""
+        "}"
+        "]"
+        "},"
+        "\"record_mode\":\"record-until-full\""
+        "}",
+        MemoryDumpManager::kTraceCategory, light_period, heavy_period);
+  }
+
+  static std::string GetTraceConfig_EmptyTriggers() {
+    return StringPrintf(
+        "{"
+        "\"enable_argument_filter\":false,"
+        "\"enable_systrace\":false,"
+        "\"excluded_categories\":["
+        "\"*\""
+        "],"
+        "\"included_categories\":["
+        "\"%s\""
+        "],"
+        "\"memory_dump_config\":{"
+        "\"allowed_dump_modes\":[\"background\",\"light\",\"detailed\"],"
+        "\"triggers\":["
+        "]"
+        "},"
+        "\"record_mode\":\"record-until-full\""
+        "}",
+        MemoryDumpManager::kTraceCategory);
+  }
+
+  static std::string GetTraceConfig_NoTriggers() {
+    return StringPrintf(
+        "{"
+        "\"enable_argument_filter\":false,"
+        "\"enable_systrace\":false,"
+        "\"excluded_categories\":["
+        "\"*\""
+        "],"
+        "\"included_categories\":["
+        "\"%s\""
+        "],"
+        "\"record_mode\":\"record-until-full\""
+        "}",
+        MemoryDumpManager::kTraceCategory);
+  }
+
+  static std::string GetTraceConfig_BackgroundTrigger(int period_ms) {
+    return StringPrintf(
+        "{"
+        "\"enable_argument_filter\":false,"
+        "\"enable_systrace\":false,"
+        "\"excluded_categories\":["
+        "\"*\""
+        "],"
+        "\"included_categories\":["
+        "\"%s\""
+        "],"
+        "\"memory_dump_config\":{"
+        "\"allowed_dump_modes\":[\"background\"],"
+        "\"triggers\":["
+        "{"
+        "\"min_time_between_dumps_ms\":%d,"
+        "\"mode\":\"background\","
+        "\"type\":\"periodic_interval\""
+        "}"
+        "]"
+        "},"
+        "\"record_mode\":\"record-until-full\""
+        "}",
+        MemoryDumpManager::kTraceCategory, period_ms);
+  }
+};
+
+}  // namespace trace_event
+}  // namespace base
+
+#endif  // BASE_TRACE_EVENT_TRACE_CONFIG_MEMORY_TEST_UTIL_H_
diff --git a/base/trace_event/trace_config_unittest.cc b/base/trace_event/trace_config_unittest.cc
new file mode 100644
index 0000000..efdbffb
--- /dev/null
+++ b/base/trace_event/trace_config_unittest.cc
@@ -0,0 +1,663 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+
+#include "base/json/json_reader.h"
+#include "base/json/json_writer.h"
+#include "base/macros.h"
+#include "base/trace_event/memory_dump_manager.h"
+#include "base/trace_event/trace_config.h"
+#include "base/trace_event/trace_config_memory_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace trace_event {
+
+namespace {
+
+const char kDefaultTraceConfigString[] =
+  "{"
+    "\"enable_argument_filter\":false,"
+    "\"enable_systrace\":false,"
+    "\"record_mode\":\"record-until-full\""
+  "}";
+
+const char kCustomTraceConfigString[] =
+    "{"
+    "\"enable_argument_filter\":true,"
+    "\"enable_systrace\":true,"
+    "\"event_filters\":["
+    "{"
+    "\"excluded_categories\":[\"unfiltered_cat\"],"
+    "\"filter_args\":{\"event_name_whitelist\":[\"a snake\",\"a dog\"]},"
+    "\"filter_predicate\":\"event_whitelist_predicate\","
+    "\"included_categories\":[\"*\"]"
+    "}"
+    "],"
+    "\"excluded_categories\":[\"excluded\",\"exc_pattern*\"],"
+    "\"included_categories\":["
+    "\"included\","
+    "\"inc_pattern*\","
+    "\"disabled-by-default-cc\","
+    "\"disabled-by-default-memory-infra\"],"
+    "\"memory_dump_config\":{"
+    "\"allowed_dump_modes\":[\"background\",\"light\",\"detailed\"],"
+    "\"heap_profiler_options\":{"
+    "\"breakdown_threshold_bytes\":10240"
+    "},"
+    "\"triggers\":["
+    "{"
+    "\"min_time_between_dumps_ms\":50,"
+    "\"mode\":\"light\","
+    "\"type\":\"periodic_interval\""
+    "},"
+    "{"
+    "\"min_time_between_dumps_ms\":1000,"
+    "\"mode\":\"detailed\","
+    "\"type\":\"periodic_interval\""
+    "}"
+    "]"
+    "},"
+    "\"record_mode\":\"record-continuously\""
+    "}";
+
+void CheckDefaultTraceConfigBehavior(const TraceConfig& tc) {
+  EXPECT_EQ(RECORD_UNTIL_FULL, tc.GetTraceRecordMode());
+  EXPECT_FALSE(tc.IsSystraceEnabled());
+  EXPECT_FALSE(tc.IsArgumentFilterEnabled());
+
+  // Default trace config enables every category filter except the
+  // disabled-by-default-* ones.
+  EXPECT_TRUE(tc.IsCategoryGroupEnabled("Category1"));
+  EXPECT_TRUE(tc.IsCategoryGroupEnabled("not-excluded-category"));
+  EXPECT_FALSE(tc.IsCategoryGroupEnabled("disabled-by-default-cc"));
+
+  EXPECT_TRUE(tc.IsCategoryGroupEnabled("Category1,not-excluded-category"));
+  EXPECT_TRUE(tc.IsCategoryGroupEnabled("Category1,disabled-by-default-cc"));
+  EXPECT_FALSE(tc.IsCategoryGroupEnabled(
+      "disabled-by-default-cc,disabled-by-default-cc2"));
+}
+
+}  // namespace
+
+TEST(TraceConfigTest, TraceConfigFromValidLegacyFormat) {
+  // From trace options strings
+  TraceConfig config("", "record-until-full");
+  EXPECT_EQ(RECORD_UNTIL_FULL, config.GetTraceRecordMode());
+  EXPECT_FALSE(config.IsSystraceEnabled());
+  EXPECT_FALSE(config.IsArgumentFilterEnabled());
+  EXPECT_STREQ("record-until-full", config.ToTraceOptionsString().c_str());
+
+  config = TraceConfig("", "record-continuously");
+  EXPECT_EQ(RECORD_CONTINUOUSLY, config.GetTraceRecordMode());
+  EXPECT_FALSE(config.IsSystraceEnabled());
+  EXPECT_FALSE(config.IsArgumentFilterEnabled());
+  EXPECT_STREQ("record-continuously", config.ToTraceOptionsString().c_str());
+
+  config = TraceConfig("", "trace-to-console");
+  EXPECT_EQ(ECHO_TO_CONSOLE, config.GetTraceRecordMode());
+  EXPECT_FALSE(config.IsSystraceEnabled());
+  EXPECT_FALSE(config.IsArgumentFilterEnabled());
+  EXPECT_STREQ("trace-to-console", config.ToTraceOptionsString().c_str());
+
+  config = TraceConfig("", "record-as-much-as-possible");
+  EXPECT_EQ(RECORD_AS_MUCH_AS_POSSIBLE, config.GetTraceRecordMode());
+  EXPECT_FALSE(config.IsSystraceEnabled());
+  EXPECT_FALSE(config.IsArgumentFilterEnabled());
+  EXPECT_STREQ("record-as-much-as-possible",
+               config.ToTraceOptionsString().c_str());
+
+  config = TraceConfig("", "enable-systrace, record-continuously");
+  EXPECT_EQ(RECORD_CONTINUOUSLY, config.GetTraceRecordMode());
+  EXPECT_TRUE(config.IsSystraceEnabled());
+  EXPECT_FALSE(config.IsArgumentFilterEnabled());
+  EXPECT_STREQ("record-continuously,enable-systrace",
+               config.ToTraceOptionsString().c_str());
+
+  config = TraceConfig("", "enable-argument-filter,record-as-much-as-possible");
+  EXPECT_EQ(RECORD_AS_MUCH_AS_POSSIBLE, config.GetTraceRecordMode());
+  EXPECT_FALSE(config.IsSystraceEnabled());
+  EXPECT_TRUE(config.IsArgumentFilterEnabled());
+  EXPECT_STREQ("record-as-much-as-possible,enable-argument-filter",
+               config.ToTraceOptionsString().c_str());
+
+  config = TraceConfig(
+    "",
+    "enable-systrace,trace-to-console,enable-argument-filter");
+  EXPECT_EQ(ECHO_TO_CONSOLE, config.GetTraceRecordMode());
+  EXPECT_TRUE(config.IsSystraceEnabled());
+  EXPECT_TRUE(config.IsArgumentFilterEnabled());
+  EXPECT_STREQ(
+    "trace-to-console,enable-systrace,enable-argument-filter",
+    config.ToTraceOptionsString().c_str());
+
+  config = TraceConfig(
+    "", "record-continuously, record-until-full, trace-to-console");
+  EXPECT_EQ(ECHO_TO_CONSOLE, config.GetTraceRecordMode());
+  EXPECT_FALSE(config.IsSystraceEnabled());
+  EXPECT_FALSE(config.IsArgumentFilterEnabled());
+  EXPECT_STREQ("trace-to-console", config.ToTraceOptionsString().c_str());
+
+  // From TraceRecordMode
+  config = TraceConfig("", RECORD_UNTIL_FULL);
+  EXPECT_EQ(RECORD_UNTIL_FULL, config.GetTraceRecordMode());
+  EXPECT_FALSE(config.IsSystraceEnabled());
+  EXPECT_FALSE(config.IsArgumentFilterEnabled());
+  EXPECT_STREQ("record-until-full", config.ToTraceOptionsString().c_str());
+
+  config = TraceConfig("", RECORD_CONTINUOUSLY);
+  EXPECT_EQ(RECORD_CONTINUOUSLY, config.GetTraceRecordMode());
+  EXPECT_FALSE(config.IsSystraceEnabled());
+  EXPECT_FALSE(config.IsArgumentFilterEnabled());
+  EXPECT_STREQ("record-continuously", config.ToTraceOptionsString().c_str());
+
+  config = TraceConfig("", ECHO_TO_CONSOLE);
+  EXPECT_EQ(ECHO_TO_CONSOLE, config.GetTraceRecordMode());
+  EXPECT_FALSE(config.IsSystraceEnabled());
+  EXPECT_FALSE(config.IsArgumentFilterEnabled());
+  EXPECT_STREQ("trace-to-console", config.ToTraceOptionsString().c_str());
+
+  config = TraceConfig("", RECORD_AS_MUCH_AS_POSSIBLE);
+  EXPECT_EQ(RECORD_AS_MUCH_AS_POSSIBLE, config.GetTraceRecordMode());
+  EXPECT_FALSE(config.IsSystraceEnabled());
+  EXPECT_FALSE(config.IsArgumentFilterEnabled());
+  EXPECT_STREQ("record-as-much-as-possible",
+               config.ToTraceOptionsString().c_str());
+
+  // From category filter strings
+  config = TraceConfig("included,-excluded,inc_pattern*,-exc_pattern*", "");
+  EXPECT_STREQ("included,inc_pattern*,-excluded,-exc_pattern*",
+               config.ToCategoryFilterString().c_str());
+
+  config = TraceConfig("only_inc_cat", "");
+  EXPECT_STREQ("only_inc_cat", config.ToCategoryFilterString().c_str());
+
+  config = TraceConfig("-only_exc_cat", "");
+  EXPECT_STREQ("-only_exc_cat", config.ToCategoryFilterString().c_str());
+
+  config = TraceConfig("disabled-by-default-cc,-excluded", "");
+  EXPECT_STREQ("disabled-by-default-cc,-excluded",
+               config.ToCategoryFilterString().c_str());
+
+  config = TraceConfig("disabled-by-default-cc,included", "");
+  EXPECT_STREQ("included,disabled-by-default-cc",
+               config.ToCategoryFilterString().c_str());
+
+  // From both trace options and category filter strings
+  config = TraceConfig("", "");
+  EXPECT_EQ(RECORD_UNTIL_FULL, config.GetTraceRecordMode());
+  EXPECT_FALSE(config.IsSystraceEnabled());
+  EXPECT_FALSE(config.IsArgumentFilterEnabled());
+  EXPECT_STREQ("", config.ToCategoryFilterString().c_str());
+  EXPECT_STREQ("record-until-full", config.ToTraceOptionsString().c_str());
+
+  config = TraceConfig("included,-excluded,inc_pattern*,-exc_pattern*",
+                       "enable-systrace, trace-to-console");
+  EXPECT_EQ(ECHO_TO_CONSOLE, config.GetTraceRecordMode());
+  EXPECT_TRUE(config.IsSystraceEnabled());
+  EXPECT_FALSE(config.IsArgumentFilterEnabled());
+  EXPECT_STREQ("included,inc_pattern*,-excluded,-exc_pattern*",
+               config.ToCategoryFilterString().c_str());
+  EXPECT_STREQ("trace-to-console,enable-systrace",
+               config.ToTraceOptionsString().c_str());
+
+  // From both trace options and category filter strings with spaces.
+  config = TraceConfig(" included , -excluded, inc_pattern*, ,-exc_pattern*   ",
+                       "enable-systrace, ,trace-to-console  ");
+  EXPECT_EQ(ECHO_TO_CONSOLE, config.GetTraceRecordMode());
+  EXPECT_TRUE(config.IsSystraceEnabled());
+  EXPECT_FALSE(config.IsArgumentFilterEnabled());
+  EXPECT_STREQ("included,inc_pattern*,-excluded,-exc_pattern*",
+               config.ToCategoryFilterString().c_str());
+  EXPECT_STREQ("trace-to-console,enable-systrace",
+               config.ToTraceOptionsString().c_str());
+
+  // From category filter string and TraceRecordMode
+  config = TraceConfig("included,-excluded,inc_pattern*,-exc_pattern*",
+                       RECORD_CONTINUOUSLY);
+  EXPECT_EQ(RECORD_CONTINUOUSLY, config.GetTraceRecordMode());
+  EXPECT_FALSE(config.IsSystraceEnabled());
+  EXPECT_FALSE(config.IsArgumentFilterEnabled());
+  EXPECT_STREQ("included,inc_pattern*,-excluded,-exc_pattern*",
+               config.ToCategoryFilterString().c_str());
+  EXPECT_STREQ("record-continuously", config.ToTraceOptionsString().c_str());
+}
+
+TEST(TraceConfigTest, TraceConfigFromInvalidLegacyStrings) {
+  TraceConfig config("", "foo-bar-baz");
+  EXPECT_EQ(RECORD_UNTIL_FULL, config.GetTraceRecordMode());
+  EXPECT_FALSE(config.IsSystraceEnabled());
+  EXPECT_FALSE(config.IsArgumentFilterEnabled());
+  EXPECT_STREQ("", config.ToCategoryFilterString().c_str());
+  EXPECT_STREQ("record-until-full", config.ToTraceOptionsString().c_str());
+
+  config = TraceConfig("arbitrary-category", "foo-bar-baz, enable-systrace");
+  EXPECT_EQ(RECORD_UNTIL_FULL, config.GetTraceRecordMode());
+  EXPECT_TRUE(config.IsSystraceEnabled());
+  EXPECT_FALSE(config.IsArgumentFilterEnabled());
+  EXPECT_STREQ("arbitrary-category", config.ToCategoryFilterString().c_str());
+  EXPECT_STREQ("record-until-full,enable-systrace",
+               config.ToTraceOptionsString().c_str());
+}
+
+TEST(TraceConfigTest, ConstructDefaultTraceConfig) {
+  TraceConfig tc;
+  EXPECT_STREQ("", tc.ToCategoryFilterString().c_str());
+  EXPECT_STREQ(kDefaultTraceConfigString, tc.ToString().c_str());
+  CheckDefaultTraceConfigBehavior(tc);
+
+  // Constructors from category filter string and trace option string.
+  TraceConfig tc_asterisk("*", "");
+  EXPECT_STREQ("*", tc_asterisk.ToCategoryFilterString().c_str());
+  CheckDefaultTraceConfigBehavior(tc_asterisk);
+
+  TraceConfig tc_empty_category_filter("", "");
+  EXPECT_STREQ("", tc_empty_category_filter.ToCategoryFilterString().c_str());
+  EXPECT_STREQ(kDefaultTraceConfigString,
+               tc_empty_category_filter.ToString().c_str());
+  CheckDefaultTraceConfigBehavior(tc_empty_category_filter);
+
+  // Constructor from JSON formated config string.
+  TraceConfig tc_empty_json_string("");
+  EXPECT_STREQ("", tc_empty_json_string.ToCategoryFilterString().c_str());
+  EXPECT_STREQ(kDefaultTraceConfigString,
+               tc_empty_json_string.ToString().c_str());
+  CheckDefaultTraceConfigBehavior(tc_empty_json_string);
+
+  // Constructor from dictionary value.
+  DictionaryValue dict;
+  TraceConfig tc_dict(dict);
+  EXPECT_STREQ("", tc_dict.ToCategoryFilterString().c_str());
+  EXPECT_STREQ(kDefaultTraceConfigString, tc_dict.ToString().c_str());
+  CheckDefaultTraceConfigBehavior(tc_dict);
+}
+
+TEST(TraceConfigTest, EmptyAndAsteriskCategoryFilterString) {
+  TraceConfig tc_empty("", "");
+  TraceConfig tc_asterisk("*", "");
+
+  EXPECT_STREQ("", tc_empty.ToCategoryFilterString().c_str());
+  EXPECT_STREQ("*", tc_asterisk.ToCategoryFilterString().c_str());
+
+  // Both fall back to default config.
+  CheckDefaultTraceConfigBehavior(tc_empty);
+  CheckDefaultTraceConfigBehavior(tc_asterisk);
+
+  // They differ only for internal checking.
+  EXPECT_FALSE(tc_empty.category_filter().IsCategoryEnabled("Category1"));
+  EXPECT_FALSE(
+      tc_empty.category_filter().IsCategoryEnabled("not-excluded-category"));
+  EXPECT_TRUE(tc_asterisk.category_filter().IsCategoryEnabled("Category1"));
+  EXPECT_TRUE(
+      tc_asterisk.category_filter().IsCategoryEnabled("not-excluded-category"));
+}
+
+TEST(TraceConfigTest, DisabledByDefaultCategoryFilterString) {
+  TraceConfig tc("foo,disabled-by-default-foo", "");
+  EXPECT_STREQ("foo,disabled-by-default-foo",
+               tc.ToCategoryFilterString().c_str());
+  EXPECT_TRUE(tc.IsCategoryGroupEnabled("foo"));
+  EXPECT_TRUE(tc.IsCategoryGroupEnabled("disabled-by-default-foo"));
+  EXPECT_FALSE(tc.IsCategoryGroupEnabled("bar"));
+  EXPECT_FALSE(tc.IsCategoryGroupEnabled("disabled-by-default-bar"));
+
+  EXPECT_TRUE(tc.event_filters().empty());
+  // Enabling only the disabled-by-default-* category means the default ones
+  // are also enabled.
+  tc = TraceConfig("disabled-by-default-foo", "");
+  EXPECT_STREQ("disabled-by-default-foo", tc.ToCategoryFilterString().c_str());
+  EXPECT_TRUE(tc.IsCategoryGroupEnabled("disabled-by-default-foo"));
+  EXPECT_TRUE(tc.IsCategoryGroupEnabled("foo"));
+  EXPECT_TRUE(tc.IsCategoryGroupEnabled("bar"));
+  EXPECT_FALSE(tc.IsCategoryGroupEnabled("disabled-by-default-bar"));
+}
+
+TEST(TraceConfigTest, TraceConfigFromDict) {
+  // Passing in empty dictionary will result in default trace config.
+  DictionaryValue dict;
+  TraceConfig tc(dict);
+  EXPECT_STREQ(kDefaultTraceConfigString, tc.ToString().c_str());
+  EXPECT_EQ(RECORD_UNTIL_FULL, tc.GetTraceRecordMode());
+  EXPECT_FALSE(tc.IsSystraceEnabled());
+  EXPECT_FALSE(tc.IsArgumentFilterEnabled());
+  EXPECT_STREQ("", tc.ToCategoryFilterString().c_str());
+
+  std::unique_ptr<Value> default_value(
+      JSONReader::Read(kDefaultTraceConfigString));
+  DCHECK(default_value);
+  const DictionaryValue* default_dict = nullptr;
+  bool is_dict = default_value->GetAsDictionary(&default_dict);
+  DCHECK(is_dict);
+  TraceConfig default_tc(*default_dict);
+  EXPECT_STREQ(kDefaultTraceConfigString, default_tc.ToString().c_str());
+  EXPECT_EQ(RECORD_UNTIL_FULL, default_tc.GetTraceRecordMode());
+  EXPECT_FALSE(default_tc.IsSystraceEnabled());
+  EXPECT_FALSE(default_tc.IsArgumentFilterEnabled());
+  EXPECT_STREQ("", default_tc.ToCategoryFilterString().c_str());
+
+  std::unique_ptr<Value> custom_value(
+      JSONReader::Read(kCustomTraceConfigString));
+  DCHECK(custom_value);
+  const DictionaryValue* custom_dict = nullptr;
+  is_dict = custom_value->GetAsDictionary(&custom_dict);
+  DCHECK(is_dict);
+  TraceConfig custom_tc(*custom_dict);
+  EXPECT_STREQ(kCustomTraceConfigString, custom_tc.ToString().c_str());
+  EXPECT_EQ(RECORD_CONTINUOUSLY, custom_tc.GetTraceRecordMode());
+  EXPECT_TRUE(custom_tc.IsSystraceEnabled());
+  EXPECT_TRUE(custom_tc.IsArgumentFilterEnabled());
+  EXPECT_STREQ(
+      "included,inc_pattern*,"
+      "disabled-by-default-cc,disabled-by-default-memory-infra,"
+      "-excluded,-exc_pattern*",
+      custom_tc.ToCategoryFilterString().c_str());
+}
+
+TEST(TraceConfigTest, TraceConfigFromValidString) {
+  // Using some non-empty config string.
+  const char config_string[] =
+      "{"
+      "\"enable_argument_filter\":true,"
+      "\"enable_systrace\":true,"
+      "\"event_filters\":["
+      "{"
+      "\"excluded_categories\":[\"unfiltered_cat\"],"
+      "\"filter_args\":{\"event_name_whitelist\":[\"a snake\",\"a dog\"]},"
+      "\"filter_predicate\":\"event_whitelist_predicate\","
+      "\"included_categories\":[\"*\"]"
+      "}"
+      "],"
+      "\"excluded_categories\":[\"excluded\",\"exc_pattern*\"],"
+      "\"included_categories\":[\"included\","
+      "\"inc_pattern*\","
+      "\"disabled-by-default-cc\"],"
+      "\"record_mode\":\"record-continuously\""
+      "}";
+  TraceConfig tc(config_string);
+
+  EXPECT_STREQ(config_string, tc.ToString().c_str());
+  EXPECT_EQ(RECORD_CONTINUOUSLY, tc.GetTraceRecordMode());
+  EXPECT_TRUE(tc.IsSystraceEnabled());
+  EXPECT_TRUE(tc.IsArgumentFilterEnabled());
+  EXPECT_STREQ(
+      "included,inc_pattern*,disabled-by-default-cc,-excluded,"
+      "-exc_pattern*",
+      tc.ToCategoryFilterString().c_str());
+
+  EXPECT_TRUE(tc.category_filter().IsCategoryEnabled("included"));
+  EXPECT_TRUE(tc.category_filter().IsCategoryEnabled("inc_pattern_category"));
+  EXPECT_TRUE(tc.category_filter().IsCategoryEnabled("disabled-by-default-cc"));
+  EXPECT_FALSE(tc.category_filter().IsCategoryEnabled("excluded"));
+  EXPECT_FALSE(tc.category_filter().IsCategoryEnabled("exc_pattern_category"));
+  EXPECT_FALSE(
+      tc.category_filter().IsCategoryEnabled("disabled-by-default-others"));
+  EXPECT_FALSE(
+      tc.category_filter().IsCategoryEnabled("not-excluded-nor-included"));
+
+  EXPECT_TRUE(tc.IsCategoryGroupEnabled("included"));
+  EXPECT_TRUE(tc.IsCategoryGroupEnabled("inc_pattern_category"));
+  EXPECT_TRUE(tc.IsCategoryGroupEnabled("disabled-by-default-cc"));
+  EXPECT_FALSE(tc.IsCategoryGroupEnabled("excluded"));
+  EXPECT_FALSE(tc.IsCategoryGroupEnabled("exc_pattern_category"));
+  EXPECT_FALSE(tc.IsCategoryGroupEnabled("disabled-by-default-others"));
+  EXPECT_FALSE(tc.IsCategoryGroupEnabled("not-excluded-nor-included"));
+
+  EXPECT_TRUE(tc.IsCategoryGroupEnabled("included,excluded"));
+  EXPECT_FALSE(tc.IsCategoryGroupEnabled("excluded,exc_pattern_category"));
+  EXPECT_TRUE(tc.IsCategoryGroupEnabled("included"));
+
+  EXPECT_EQ(tc.event_filters().size(), 1u);
+  const TraceConfig::EventFilterConfig& event_filter = tc.event_filters()[0];
+  EXPECT_STREQ("event_whitelist_predicate",
+               event_filter.predicate_name().c_str());
+  EXPECT_EQ(1u, event_filter.category_filter().included_categories().size());
+  EXPECT_STREQ("*",
+               event_filter.category_filter().included_categories()[0].c_str());
+  EXPECT_EQ(1u, event_filter.category_filter().excluded_categories().size());
+  EXPECT_STREQ("unfiltered_cat",
+               event_filter.category_filter().excluded_categories()[0].c_str());
+  EXPECT_TRUE(event_filter.filter_args());
+
+  std::string json_out;
+  base::JSONWriter::Write(*event_filter.filter_args(), &json_out);
+  EXPECT_STREQ(json_out.c_str(),
+               "{\"event_name_whitelist\":[\"a snake\",\"a dog\"]}");
+  std::unordered_set<std::string> filter_values;
+  EXPECT_TRUE(event_filter.GetArgAsSet("event_name_whitelist", &filter_values));
+  EXPECT_EQ(2u, filter_values.size());
+  EXPECT_EQ(1u, filter_values.count("a snake"));
+  EXPECT_EQ(1u, filter_values.count("a dog"));
+
+  const char config_string_2[] = "{\"included_categories\":[\"*\"]}";
+  TraceConfig tc2(config_string_2);
+  EXPECT_TRUE(tc2.category_filter().IsCategoryEnabled(
+      "non-disabled-by-default-pattern"));
+  EXPECT_FALSE(
+      tc2.category_filter().IsCategoryEnabled("disabled-by-default-pattern"));
+  EXPECT_TRUE(tc2.IsCategoryGroupEnabled("non-disabled-by-default-pattern"));
+  EXPECT_FALSE(tc2.IsCategoryGroupEnabled("disabled-by-default-pattern"));
+
+  // Clear
+  tc.Clear();
+  EXPECT_STREQ(tc.ToString().c_str(),
+               "{"
+                 "\"enable_argument_filter\":false,"
+                 "\"enable_systrace\":false,"
+                 "\"record_mode\":\"record-until-full\""
+               "}");
+}
+
+TEST(TraceConfigTest, TraceConfigFromInvalidString) {
+  // The config string needs to be a dictionary correctly formatted as a JSON
+  // string. Otherwise, it will fall back to the default initialization.
+  TraceConfig tc("");
+  EXPECT_STREQ(kDefaultTraceConfigString, tc.ToString().c_str());
+  EXPECT_EQ(RECORD_UNTIL_FULL, tc.GetTraceRecordMode());
+  EXPECT_FALSE(tc.IsSystraceEnabled());
+  EXPECT_FALSE(tc.IsArgumentFilterEnabled());
+  EXPECT_STREQ("", tc.ToCategoryFilterString().c_str());
+  CheckDefaultTraceConfigBehavior(tc);
+
+  tc = TraceConfig("This is an invalid config string.");
+  EXPECT_STREQ(kDefaultTraceConfigString, tc.ToString().c_str());
+  EXPECT_EQ(RECORD_UNTIL_FULL, tc.GetTraceRecordMode());
+  EXPECT_FALSE(tc.IsSystraceEnabled());
+  EXPECT_FALSE(tc.IsArgumentFilterEnabled());
+  EXPECT_STREQ("", tc.ToCategoryFilterString().c_str());
+  CheckDefaultTraceConfigBehavior(tc);
+
+  tc = TraceConfig("[\"This\", \"is\", \"not\", \"a\", \"dictionary\"]");
+  EXPECT_STREQ(kDefaultTraceConfigString, tc.ToString().c_str());
+  EXPECT_EQ(RECORD_UNTIL_FULL, tc.GetTraceRecordMode());
+  EXPECT_FALSE(tc.IsSystraceEnabled());
+  EXPECT_FALSE(tc.IsArgumentFilterEnabled());
+  EXPECT_STREQ("", tc.ToCategoryFilterString().c_str());
+  CheckDefaultTraceConfigBehavior(tc);
+
+  tc = TraceConfig("{\"record_mode\": invalid-value-needs-double-quote}");
+  EXPECT_STREQ(kDefaultTraceConfigString, tc.ToString().c_str());
+  EXPECT_EQ(RECORD_UNTIL_FULL, tc.GetTraceRecordMode());
+  EXPECT_FALSE(tc.IsSystraceEnabled());
+  EXPECT_FALSE(tc.IsArgumentFilterEnabled());
+  EXPECT_STREQ("", tc.ToCategoryFilterString().c_str());
+  CheckDefaultTraceConfigBehavior(tc);
+
+  // If the config string a dictionary formatted as a JSON string, it will
+  // initialize TraceConfig with best effort.
+  tc = TraceConfig("{}");
+  EXPECT_EQ(RECORD_UNTIL_FULL, tc.GetTraceRecordMode());
+  EXPECT_FALSE(tc.IsSystraceEnabled());
+  EXPECT_FALSE(tc.IsArgumentFilterEnabled());
+  EXPECT_STREQ("", tc.ToCategoryFilterString().c_str());
+  CheckDefaultTraceConfigBehavior(tc);
+
+  tc = TraceConfig("{\"arbitrary-key\":\"arbitrary-value\"}");
+  EXPECT_EQ(RECORD_UNTIL_FULL, tc.GetTraceRecordMode());
+  EXPECT_FALSE(tc.IsSystraceEnabled());
+  EXPECT_FALSE(tc.IsArgumentFilterEnabled());
+  EXPECT_STREQ("", tc.ToCategoryFilterString().c_str());
+  CheckDefaultTraceConfigBehavior(tc);
+
+  const char invalid_config_string[] =
+      "{"
+      "\"enable_systrace\":1,"
+      "\"excluded_categories\":[\"excluded\"],"
+      "\"included_categories\":\"not a list\","
+      "\"record_mode\":\"arbitrary-mode\""
+      "}";
+  tc = TraceConfig(invalid_config_string);
+  EXPECT_EQ(RECORD_UNTIL_FULL, tc.GetTraceRecordMode());
+  EXPECT_FALSE(tc.IsSystraceEnabled());
+  EXPECT_FALSE(tc.IsArgumentFilterEnabled());
+
+  const char invalid_config_string_2[] =
+    "{"
+      "\"included_categories\":[\"category\",\"disabled-by-default-pattern\"],"
+      "\"excluded_categories\":[\"category\",\"disabled-by-default-pattern\"]"
+    "}";
+  tc = TraceConfig(invalid_config_string_2);
+  EXPECT_TRUE(tc.category_filter().IsCategoryEnabled("category"));
+  EXPECT_TRUE(
+      tc.category_filter().IsCategoryEnabled("disabled-by-default-pattern"));
+  EXPECT_TRUE(tc.IsCategoryGroupEnabled("category"));
+  EXPECT_TRUE(tc.IsCategoryGroupEnabled("disabled-by-default-pattern"));
+}
+
+TEST(TraceConfigTest, MergingTraceConfigs) {
+  // Merge
+  TraceConfig tc;
+  TraceConfig tc2("included,-excluded,inc_pattern*,-exc_pattern*", "");
+  tc.Merge(tc2);
+  EXPECT_STREQ("{"
+                 "\"enable_argument_filter\":false,"
+                 "\"enable_systrace\":false,"
+                 "\"excluded_categories\":[\"excluded\",\"exc_pattern*\"],"
+                 "\"record_mode\":\"record-until-full\""
+               "}",
+               tc.ToString().c_str());
+}
+
+TEST(TraceConfigTest, IsCategoryGroupEnabled) {
+  // Enabling a disabled- category does not require all categories to be traced
+  // to be included.
+  TraceConfig tc("disabled-by-default-cc,-excluded", "");
+  EXPECT_STREQ("disabled-by-default-cc,-excluded",
+               tc.ToCategoryFilterString().c_str());
+  EXPECT_TRUE(tc.IsCategoryGroupEnabled("disabled-by-default-cc"));
+  EXPECT_TRUE(tc.IsCategoryGroupEnabled("some_other_group"));
+  EXPECT_FALSE(tc.IsCategoryGroupEnabled("excluded"));
+
+  // Enabled a disabled- category and also including makes all categories to
+  // be traced require including.
+  tc = TraceConfig("disabled-by-default-cc,included", "");
+  EXPECT_STREQ("included,disabled-by-default-cc",
+               tc.ToCategoryFilterString().c_str());
+  EXPECT_TRUE(tc.IsCategoryGroupEnabled("disabled-by-default-cc"));
+  EXPECT_TRUE(tc.IsCategoryGroupEnabled("included"));
+  EXPECT_FALSE(tc.IsCategoryGroupEnabled("other_included"));
+
+  // Excluding categories won't enable disabled-by-default ones with the
+  // excluded category is also present in the group.
+  tc = TraceConfig("-excluded", "");
+  EXPECT_STREQ("-excluded", tc.ToCategoryFilterString().c_str());
+  EXPECT_FALSE(tc.IsCategoryGroupEnabled("excluded,disabled-by-default-cc"));
+}
+
+TEST(TraceConfigTest, IsCategoryNameAllowed) {
+  // Test that IsCategoryNameAllowed actually catches categories that are
+  // explicitly forbidden. This method is called in a DCHECK to assert that we
+  // don't have these types of strings as categories.
+  EXPECT_FALSE(
+      TraceConfigCategoryFilter::IsCategoryNameAllowed(" bad_category "));
+  EXPECT_FALSE(
+      TraceConfigCategoryFilter::IsCategoryNameAllowed(" bad_category"));
+  EXPECT_FALSE(
+      TraceConfigCategoryFilter::IsCategoryNameAllowed("bad_category "));
+  EXPECT_FALSE(
+      TraceConfigCategoryFilter::IsCategoryNameAllowed("   bad_category"));
+  EXPECT_FALSE(
+      TraceConfigCategoryFilter::IsCategoryNameAllowed("bad_category   "));
+  EXPECT_FALSE(
+      TraceConfigCategoryFilter::IsCategoryNameAllowed("   bad_category   "));
+  EXPECT_FALSE(TraceConfigCategoryFilter::IsCategoryNameAllowed(""));
+  EXPECT_TRUE(
+      TraceConfigCategoryFilter::IsCategoryNameAllowed("good_category"));
+}
+
+TEST(TraceConfigTest, SetTraceOptionValues) {
+  TraceConfig tc;
+  EXPECT_EQ(RECORD_UNTIL_FULL, tc.GetTraceRecordMode());
+  EXPECT_FALSE(tc.IsSystraceEnabled());
+
+  tc.SetTraceRecordMode(RECORD_AS_MUCH_AS_POSSIBLE);
+  EXPECT_EQ(RECORD_AS_MUCH_AS_POSSIBLE, tc.GetTraceRecordMode());
+
+  tc.EnableSystrace();
+  EXPECT_TRUE(tc.IsSystraceEnabled());
+}
+
+TEST(TraceConfigTest, TraceConfigFromMemoryConfigString) {
+  std::string tc_str1 =
+      TraceConfigMemoryTestUtil::GetTraceConfig_PeriodicTriggers(200, 2000);
+  TraceConfig tc1(tc_str1);
+  EXPECT_EQ(tc_str1, tc1.ToString());
+  TraceConfig tc2(
+      TraceConfigMemoryTestUtil::GetTraceConfig_LegacyPeriodicTriggers(200,
+                                                                       2000));
+  EXPECT_EQ(tc_str1, tc2.ToString());
+
+  EXPECT_TRUE(tc1.IsCategoryGroupEnabled(MemoryDumpManager::kTraceCategory));
+  ASSERT_EQ(2u, tc1.memory_dump_config().triggers.size());
+
+  EXPECT_EQ(200u,
+            tc1.memory_dump_config().triggers[0].min_time_between_dumps_ms);
+  EXPECT_EQ(MemoryDumpLevelOfDetail::LIGHT,
+            tc1.memory_dump_config().triggers[0].level_of_detail);
+
+  EXPECT_EQ(2000u,
+            tc1.memory_dump_config().triggers[1].min_time_between_dumps_ms);
+  EXPECT_EQ(MemoryDumpLevelOfDetail::DETAILED,
+            tc1.memory_dump_config().triggers[1].level_of_detail);
+  EXPECT_EQ(
+      2048u,
+      tc1.memory_dump_config().heap_profiler_options.breakdown_threshold_bytes);
+
+  std::string tc_str3 =
+      TraceConfigMemoryTestUtil::GetTraceConfig_BackgroundTrigger(
+          1 /* period_ms */);
+  TraceConfig tc3(tc_str3);
+  EXPECT_EQ(tc_str3, tc3.ToString());
+  EXPECT_TRUE(tc3.IsCategoryGroupEnabled(MemoryDumpManager::kTraceCategory));
+  ASSERT_EQ(1u, tc3.memory_dump_config().triggers.size());
+  EXPECT_EQ(1u, tc3.memory_dump_config().triggers[0].min_time_between_dumps_ms);
+  EXPECT_EQ(MemoryDumpLevelOfDetail::BACKGROUND,
+            tc3.memory_dump_config().triggers[0].level_of_detail);
+}
+
+TEST(TraceConfigTest, EmptyMemoryDumpConfigTest) {
+  // Empty trigger list should also be specified when converting back to string.
+  TraceConfig tc(TraceConfigMemoryTestUtil::GetTraceConfig_EmptyTriggers());
+  EXPECT_EQ(TraceConfigMemoryTestUtil::GetTraceConfig_EmptyTriggers(),
+            tc.ToString());
+  EXPECT_EQ(0u, tc.memory_dump_config().triggers.size());
+  EXPECT_EQ(
+      static_cast<uint32_t>(TraceConfig::MemoryDumpConfig::HeapProfiler::
+                                kDefaultBreakdownThresholdBytes),
+      tc.memory_dump_config().heap_profiler_options.breakdown_threshold_bytes);
+}
+
+TEST(TraceConfigTest, LegacyStringToMemoryDumpConfig) {
+  TraceConfig tc(MemoryDumpManager::kTraceCategory, "");
+  EXPECT_TRUE(tc.IsCategoryGroupEnabled(MemoryDumpManager::kTraceCategory));
+  EXPECT_NE(std::string::npos, tc.ToString().find("memory_dump_config"));
+  EXPECT_EQ(0u, tc.memory_dump_config().triggers.size());
+  EXPECT_EQ(
+      static_cast<uint32_t>(TraceConfig::MemoryDumpConfig::HeapProfiler::
+                                kDefaultBreakdownThresholdBytes),
+      tc.memory_dump_config().heap_profiler_options.breakdown_threshold_bytes);
+}
+
+}  // namespace trace_event
+}  // namespace base
diff --git a/base/trace_event/trace_event_android.cc b/base/trace_event/trace_event_android.cc
new file mode 100644
index 0000000..30d9c74
--- /dev/null
+++ b/base/trace_event/trace_event_android.cc
@@ -0,0 +1,216 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/trace_event/trace_event_impl.h"
+
+#include <fcntl.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include "base/format_macros.h"
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/strings/stringprintf.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread.h"
+#include "base/trace_event/trace_event.h"
+
+namespace base {
+namespace trace_event {
+
+namespace {
+
+int g_atrace_fd = -1;
+const char kATraceMarkerFile[] = "/sys/kernel/debug/tracing/trace_marker";
+
+void WriteToATrace(int fd, const char* buffer, size_t size) {
+  size_t total_written = 0;
+  while (total_written < size) {
+    ssize_t written = HANDLE_EINTR(write(
+        fd, buffer + total_written, size - total_written));
+    if (written <= 0)
+      break;
+    total_written += written;
+  }
+  if (total_written < size) {
+    PLOG(WARNING) << "Failed to write buffer '" << std::string(buffer, size)
+                  << "' to " << kATraceMarkerFile;
+  }
+}
+
+void WriteEvent(
+    char phase,
+    const char* category_group,
+    const char* name,
+    unsigned long long id,
+    const char** arg_names,
+    const unsigned char* arg_types,
+    const TraceEvent::TraceValue* arg_values,
+    const std::unique_ptr<ConvertableToTraceFormat>* convertable_values,
+    unsigned int flags) {
+  std::string out = StringPrintf("%c|%d|%s", phase, getpid(), name);
+  if (flags & TRACE_EVENT_FLAG_HAS_ID)
+    StringAppendF(&out, "-%" PRIx64, static_cast<uint64_t>(id));
+  out += '|';
+
+  for (int i = 0; i < kTraceMaxNumArgs && arg_names[i];
+       ++i) {
+    if (i)
+      out += ';';
+    out += arg_names[i];
+    out += '=';
+    std::string::size_type value_start = out.length();
+    if (arg_types[i] == TRACE_VALUE_TYPE_CONVERTABLE)
+      convertable_values[i]->AppendAsTraceFormat(&out);
+    else
+      TraceEvent::AppendValueAsJSON(arg_types[i], arg_values[i], &out);
+
+    // Remove the quotes which may confuse the atrace script.
+    ReplaceSubstringsAfterOffset(&out, value_start, "\\\"", "'");
+    ReplaceSubstringsAfterOffset(&out, value_start, "\"", "");
+    // Replace chars used for separators with similar chars in the value.
+    std::replace(out.begin() + value_start, out.end(), ';', ',');
+    std::replace(out.begin() + value_start, out.end(), '|', '!');
+  }
+
+  out += '|';
+  out += category_group;
+  WriteToATrace(g_atrace_fd, out.c_str(), out.size());
+}
+
+void NoOpOutputCallback(WaitableEvent* complete_event,
+                        const scoped_refptr<RefCountedString>&,
+                        bool has_more_events) {
+  if (!has_more_events)
+    complete_event->Signal();
+}
+
+void EndChromeTracing(TraceLog* trace_log,
+                      WaitableEvent* complete_event) {
+  trace_log->SetDisabled();
+  // Delete the buffered trace events as they have been sent to atrace.
+  trace_log->Flush(Bind(&NoOpOutputCallback, complete_event));
+}
+
+}  // namespace
+
+// These functions support Android systrace.py when 'webview' category is
+// traced. With the new adb_profile_chrome, we may have two phases:
+// - before WebView is ready for combined tracing, we can use adb_profile_chrome
+//   to trace android categories other than 'webview' and chromium categories.
+//   In this way we can avoid the conflict between StartATrace/StopATrace and
+//   the intents.
+// - TODO(wangxianzhu): after WebView is ready for combined tracing, remove
+//   StartATrace, StopATrace and SendToATrace, and perhaps send Java traces
+//   directly to atrace in trace_event_binding.cc.
+
+void TraceLog::StartATrace() {
+  if (g_atrace_fd != -1)
+    return;
+
+  g_atrace_fd = HANDLE_EINTR(open(kATraceMarkerFile, O_WRONLY));
+  if (g_atrace_fd == -1) {
+    PLOG(WARNING) << "Couldn't open " << kATraceMarkerFile;
+    return;
+  }
+  TraceConfig trace_config;
+  trace_config.SetTraceRecordMode(RECORD_CONTINUOUSLY);
+  SetEnabled(trace_config, TraceLog::RECORDING_MODE);
+}
+
+void TraceLog::StopATrace() {
+  if (g_atrace_fd == -1)
+    return;
+
+  close(g_atrace_fd);
+  g_atrace_fd = -1;
+
+  // TraceLog::Flush() requires the current thread to have a message loop, but
+  // this thread called from Java may not have one, so flush in another thread.
+  Thread end_chrome_tracing_thread("end_chrome_tracing");
+  WaitableEvent complete_event(WaitableEvent::ResetPolicy::AUTOMATIC,
+                               WaitableEvent::InitialState::NOT_SIGNALED);
+  end_chrome_tracing_thread.Start();
+  end_chrome_tracing_thread.task_runner()->PostTask(
+      FROM_HERE, base::Bind(&EndChromeTracing, Unretained(this),
+                            Unretained(&complete_event)));
+  complete_event.Wait();
+}
+
+void TraceEvent::SendToATrace() {
+  if (g_atrace_fd == -1)
+    return;
+
+  const char* category_group =
+      TraceLog::GetCategoryGroupName(category_group_enabled_);
+
+  switch (phase_) {
+    case TRACE_EVENT_PHASE_BEGIN:
+      WriteEvent('B', category_group, name_, id_,
+                 arg_names_, arg_types_, arg_values_, convertable_values_,
+                 flags_);
+      break;
+
+    case TRACE_EVENT_PHASE_COMPLETE:
+      WriteEvent(duration_.ToInternalValue() == -1 ? 'B' : 'E',
+                 category_group, name_, id_,
+                 arg_names_, arg_types_, arg_values_, convertable_values_,
+                 flags_);
+      break;
+
+    case TRACE_EVENT_PHASE_END:
+      // Though a single 'E' is enough, here append pid, name and
+      // category_group etc. So that unpaired events can be found easily.
+      WriteEvent('E', category_group, name_, id_,
+                 arg_names_, arg_types_, arg_values_, convertable_values_,
+                 flags_);
+      break;
+
+    case TRACE_EVENT_PHASE_INSTANT:
+      // Simulate an instance event with a pair of begin/end events.
+      WriteEvent('B', category_group, name_, id_,
+                 arg_names_, arg_types_, arg_values_, convertable_values_,
+                 flags_);
+      WriteToATrace(g_atrace_fd, "E", 1);
+      break;
+
+    case TRACE_EVENT_PHASE_COUNTER:
+      for (int i = 0; i < kTraceMaxNumArgs && arg_names_[i]; ++i) {
+        DCHECK(arg_types_[i] == TRACE_VALUE_TYPE_INT);
+        std::string out = base::StringPrintf(
+            "C|%d|%s-%s", getpid(), name_, arg_names_[i]);
+        if (flags_ & TRACE_EVENT_FLAG_HAS_ID)
+          StringAppendF(&out, "-%" PRIx64, static_cast<uint64_t>(id_));
+        StringAppendF(&out, "|%d|%s",
+                      static_cast<int>(arg_values_[i].as_int), category_group);
+        WriteToATrace(g_atrace_fd, out.c_str(), out.size());
+      }
+      break;
+
+    default:
+      // Do nothing.
+      break;
+  }
+}
+
+void TraceLog::AddClockSyncMetadataEvent() {
+  int atrace_fd = HANDLE_EINTR(open(kATraceMarkerFile, O_WRONLY | O_APPEND));
+  if (atrace_fd == -1) {
+    PLOG(WARNING) << "Couldn't open " << kATraceMarkerFile;
+    return;
+  }
+
+  // Android's kernel trace system has a trace_marker feature: this is a file on
+  // debugfs that takes the written data and pushes it onto the trace
+  // buffer. So, to establish clock sync, we write our monotonic clock into that
+  // trace buffer.
+  double now_in_seconds = (TRACE_TIME_TICKS_NOW() - TimeTicks()).InSecondsF();
+  std::string marker = StringPrintf(
+      "trace_event_clock_sync: parent_ts=%f\n", now_in_seconds);
+  WriteToATrace(atrace_fd, marker.c_str(), marker.size());
+  close(atrace_fd);
+}
+
+}  // namespace trace_event
+}  // namespace base
diff --git a/base/trace_event/trace_event_android_unittest.cc b/base/trace_event/trace_event_android_unittest.cc
new file mode 100644
index 0000000..58bd77e
--- /dev/null
+++ b/base/trace_event/trace_event_android_unittest.cc
@@ -0,0 +1,22 @@
+// Copyright (c) 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/trace_event/trace_event.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace trace_event {
+
+TEST(TraceEventAndroidTest, WriteToATrace) {
+  // Just a smoke test to ensure no crash.
+  TraceLog* trace_log = TraceLog::GetInstance();
+  trace_log->StartATrace();
+  TRACE_EVENT0("test", "test-event");
+  trace_log->StopATrace();
+  trace_log->AddClockSyncMetadataEvent();
+}
+
+}  // namespace trace_event
+}  // namespace base
diff --git a/base/trace_event/trace_event_argument.cc b/base/trace_event/trace_event_argument.cc
new file mode 100644
index 0000000..e614b27
--- /dev/null
+++ b/base/trace_event/trace_event_argument.cc
@@ -0,0 +1,576 @@
+// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/trace_event/trace_event_argument.h"
+
+#include <stdint.h>
+
+#include <utility>
+
+#include "base/bits.h"
+#include "base/containers/circular_deque.h"
+#include "base/json/string_escape.h"
+#include "base/memory/ptr_util.h"
+#include "base/trace_event/trace_event.h"
+#include "base/trace_event/trace_event_impl.h"
+#include "base/trace_event/trace_event_memory_overhead.h"
+#include "base/values.h"
+
+namespace base {
+namespace trace_event {
+
+namespace {
+const char kTypeStartDict = '{';
+const char kTypeEndDict = '}';
+const char kTypeStartArray = '[';
+const char kTypeEndArray = ']';
+const char kTypeBool = 'b';
+const char kTypeInt = 'i';
+const char kTypeDouble = 'd';
+const char kTypeString = 's';
+const char kTypeCStr = '*';  // only used for key names
+
+#ifndef NDEBUG
+const bool kStackTypeDict = false;
+const bool kStackTypeArray = true;
+#define DCHECK_CURRENT_CONTAINER_IS(x) DCHECK_EQ(x, nesting_stack_.back())
+#define DCHECK_CONTAINER_STACK_DEPTH_EQ(x) DCHECK_EQ(x, nesting_stack_.size())
+#define DEBUG_PUSH_CONTAINER(x) nesting_stack_.push_back(x)
+#define DEBUG_POP_CONTAINER() nesting_stack_.pop_back()
+#else
+#define DCHECK_CURRENT_CONTAINER_IS(x) do {} while (0)
+#define DCHECK_CONTAINER_STACK_DEPTH_EQ(x) do {} while (0)
+#define DEBUG_PUSH_CONTAINER(x) do {} while (0)
+#define DEBUG_POP_CONTAINER() do {} while (0)
+#endif
+
+inline void WriteKeyNameAsRawPtr(Pickle& pickle, const char* ptr) {
+  pickle.WriteBytes(&kTypeCStr, 1);
+  pickle.WriteUInt64(static_cast<uint64_t>(reinterpret_cast<uintptr_t>(ptr)));
+}
+
+inline void WriteKeyNameWithCopy(Pickle& pickle, base::StringPiece str) {
+  pickle.WriteBytes(&kTypeString, 1);
+  pickle.WriteString(str);
+}
+
+std::string ReadKeyName(PickleIterator& pickle_iterator) {
+  const char* type = nullptr;
+  bool res = pickle_iterator.ReadBytes(&type, 1);
+  std::string key_name;
+  if (res && *type == kTypeCStr) {
+    uint64_t ptr_value = 0;
+    res = pickle_iterator.ReadUInt64(&ptr_value);
+    key_name = reinterpret_cast<const char*>(static_cast<uintptr_t>(ptr_value));
+  } else if (res && *type == kTypeString) {
+    res = pickle_iterator.ReadString(&key_name);
+  }
+  DCHECK(res);
+  return key_name;
+}
+}  // namespace
+
+TracedValue::TracedValue() : TracedValue(0) {
+}
+
+TracedValue::TracedValue(size_t capacity) {
+  DEBUG_PUSH_CONTAINER(kStackTypeDict);
+  if (capacity)
+    pickle_.Reserve(capacity);
+}
+
+TracedValue::~TracedValue() {
+  DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
+  DEBUG_POP_CONTAINER();
+  DCHECK_CONTAINER_STACK_DEPTH_EQ(0u);
+}
+
+void TracedValue::SetInteger(const char* name, int value) {
+  DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
+  pickle_.WriteBytes(&kTypeInt, 1);
+  pickle_.WriteInt(value);
+  WriteKeyNameAsRawPtr(pickle_, name);
+}
+
+void TracedValue::SetIntegerWithCopiedName(base::StringPiece name, int value) {
+  DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
+  pickle_.WriteBytes(&kTypeInt, 1);
+  pickle_.WriteInt(value);
+  WriteKeyNameWithCopy(pickle_, name);
+}
+
+void TracedValue::SetDouble(const char* name, double value) {
+  DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
+  pickle_.WriteBytes(&kTypeDouble, 1);
+  pickle_.WriteDouble(value);
+  WriteKeyNameAsRawPtr(pickle_, name);
+}
+
+void TracedValue::SetDoubleWithCopiedName(base::StringPiece name,
+                                          double value) {
+  DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
+  pickle_.WriteBytes(&kTypeDouble, 1);
+  pickle_.WriteDouble(value);
+  WriteKeyNameWithCopy(pickle_, name);
+}
+
+void TracedValue::SetBoolean(const char* name, bool value) {
+  DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
+  pickle_.WriteBytes(&kTypeBool, 1);
+  pickle_.WriteBool(value);
+  WriteKeyNameAsRawPtr(pickle_, name);
+}
+
+void TracedValue::SetBooleanWithCopiedName(base::StringPiece name,
+                                           bool value) {
+  DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
+  pickle_.WriteBytes(&kTypeBool, 1);
+  pickle_.WriteBool(value);
+  WriteKeyNameWithCopy(pickle_, name);
+}
+
+void TracedValue::SetString(const char* name, base::StringPiece value) {
+  DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
+  pickle_.WriteBytes(&kTypeString, 1);
+  pickle_.WriteString(value);
+  WriteKeyNameAsRawPtr(pickle_, name);
+}
+
+void TracedValue::SetStringWithCopiedName(base::StringPiece name,
+                                          base::StringPiece value) {
+  DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
+  pickle_.WriteBytes(&kTypeString, 1);
+  pickle_.WriteString(value);
+  WriteKeyNameWithCopy(pickle_, name);
+}
+
+void TracedValue::SetValue(const char* name, const TracedValue& value) {
+  DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
+  BeginDictionary(name);
+  pickle_.WriteBytes(value.pickle_.payload(),
+                     static_cast<int>(value.pickle_.payload_size()));
+  EndDictionary();
+}
+
+void TracedValue::SetValueWithCopiedName(base::StringPiece name,
+                                         const TracedValue& value) {
+  DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
+  BeginDictionaryWithCopiedName(name);
+  pickle_.WriteBytes(value.pickle_.payload(),
+                     static_cast<int>(value.pickle_.payload_size()));
+  EndDictionary();
+}
+
+void TracedValue::BeginDictionary(const char* name) {
+  DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
+  DEBUG_PUSH_CONTAINER(kStackTypeDict);
+  pickle_.WriteBytes(&kTypeStartDict, 1);
+  WriteKeyNameAsRawPtr(pickle_, name);
+}
+
+void TracedValue::BeginDictionaryWithCopiedName(base::StringPiece name) {
+  DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
+  DEBUG_PUSH_CONTAINER(kStackTypeDict);
+  pickle_.WriteBytes(&kTypeStartDict, 1);
+  WriteKeyNameWithCopy(pickle_, name);
+}
+
+void TracedValue::BeginArray(const char* name) {
+  DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
+  DEBUG_PUSH_CONTAINER(kStackTypeArray);
+  pickle_.WriteBytes(&kTypeStartArray, 1);
+  WriteKeyNameAsRawPtr(pickle_, name);
+}
+
+void TracedValue::BeginArrayWithCopiedName(base::StringPiece name) {
+  DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
+  DEBUG_PUSH_CONTAINER(kStackTypeArray);
+  pickle_.WriteBytes(&kTypeStartArray, 1);
+  WriteKeyNameWithCopy(pickle_, name);
+}
+
+void TracedValue::EndDictionary() {
+  DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
+  DEBUG_POP_CONTAINER();
+  pickle_.WriteBytes(&kTypeEndDict, 1);
+}
+
+void TracedValue::AppendInteger(int value) {
+  DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray);
+  pickle_.WriteBytes(&kTypeInt, 1);
+  pickle_.WriteInt(value);
+}
+
+void TracedValue::AppendDouble(double value) {
+  DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray);
+  pickle_.WriteBytes(&kTypeDouble, 1);
+  pickle_.WriteDouble(value);
+}
+
+void TracedValue::AppendBoolean(bool value) {
+  DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray);
+  pickle_.WriteBytes(&kTypeBool, 1);
+  pickle_.WriteBool(value);
+}
+
+void TracedValue::AppendString(base::StringPiece value) {
+  DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray);
+  pickle_.WriteBytes(&kTypeString, 1);
+  pickle_.WriteString(value);
+}
+
+void TracedValue::BeginArray() {
+  DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray);
+  DEBUG_PUSH_CONTAINER(kStackTypeArray);
+  pickle_.WriteBytes(&kTypeStartArray, 1);
+}
+
+void TracedValue::BeginDictionary() {
+  DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray);
+  DEBUG_PUSH_CONTAINER(kStackTypeDict);
+  pickle_.WriteBytes(&kTypeStartDict, 1);
+}
+
+void TracedValue::EndArray() {
+  DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray);
+  DEBUG_POP_CONTAINER();
+  pickle_.WriteBytes(&kTypeEndArray, 1);
+}
+
+void TracedValue::SetValue(const char* name,
+                           std::unique_ptr<base::Value> value) {
+  SetBaseValueWithCopiedName(name, *value);
+}
+
+void TracedValue::SetBaseValueWithCopiedName(base::StringPiece name,
+                                             const base::Value& value) {
+  DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
+  switch (value.type()) {
+    case base::Value::Type::NONE:
+    case base::Value::Type::BINARY:
+      NOTREACHED();
+      break;
+
+    case base::Value::Type::BOOLEAN: {
+      bool bool_value;
+      value.GetAsBoolean(&bool_value);
+      SetBooleanWithCopiedName(name, bool_value);
+    } break;
+
+    case base::Value::Type::INTEGER: {
+      int int_value;
+      value.GetAsInteger(&int_value);
+      SetIntegerWithCopiedName(name, int_value);
+    } break;
+
+    case base::Value::Type::DOUBLE: {
+      double double_value;
+      value.GetAsDouble(&double_value);
+      SetDoubleWithCopiedName(name, double_value);
+    } break;
+
+    case base::Value::Type::STRING: {
+      const Value* string_value;
+      value.GetAsString(&string_value);
+      SetStringWithCopiedName(name, string_value->GetString());
+    } break;
+
+    case base::Value::Type::DICTIONARY: {
+      const DictionaryValue* dict_value;
+      value.GetAsDictionary(&dict_value);
+      BeginDictionaryWithCopiedName(name);
+      for (DictionaryValue::Iterator it(*dict_value); !it.IsAtEnd();
+           it.Advance()) {
+        SetBaseValueWithCopiedName(it.key(), it.value());
+      }
+      EndDictionary();
+    } break;
+
+    case base::Value::Type::LIST: {
+      const ListValue* list_value;
+      value.GetAsList(&list_value);
+      BeginArrayWithCopiedName(name);
+      for (const auto& base_value : *list_value)
+        AppendBaseValue(base_value);
+      EndArray();
+    } break;
+  }
+}
+
+void TracedValue::AppendBaseValue(const base::Value& value) {
+  DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray);
+  switch (value.type()) {
+    case base::Value::Type::NONE:
+    case base::Value::Type::BINARY:
+      NOTREACHED();
+      break;
+
+    case base::Value::Type::BOOLEAN: {
+      bool bool_value;
+      value.GetAsBoolean(&bool_value);
+      AppendBoolean(bool_value);
+    } break;
+
+    case base::Value::Type::INTEGER: {
+      int int_value;
+      value.GetAsInteger(&int_value);
+      AppendInteger(int_value);
+    } break;
+
+    case base::Value::Type::DOUBLE: {
+      double double_value;
+      value.GetAsDouble(&double_value);
+      AppendDouble(double_value);
+    } break;
+
+    case base::Value::Type::STRING: {
+      const Value* string_value;
+      value.GetAsString(&string_value);
+      AppendString(string_value->GetString());
+    } break;
+
+    case base::Value::Type::DICTIONARY: {
+      const DictionaryValue* dict_value;
+      value.GetAsDictionary(&dict_value);
+      BeginDictionary();
+      for (DictionaryValue::Iterator it(*dict_value); !it.IsAtEnd();
+           it.Advance()) {
+        SetBaseValueWithCopiedName(it.key(), it.value());
+      }
+      EndDictionary();
+    } break;
+
+    case base::Value::Type::LIST: {
+      const ListValue* list_value;
+      value.GetAsList(&list_value);
+      BeginArray();
+      for (const auto& base_value : *list_value)
+        AppendBaseValue(base_value);
+      EndArray();
+    } break;
+  }
+}
+
+std::unique_ptr<base::Value> TracedValue::ToBaseValue() const {
+  base::Value root(base::Value::Type::DICTIONARY);
+  Value* cur_dict = &root;
+  Value* cur_list = nullptr;
+  std::vector<Value*> stack;
+  PickleIterator it(pickle_);
+  const char* type;
+
+  while (it.ReadBytes(&type, 1)) {
+    DCHECK((cur_dict && !cur_list) || (cur_list && !cur_dict));
+    switch (*type) {
+      case kTypeStartDict: {
+        base::Value new_dict(base::Value::Type::DICTIONARY);
+        if (cur_dict) {
+          stack.push_back(cur_dict);
+          cur_dict = cur_dict->SetKey(ReadKeyName(it), std::move(new_dict));
+        } else {
+          cur_list->GetList().push_back(std::move(new_dict));
+          // |new_dict| is invalidated at this point, so |cur_dict| needs to be
+          // reset.
+          cur_dict = &cur_list->GetList().back();
+          stack.push_back(cur_list);
+          cur_list = nullptr;
+        }
+      } break;
+
+      case kTypeEndArray:
+      case kTypeEndDict: {
+        if (stack.back()->is_dict()) {
+          cur_dict = stack.back();
+          cur_list = nullptr;
+        } else if (stack.back()->is_list()) {
+          cur_list = stack.back();
+          cur_dict = nullptr;
+        }
+        stack.pop_back();
+      } break;
+
+      case kTypeStartArray: {
+        base::Value new_list(base::Value::Type::LIST);
+        if (cur_dict) {
+          stack.push_back(cur_dict);
+          cur_list = cur_dict->SetKey(ReadKeyName(it), std::move(new_list));
+          cur_dict = nullptr;
+        } else {
+          cur_list->GetList().push_back(std::move(new_list));
+          stack.push_back(cur_list);
+          // |cur_list| is invalidated at this point by the Append, so it needs
+          // to be reset.
+          cur_list = &cur_list->GetList().back();
+        }
+      } break;
+
+      case kTypeBool: {
+        bool value;
+        CHECK(it.ReadBool(&value));
+        base::Value new_bool(value);
+        if (cur_dict) {
+          cur_dict->SetKey(ReadKeyName(it), std::move(new_bool));
+        } else {
+          cur_list->GetList().push_back(std::move(new_bool));
+        }
+      } break;
+
+      case kTypeInt: {
+        int value;
+        CHECK(it.ReadInt(&value));
+        base::Value new_int(value);
+        if (cur_dict) {
+          cur_dict->SetKey(ReadKeyName(it), std::move(new_int));
+        } else {
+          cur_list->GetList().push_back(std::move(new_int));
+        }
+      } break;
+
+      case kTypeDouble: {
+        double value;
+        CHECK(it.ReadDouble(&value));
+        base::Value new_double(value);
+        if (cur_dict) {
+          cur_dict->SetKey(ReadKeyName(it), std::move(new_double));
+        } else {
+          cur_list->GetList().push_back(std::move(new_double));
+        }
+      } break;
+
+      case kTypeString: {
+        std::string value;
+        CHECK(it.ReadString(&value));
+        base::Value new_str(std::move(value));
+        if (cur_dict) {
+          cur_dict->SetKey(ReadKeyName(it), std::move(new_str));
+        } else {
+          cur_list->GetList().push_back(std::move(new_str));
+        }
+      } break;
+
+      default:
+        NOTREACHED();
+    }
+  }
+  DCHECK(stack.empty());
+  return base::Value::ToUniquePtrValue(std::move(root));
+}
+
+void TracedValue::AppendAsTraceFormat(std::string* out) const {
+  DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
+  DCHECK_CONTAINER_STACK_DEPTH_EQ(1u);
+
+  struct State {
+    enum Type { kTypeDict, kTypeArray };
+    Type type;
+    bool needs_comma;
+  };
+
+  auto maybe_append_key_name = [](State current_state, PickleIterator* it,
+                                  std::string* out) {
+    if (current_state.type == State::kTypeDict) {
+      EscapeJSONString(ReadKeyName(*it), true, out);
+      out->append(":");
+    }
+  };
+
+  base::circular_deque<State> state_stack;
+
+  out->append("{");
+  state_stack.push_back({State::kTypeDict});
+
+  PickleIterator it(pickle_);
+  for (const char* type; it.ReadBytes(&type, 1);) {
+    switch (*type) {
+      case kTypeEndDict:
+        out->append("}");
+        state_stack.pop_back();
+        continue;
+
+      case kTypeEndArray:
+        out->append("]");
+        state_stack.pop_back();
+        continue;
+    }
+
+    // Use an index so it will stay valid across resizes.
+    size_t current_state_index = state_stack.size() - 1;
+    if (state_stack[current_state_index].needs_comma)
+      out->append(",");
+
+    switch (*type) {
+      case kTypeStartDict: {
+        maybe_append_key_name(state_stack[current_state_index], &it, out);
+        out->append("{");
+        state_stack.push_back({State::kTypeDict});
+        break;
+      }
+
+      case kTypeStartArray: {
+        maybe_append_key_name(state_stack[current_state_index], &it, out);
+        out->append("[");
+        state_stack.push_back({State::kTypeArray});
+        break;
+      }
+
+      case kTypeBool: {
+        TraceEvent::TraceValue json_value;
+        CHECK(it.ReadBool(&json_value.as_bool));
+        maybe_append_key_name(state_stack[current_state_index], &it, out);
+        TraceEvent::AppendValueAsJSON(TRACE_VALUE_TYPE_BOOL, json_value, out);
+        break;
+      }
+
+      case kTypeInt: {
+        int value;
+        CHECK(it.ReadInt(&value));
+        maybe_append_key_name(state_stack[current_state_index], &it, out);
+        TraceEvent::TraceValue json_value;
+        json_value.as_int = value;
+        TraceEvent::AppendValueAsJSON(TRACE_VALUE_TYPE_INT, json_value, out);
+        break;
+      }
+
+      case kTypeDouble: {
+        TraceEvent::TraceValue json_value;
+        CHECK(it.ReadDouble(&json_value.as_double));
+        maybe_append_key_name(state_stack[current_state_index], &it, out);
+        TraceEvent::AppendValueAsJSON(TRACE_VALUE_TYPE_DOUBLE, json_value, out);
+        break;
+      }
+
+      case kTypeString: {
+        std::string value;
+        CHECK(it.ReadString(&value));
+        maybe_append_key_name(state_stack[current_state_index], &it, out);
+        TraceEvent::TraceValue json_value;
+        json_value.as_string = value.c_str();
+        TraceEvent::AppendValueAsJSON(TRACE_VALUE_TYPE_STRING, json_value, out);
+        break;
+      }
+
+      default:
+        NOTREACHED();
+    }
+
+    state_stack[current_state_index].needs_comma = true;
+  }
+
+  out->append("}");
+  state_stack.pop_back();
+
+  DCHECK(state_stack.empty());
+}
+
+void TracedValue::EstimateTraceMemoryOverhead(
+    TraceEventMemoryOverhead* overhead) {
+  overhead->Add(TraceEventMemoryOverhead::kTracedValue,
+                /* allocated size */
+                pickle_.GetTotalAllocatedSize(),
+                /* resident size */
+                pickle_.size());
+}
+
+}  // namespace trace_event
+}  // namespace base
diff --git a/base/trace_event/trace_event_argument.h b/base/trace_event/trace_event_argument.h
new file mode 100644
index 0000000..81d8c01
--- /dev/null
+++ b/base/trace_event/trace_event_argument.h
@@ -0,0 +1,92 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TRACE_EVENT_TRACE_EVENT_ARGUMENT_H_
+#define BASE_TRACE_EVENT_TRACE_EVENT_ARGUMENT_H_
+
+#include <stddef.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/pickle.h"
+#include "base/strings/string_piece.h"
+#include "base/trace_event/trace_event_impl.h"
+
+namespace base {
+
+class Value;
+
+namespace trace_event {
+
+class BASE_EXPORT TracedValue : public ConvertableToTraceFormat {
+ public:
+  TracedValue();
+  explicit TracedValue(size_t capacity);
+  ~TracedValue() override;
+
+  void EndDictionary();
+  void EndArray();
+
+  // These methods assume that |name| is a long lived "quoted" string.
+  void SetInteger(const char* name, int value);
+  void SetDouble(const char* name, double value);
+  void SetBoolean(const char* name, bool value);
+  void SetString(const char* name, base::StringPiece value);
+  void SetValue(const char* name, const TracedValue& value);
+  void BeginDictionary(const char* name);
+  void BeginArray(const char* name);
+
+  // These, instead, can be safely passed a temporary string.
+  void SetIntegerWithCopiedName(base::StringPiece name, int value);
+  void SetDoubleWithCopiedName(base::StringPiece name, double value);
+  void SetBooleanWithCopiedName(base::StringPiece name, bool value);
+  void SetStringWithCopiedName(base::StringPiece name,
+                               base::StringPiece value);
+  void SetValueWithCopiedName(base::StringPiece name,
+                              const TracedValue& value);
+  void BeginDictionaryWithCopiedName(base::StringPiece name);
+  void BeginArrayWithCopiedName(base::StringPiece name);
+
+  void AppendInteger(int);
+  void AppendDouble(double);
+  void AppendBoolean(bool);
+  void AppendString(base::StringPiece);
+  void BeginArray();
+  void BeginDictionary();
+
+  // ConvertableToTraceFormat implementation.
+  void AppendAsTraceFormat(std::string* out) const override;
+
+  void EstimateTraceMemoryOverhead(TraceEventMemoryOverhead* overhead) override;
+
+  // DEPRECATED: do not use, here only for legacy reasons. These methods causes
+  // a copy-and-translation of the base::Value into the equivalent TracedValue.
+  // TODO(primiano): migrate the (three) existing clients to the cheaper
+  // SetValue(TracedValue) API. crbug.com/495628.
+  void SetValue(const char* name, std::unique_ptr<base::Value> value);
+  void SetBaseValueWithCopiedName(base::StringPiece name,
+                                  const base::Value& value);
+  void AppendBaseValue(const base::Value& value);
+
+  // Public for tests only.
+  std::unique_ptr<base::Value> ToBaseValue() const;
+
+ private:
+  Pickle pickle_;
+
+#ifndef NDEBUG
+  // In debug builds checks the pairings of {Start,End}{Dictionary,Array}
+  std::vector<bool> nesting_stack_;
+#endif
+
+  DISALLOW_COPY_AND_ASSIGN(TracedValue);
+};
+
+}  // namespace trace_event
+}  // namespace base
+
+#endif  // BASE_TRACE_EVENT_TRACE_EVENT_ARGUMENT_H_
diff --git a/base/trace_event/trace_event_argument_unittest.cc b/base/trace_event/trace_event_argument_unittest.cc
new file mode 100644
index 0000000..448b2d5
--- /dev/null
+++ b/base/trace_event/trace_event_argument_unittest.cc
@@ -0,0 +1,165 @@
+// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/trace_event/trace_event_argument.h"
+
+#include <stddef.h>
+
+#include <utility>
+
+#include "base/memory/ptr_util.h"
+#include "base/values.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace trace_event {
+
+TEST(TraceEventArgumentTest, FlatDictionary) {
+  std::unique_ptr<TracedValue> value(new TracedValue());
+  value->SetBoolean("bool", true);
+  value->SetDouble("double", 0.0);
+  value->SetInteger("int", 2014);
+  value->SetString("string", "string");
+  std::string json = "PREFIX";
+  value->AppendAsTraceFormat(&json);
+  EXPECT_EQ(
+      "PREFIX{\"bool\":true,\"double\":0.0,\"int\":2014,\"string\":\"string\"}",
+      json);
+}
+
+TEST(TraceEventArgumentTest, NoDotPathExpansion) {
+  std::unique_ptr<TracedValue> value(new TracedValue());
+  value->SetBoolean("bo.ol", true);
+  value->SetDouble("doub.le", 0.0);
+  value->SetInteger("in.t", 2014);
+  value->SetString("str.ing", "str.ing");
+  std::string json;
+  value->AppendAsTraceFormat(&json);
+  EXPECT_EQ(
+      "{\"bo.ol\":true,\"doub.le\":0.0,\"in.t\":2014,\"str.ing\":\"str.ing\"}",
+      json);
+}
+
+TEST(TraceEventArgumentTest, Hierarchy) {
+  std::unique_ptr<TracedValue> value(new TracedValue());
+  value->BeginArray("a1");
+  value->AppendInteger(1);
+  value->AppendBoolean(true);
+  value->BeginDictionary();
+  value->SetInteger("i2", 3);
+  value->EndDictionary();
+  value->EndArray();
+  value->SetBoolean("b0", true);
+  value->SetDouble("d0", 0.0);
+  value->BeginDictionary("dict1");
+  value->BeginDictionary("dict2");
+  value->SetBoolean("b2", false);
+  value->EndDictionary();
+  value->SetInteger("i1", 2014);
+  value->SetString("s1", "foo");
+  value->EndDictionary();
+  value->SetInteger("i0", 2014);
+  value->SetString("s0", "foo");
+  std::string json;
+  value->AppendAsTraceFormat(&json);
+  EXPECT_EQ(
+      "{\"a1\":[1,true,{\"i2\":3}],\"b0\":true,\"d0\":0.0,\"dict1\":{\"dict2\":"
+      "{\"b2\":false},\"i1\":2014,\"s1\":\"foo\"},\"i0\":2014,\"s0\":"
+      "\"foo\"}",
+      json);
+}
+
+TEST(TraceEventArgumentTest, LongStrings) {
+  std::string kLongString = "supercalifragilisticexpialidocious";
+  std::string kLongString2 = "0123456789012345678901234567890123456789";
+  char kLongString3[4096];
+  for (size_t i = 0; i < sizeof(kLongString3); ++i)
+    kLongString3[i] = 'a' + (i % 25);
+  kLongString3[sizeof(kLongString3) - 1] = '\0';
+
+  std::unique_ptr<TracedValue> value(new TracedValue());
+  value->SetString("a", "short");
+  value->SetString("b", kLongString);
+  value->BeginArray("c");
+  value->AppendString(kLongString2);
+  value->AppendString("");
+  value->BeginDictionary();
+  value->SetString("a", kLongString3);
+  value->EndDictionary();
+  value->EndArray();
+
+  std::string json;
+  value->AppendAsTraceFormat(&json);
+  EXPECT_EQ("{\"a\":\"short\",\"b\":\"" + kLongString + "\",\"c\":[\"" +
+                kLongString2 + "\",\"\",{\"a\":\"" + kLongString3 + "\"}]}",
+            json);
+}
+
+TEST(TraceEventArgumentTest, PassBaseValue) {
+  Value int_value(42);
+  Value bool_value(true);
+  Value double_value(42.0f);
+
+  auto dict_value = WrapUnique(new DictionaryValue);
+  dict_value->SetBoolean("bool", true);
+  dict_value->SetInteger("int", 42);
+  dict_value->SetDouble("double", 42.0f);
+  dict_value->SetString("string", std::string("a") + "b");
+  dict_value->SetString("string", std::string("a") + "b");
+
+  auto list_value = WrapUnique(new ListValue);
+  list_value->AppendBoolean(false);
+  list_value->AppendInteger(1);
+  list_value->AppendString("in_list");
+  list_value->Append(std::move(dict_value));
+
+  std::unique_ptr<TracedValue> value(new TracedValue());
+  value->BeginDictionary("outer_dict");
+  value->SetValue("inner_list", std::move(list_value));
+  value->EndDictionary();
+
+  dict_value.reset();
+  list_value.reset();
+
+  std::string json;
+  value->AppendAsTraceFormat(&json);
+  EXPECT_EQ(
+      "{\"outer_dict\":{\"inner_list\":[false,1,\"in_list\",{\"bool\":true,"
+      "\"double\":42.0,\"int\":42,\"string\":\"ab\"}]}}",
+      json);
+}
+
+TEST(TraceEventArgumentTest, PassTracedValue) {
+  auto dict_value = std::make_unique<TracedValue>();
+  dict_value->SetInteger("a", 1);
+
+  auto nested_dict_value = std::make_unique<TracedValue>();
+  nested_dict_value->SetInteger("b", 2);
+  nested_dict_value->BeginArray("c");
+  nested_dict_value->AppendString("foo");
+  nested_dict_value->EndArray();
+
+  dict_value->SetValue("e", *nested_dict_value);
+
+  // Check the merged result.
+  std::string json;
+  dict_value->AppendAsTraceFormat(&json);
+  EXPECT_EQ("{\"a\":1,\"e\":{\"b\":2,\"c\":[\"foo\"]}}", json);
+
+  // Check that the passed nestd dict was left unouthced.
+  json = "";
+  nested_dict_value->AppendAsTraceFormat(&json);
+  EXPECT_EQ("{\"b\":2,\"c\":[\"foo\"]}", json);
+
+  // And that it is still usable.
+  nested_dict_value->SetInteger("f", 3);
+  nested_dict_value->BeginDictionary("g");
+  nested_dict_value->EndDictionary();
+  json = "";
+  nested_dict_value->AppendAsTraceFormat(&json);
+  EXPECT_EQ("{\"b\":2,\"c\":[\"foo\"],\"f\":3,\"g\":{}}", json);
+}
+
+}  // namespace trace_event
+}  // namespace base
diff --git a/base/trace_event/trace_event_filter.cc b/base/trace_event/trace_event_filter.cc
new file mode 100644
index 0000000..d0b116e
--- /dev/null
+++ b/base/trace_event/trace_event_filter.cc
@@ -0,0 +1,17 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/trace_event/trace_event_filter.h"
+
+namespace base {
+namespace trace_event {
+
+TraceEventFilter::TraceEventFilter() = default;
+TraceEventFilter::~TraceEventFilter() = default;
+
+void TraceEventFilter::EndEvent(const char* category_name,
+                                const char* event_name) const {}
+
+}  // namespace trace_event
+}  // namespace base
diff --git a/base/trace_event/trace_event_filter.h b/base/trace_event/trace_event_filter.h
new file mode 100644
index 0000000..48c6711
--- /dev/null
+++ b/base/trace_event/trace_event_filter.h
@@ -0,0 +1,51 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TRACE_EVENT_TRACE_EVENT_FILTER_H_
+#define BASE_TRACE_EVENT_TRACE_EVENT_FILTER_H_
+
+#include <memory>
+
+#include "base/base_export.h"
+#include "base/macros.h"
+
+namespace base {
+namespace trace_event {
+
+class TraceEvent;
+
+// TraceEventFilter is like iptables for TRACE_EVENT macros. Filters can be
+// enabled on a per-category basis, hence a single filter instance can serve
+// more than a TraceCategory. There are two use cases for filters:
+// 1. Snooping TRACE_EVENT macros without adding them to the TraceLog. This is
+//    possible by setting the ENABLED_FOR_FILTERING flag on a category w/o
+//    ENABLED_FOR_RECORDING (see TraceConfig for user-facing configuration).
+// 2. Filtering TRACE_EVENT macros before they are added to the TraceLog. This
+//    requires both the ENABLED_FOR_FILTERING and ENABLED_FOR_RECORDING flags
+//    on the category.
+// More importantly, filters must be thread-safe. The FilterTraceEvent and
+// EndEvent methods can be called concurrently as trace macros are hit on
+// different threads.
+class BASE_EXPORT TraceEventFilter {
+ public:
+  TraceEventFilter();
+  virtual ~TraceEventFilter();
+
+  // If the category is ENABLED_FOR_RECORDING, the event is added iff all the
+  // filters enabled for the category return true. false causes the event to be
+  // discarded.
+  virtual bool FilterTraceEvent(const TraceEvent& trace_event) const = 0;
+
+  // Notifies the end of a duration event when the RAII macro goes out of scope.
+  virtual void EndEvent(const char* category_name,
+                        const char* event_name) const;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TraceEventFilter);
+};
+
+}  // namespace trace_event
+}  // namespace base
+
+#endif  // BASE_TRACE_EVENT_TRACE_EVENT_FILTER_H_
diff --git a/base/trace_event/trace_event_filter_test_utils.cc b/base/trace_event/trace_event_filter_test_utils.cc
new file mode 100644
index 0000000..85b4cfa
--- /dev/null
+++ b/base/trace_event/trace_event_filter_test_utils.cc
@@ -0,0 +1,61 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/trace_event/trace_event_filter_test_utils.h"
+
+#include "base/logging.h"
+
+namespace base {
+namespace trace_event {
+
+namespace {
+TestEventFilter::HitsCounter* g_hits_counter;
+}  // namespace;
+
+// static
+const char TestEventFilter::kName[] = "testing_predicate";
+bool TestEventFilter::filter_return_value_;
+
+// static
+std::unique_ptr<TraceEventFilter> TestEventFilter::Factory(
+    const std::string& predicate_name) {
+  std::unique_ptr<TraceEventFilter> res;
+  if (predicate_name == kName)
+    res.reset(new TestEventFilter());
+  return res;
+}
+
+TestEventFilter::TestEventFilter() = default;
+TestEventFilter::~TestEventFilter() = default;
+
+bool TestEventFilter::FilterTraceEvent(const TraceEvent& trace_event) const {
+  if (g_hits_counter)
+    g_hits_counter->filter_trace_event_hit_count++;
+  return filter_return_value_;
+}
+
+void TestEventFilter::EndEvent(const char* category_name,
+                               const char* name) const {
+  if (g_hits_counter)
+    g_hits_counter->end_event_hit_count++;
+}
+
+TestEventFilter::HitsCounter::HitsCounter() {
+  Reset();
+  DCHECK(!g_hits_counter);
+  g_hits_counter = this;
+}
+
+TestEventFilter::HitsCounter::~HitsCounter() {
+  DCHECK(g_hits_counter);
+  g_hits_counter = nullptr;
+}
+
+void TestEventFilter::HitsCounter::Reset() {
+  filter_trace_event_hit_count = 0;
+  end_event_hit_count = 0;
+}
+
+}  // namespace trace_event
+}  // namespace base
diff --git a/base/trace_event/trace_event_filter_test_utils.h b/base/trace_event/trace_event_filter_test_utils.h
new file mode 100644
index 0000000..419068b
--- /dev/null
+++ b/base/trace_event/trace_event_filter_test_utils.h
@@ -0,0 +1,53 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TRACE_EVENT_TRACE_EVENT_FILTER_TEST_UTILS_H_
+#define BASE_TRACE_EVENT_TRACE_EVENT_FILTER_TEST_UTILS_H_
+
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+#include "base/trace_event/trace_event_filter.h"
+
+namespace base {
+namespace trace_event {
+
+class TestEventFilter : public TraceEventFilter {
+ public:
+  struct HitsCounter {
+    HitsCounter();
+    ~HitsCounter();
+    void Reset();
+    size_t filter_trace_event_hit_count;
+    size_t end_event_hit_count;
+  };
+
+  static const char kName[];
+
+  // Factory method for TraceLog::SetFilterFactoryForTesting().
+  static std::unique_ptr<TraceEventFilter> Factory(
+      const std::string& predicate_name);
+
+  TestEventFilter();
+  ~TestEventFilter() override;
+
+  // TraceEventFilter implementation.
+  bool FilterTraceEvent(const TraceEvent& trace_event) const override;
+  void EndEvent(const char* category_name, const char* name) const override;
+
+  static void set_filter_return_value(bool value) {
+    filter_return_value_ = value;
+  }
+
+ private:
+  static bool filter_return_value_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestEventFilter);
+};
+
+}  // namespace trace_event
+}  // namespace base
+
+#endif  // BASE_TRACE_EVENT_TRACE_EVENT_FILTER_TEST_UTILS_H_
diff --git a/base/trace_event/trace_event_impl.cc b/base/trace_event/trace_event_impl.cc
new file mode 100644
index 0000000..c72e1fc
--- /dev/null
+++ b/base/trace_event/trace_event_impl.cc
@@ -0,0 +1,489 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/trace_event/trace_event_impl.h"
+
+#include <stddef.h>
+
+#include "base/format_macros.h"
+#include "base/json/string_escape.h"
+#include "base/memory/ptr_util.h"
+#include "base/process/process_handle.h"
+#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/trace_event/trace_event.h"
+#include "base/trace_event/trace_event_argument.h"
+#include "base/trace_event/trace_log.h"
+
+namespace base {
+namespace trace_event {
+
+namespace {
+
+size_t GetAllocLength(const char* str) { return str ? strlen(str) + 1 : 0; }
+
+// Copies |*member| into |*buffer|, sets |*member| to point to this new
+// location, and then advances |*buffer| by the amount written.
+void CopyTraceEventParameter(char** buffer,
+                             const char** member,
+                             const char* end) {
+  if (*member) {
+    size_t written = strlcpy(*buffer, *member, end - *buffer) + 1;
+    DCHECK_LE(static_cast<int>(written), end - *buffer);
+    *member = *buffer;
+    *buffer += written;
+  }
+}
+
+}  // namespace
+
+TraceEvent::TraceEvent()
+    : duration_(TimeDelta::FromInternalValue(-1)),
+      scope_(trace_event_internal::kGlobalScope),
+      id_(0u),
+      category_group_enabled_(nullptr),
+      name_(nullptr),
+      thread_id_(0),
+      flags_(0),
+      phase_(TRACE_EVENT_PHASE_BEGIN) {
+  for (int i = 0; i < kTraceMaxNumArgs; ++i)
+    arg_names_[i] = nullptr;
+  memset(arg_values_, 0, sizeof(arg_values_));
+}
+
+TraceEvent::~TraceEvent() = default;
+
+void TraceEvent::MoveFrom(std::unique_ptr<TraceEvent> other) {
+  timestamp_ = other->timestamp_;
+  thread_timestamp_ = other->thread_timestamp_;
+  duration_ = other->duration_;
+  scope_ = other->scope_;
+  id_ = other->id_;
+  category_group_enabled_ = other->category_group_enabled_;
+  name_ = other->name_;
+  if (other->flags_ & TRACE_EVENT_FLAG_HAS_PROCESS_ID)
+    process_id_ = other->process_id_;
+  else
+    thread_id_ = other->thread_id_;
+  phase_ = other->phase_;
+  flags_ = other->flags_;
+  parameter_copy_storage_ = std::move(other->parameter_copy_storage_);
+
+  for (int i = 0; i < kTraceMaxNumArgs; ++i) {
+    arg_names_[i] = other->arg_names_[i];
+    arg_types_[i] = other->arg_types_[i];
+    arg_values_[i] = other->arg_values_[i];
+    convertable_values_[i] = std::move(other->convertable_values_[i]);
+  }
+}
+
+void TraceEvent::Initialize(
+    int thread_id,
+    TimeTicks timestamp,
+    ThreadTicks thread_timestamp,
+    char phase,
+    const unsigned char* category_group_enabled,
+    const char* name,
+    const char* scope,
+    unsigned long long id,
+    unsigned long long bind_id,
+    int num_args,
+    const char* const* arg_names,
+    const unsigned char* arg_types,
+    const unsigned long long* arg_values,
+    std::unique_ptr<ConvertableToTraceFormat>* convertable_values,
+    unsigned int flags) {
+  timestamp_ = timestamp;
+  thread_timestamp_ = thread_timestamp;
+  duration_ = TimeDelta::FromInternalValue(-1);
+  scope_ = scope;
+  id_ = id;
+  category_group_enabled_ = category_group_enabled;
+  name_ = name;
+  thread_id_ = thread_id;
+  phase_ = phase;
+  flags_ = flags;
+  bind_id_ = bind_id;
+
+  // Clamp num_args since it may have been set by a third_party library.
+  num_args = (num_args > kTraceMaxNumArgs) ? kTraceMaxNumArgs : num_args;
+  int i = 0;
+  for (; i < num_args; ++i) {
+    arg_names_[i] = arg_names[i];
+    arg_types_[i] = arg_types[i];
+
+    if (arg_types[i] == TRACE_VALUE_TYPE_CONVERTABLE) {
+      convertable_values_[i] = std::move(convertable_values[i]);
+    } else {
+      arg_values_[i].as_uint = arg_values[i];
+      convertable_values_[i].reset();
+    }
+  }
+  for (; i < kTraceMaxNumArgs; ++i) {
+    arg_names_[i] = nullptr;
+    arg_values_[i].as_uint = 0u;
+    convertable_values_[i].reset();
+    arg_types_[i] = TRACE_VALUE_TYPE_UINT;
+  }
+
+  bool copy = !!(flags & TRACE_EVENT_FLAG_COPY);
+  size_t alloc_size = 0;
+  if (copy) {
+    alloc_size += GetAllocLength(name) + GetAllocLength(scope);
+    for (i = 0; i < num_args; ++i) {
+      alloc_size += GetAllocLength(arg_names_[i]);
+      if (arg_types_[i] == TRACE_VALUE_TYPE_STRING)
+        arg_types_[i] = TRACE_VALUE_TYPE_COPY_STRING;
+    }
+  }
+
+  bool arg_is_copy[kTraceMaxNumArgs];
+  for (i = 0; i < num_args; ++i) {
+    // No copying of convertable types, we retain ownership.
+    if (arg_types_[i] == TRACE_VALUE_TYPE_CONVERTABLE)
+      continue;
+
+    // We only take a copy of arg_vals if they are of type COPY_STRING.
+    arg_is_copy[i] = (arg_types_[i] == TRACE_VALUE_TYPE_COPY_STRING);
+    if (arg_is_copy[i])
+      alloc_size += GetAllocLength(arg_values_[i].as_string);
+  }
+
+  if (alloc_size) {
+    parameter_copy_storage_.reset(new std::string);
+    parameter_copy_storage_->resize(alloc_size);
+    char* ptr = base::data(*parameter_copy_storage_);
+    const char* end = ptr + alloc_size;
+    if (copy) {
+      CopyTraceEventParameter(&ptr, &name_, end);
+      CopyTraceEventParameter(&ptr, &scope_, end);
+      for (i = 0; i < num_args; ++i) {
+        CopyTraceEventParameter(&ptr, &arg_names_[i], end);
+      }
+    }
+    for (i = 0; i < num_args; ++i) {
+      if (arg_types_[i] == TRACE_VALUE_TYPE_CONVERTABLE)
+        continue;
+      if (arg_is_copy[i])
+        CopyTraceEventParameter(&ptr, &arg_values_[i].as_string, end);
+    }
+    DCHECK_EQ(end, ptr) << "Overrun by " << ptr - end;
+  }
+}
+
+void TraceEvent::Reset() {
+  // Only reset fields that won't be initialized in Initialize(), or that may
+  // hold references to other objects.
+  duration_ = TimeDelta::FromInternalValue(-1);
+  parameter_copy_storage_.reset();
+  for (int i = 0; i < kTraceMaxNumArgs; ++i)
+    convertable_values_[i].reset();
+}
+
+void TraceEvent::UpdateDuration(const TimeTicks& now,
+                                const ThreadTicks& thread_now) {
+  DCHECK_EQ(duration_.ToInternalValue(), -1);
+  duration_ = now - timestamp_;
+
+  // |thread_timestamp_| can be empty if the thread ticks clock wasn't
+  // initialized when it was recorded.
+  if (thread_timestamp_ != ThreadTicks())
+    thread_duration_ = thread_now - thread_timestamp_;
+}
+
+void TraceEvent::EstimateTraceMemoryOverhead(
+    TraceEventMemoryOverhead* overhead) {
+  overhead->Add(TraceEventMemoryOverhead::kTraceEvent, sizeof(*this));
+
+  if (parameter_copy_storage_)
+    overhead->AddString(*parameter_copy_storage_);
+
+  for (size_t i = 0; i < kTraceMaxNumArgs; ++i) {
+    if (arg_types_[i] == TRACE_VALUE_TYPE_CONVERTABLE)
+      convertable_values_[i]->EstimateTraceMemoryOverhead(overhead);
+  }
+}
+
+// static
+void TraceEvent::AppendValueAsJSON(unsigned char type,
+                                   TraceEvent::TraceValue value,
+                                   std::string* out) {
+  switch (type) {
+    case TRACE_VALUE_TYPE_BOOL:
+      *out += value.as_bool ? "true" : "false";
+      break;
+    case TRACE_VALUE_TYPE_UINT:
+      StringAppendF(out, "%" PRIu64, static_cast<uint64_t>(value.as_uint));
+      break;
+    case TRACE_VALUE_TYPE_INT:
+      StringAppendF(out, "%" PRId64, static_cast<int64_t>(value.as_int));
+      break;
+    case TRACE_VALUE_TYPE_DOUBLE: {
+      // FIXME: base/json/json_writer.cc is using the same code,
+      //        should be made into a common method.
+      std::string real;
+      double val = value.as_double;
+      if (std::isfinite(val)) {
+        real = NumberToString(val);
+        // 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");
+        }
+        // The JSON spec requires that non-integer values in the range (-1,1)
+        // have a zero before the decimal point - ".52" is not valid, "0.52" is.
+        if (real[0] == '.') {
+          real.insert(0, "0");
+        } else if (real.length() > 1 && real[0] == '-' && real[1] == '.') {
+          // "-.1" bad "-0.1" good
+          real.insert(1, "0");
+        }
+      } else if (std::isnan(val)){
+        // The JSON spec doesn't allow NaN and Infinity (since these are
+        // objects in EcmaScript).  Use strings instead.
+        real = "\"NaN\"";
+      } else if (val < 0) {
+        real = "\"-Infinity\"";
+      } else {
+        real = "\"Infinity\"";
+      }
+      StringAppendF(out, "%s", real.c_str());
+      break;
+    }
+    case TRACE_VALUE_TYPE_POINTER:
+      // JSON only supports double and int numbers.
+      // So as not to lose bits from a 64-bit pointer, output as a hex string.
+      StringAppendF(
+          out, "\"0x%" PRIx64 "\"",
+          static_cast<uint64_t>(reinterpret_cast<uintptr_t>(value.as_pointer)));
+      break;
+    case TRACE_VALUE_TYPE_STRING:
+    case TRACE_VALUE_TYPE_COPY_STRING:
+      EscapeJSONString(value.as_string ? value.as_string : "NULL", true, out);
+      break;
+    default:
+      NOTREACHED() << "Don't know how to print this value";
+      break;
+  }
+}
+
+void TraceEvent::AppendAsJSON(
+    std::string* out,
+    const ArgumentFilterPredicate& argument_filter_predicate) const {
+  int64_t time_int64 = timestamp_.ToInternalValue();
+  int process_id;
+  int thread_id;
+  if ((flags_ & TRACE_EVENT_FLAG_HAS_PROCESS_ID) &&
+      process_id_ != kNullProcessId) {
+    process_id = process_id_;
+    thread_id = -1;
+  } else {
+    process_id = TraceLog::GetInstance()->process_id();
+    thread_id = thread_id_;
+  }
+  const char* category_group_name =
+      TraceLog::GetCategoryGroupName(category_group_enabled_);
+
+  // Category group checked at category creation time.
+  DCHECK(!strchr(name_, '"'));
+  StringAppendF(out, "{\"pid\":%i,\"tid\":%i,\"ts\":%" PRId64
+                     ",\"ph\":\"%c\",\"cat\":\"%s\",\"name\":",
+                process_id, thread_id, time_int64, phase_, category_group_name);
+  EscapeJSONString(name_, true, out);
+  *out += ",\"args\":";
+
+  // Output argument names and values, stop at first NULL argument name.
+  // TODO(oysteine): The dual predicates here is a bit ugly; if the filtering
+  // capabilities need to grow even more precise we should rethink this
+  // approach
+  ArgumentNameFilterPredicate argument_name_filter_predicate;
+  bool strip_args =
+      arg_names_[0] && !argument_filter_predicate.is_null() &&
+      !argument_filter_predicate.Run(category_group_name, name_,
+                                     &argument_name_filter_predicate);
+
+  if (strip_args) {
+    *out += "\"__stripped__\"";
+  } else {
+    *out += "{";
+
+    for (int i = 0; i < kTraceMaxNumArgs && arg_names_[i]; ++i) {
+      if (i > 0)
+        *out += ",";
+      *out += "\"";
+      *out += arg_names_[i];
+      *out += "\":";
+
+      if (argument_name_filter_predicate.is_null() ||
+          argument_name_filter_predicate.Run(arg_names_[i])) {
+        if (arg_types_[i] == TRACE_VALUE_TYPE_CONVERTABLE)
+          convertable_values_[i]->AppendAsTraceFormat(out);
+        else
+          AppendValueAsJSON(arg_types_[i], arg_values_[i], out);
+      } else {
+        *out += "\"__stripped__\"";
+      }
+    }
+
+    *out += "}";
+  }
+
+  if (phase_ == TRACE_EVENT_PHASE_COMPLETE) {
+    int64_t duration = duration_.ToInternalValue();
+    if (duration != -1)
+      StringAppendF(out, ",\"dur\":%" PRId64, duration);
+    if (!thread_timestamp_.is_null()) {
+      int64_t thread_duration = thread_duration_.ToInternalValue();
+      if (thread_duration != -1)
+        StringAppendF(out, ",\"tdur\":%" PRId64, thread_duration);
+    }
+  }
+
+  // Output tts if thread_timestamp is valid.
+  if (!thread_timestamp_.is_null()) {
+    int64_t thread_time_int64 = thread_timestamp_.ToInternalValue();
+    StringAppendF(out, ",\"tts\":%" PRId64, thread_time_int64);
+  }
+
+  // Output async tts marker field if flag is set.
+  if (flags_ & TRACE_EVENT_FLAG_ASYNC_TTS) {
+    StringAppendF(out, ", \"use_async_tts\":1");
+  }
+
+  // If id_ is set, print it out as a hex string so we don't loose any
+  // bits (it might be a 64-bit pointer).
+  unsigned int id_flags_ = flags_ & (TRACE_EVENT_FLAG_HAS_ID |
+                                     TRACE_EVENT_FLAG_HAS_LOCAL_ID |
+                                     TRACE_EVENT_FLAG_HAS_GLOBAL_ID);
+  if (id_flags_) {
+    if (scope_ != trace_event_internal::kGlobalScope)
+      StringAppendF(out, ",\"scope\":\"%s\"", scope_);
+
+    switch (id_flags_) {
+      case TRACE_EVENT_FLAG_HAS_ID:
+        StringAppendF(out, ",\"id\":\"0x%" PRIx64 "\"",
+                      static_cast<uint64_t>(id_));
+        break;
+
+      case TRACE_EVENT_FLAG_HAS_LOCAL_ID:
+        StringAppendF(out, ",\"id2\":{\"local\":\"0x%" PRIx64 "\"}",
+                      static_cast<uint64_t>(id_));
+        break;
+
+      case TRACE_EVENT_FLAG_HAS_GLOBAL_ID:
+        StringAppendF(out, ",\"id2\":{\"global\":\"0x%" PRIx64 "\"}",
+                      static_cast<uint64_t>(id_));
+        break;
+
+      default:
+        NOTREACHED() << "More than one of the ID flags are set";
+        break;
+    }
+  }
+
+  if (flags_ & TRACE_EVENT_FLAG_BIND_TO_ENCLOSING)
+    StringAppendF(out, ",\"bp\":\"e\"");
+
+  if ((flags_ & TRACE_EVENT_FLAG_FLOW_OUT) ||
+      (flags_ & TRACE_EVENT_FLAG_FLOW_IN)) {
+    StringAppendF(out, ",\"bind_id\":\"0x%" PRIx64 "\"",
+                  static_cast<uint64_t>(bind_id_));
+  }
+  if (flags_ & TRACE_EVENT_FLAG_FLOW_IN)
+    StringAppendF(out, ",\"flow_in\":true");
+  if (flags_ & TRACE_EVENT_FLAG_FLOW_OUT)
+    StringAppendF(out, ",\"flow_out\":true");
+
+  // Instant events also output their scope.
+  if (phase_ == TRACE_EVENT_PHASE_INSTANT) {
+    char scope = '?';
+    switch (flags_ & TRACE_EVENT_FLAG_SCOPE_MASK) {
+      case TRACE_EVENT_SCOPE_GLOBAL:
+        scope = TRACE_EVENT_SCOPE_NAME_GLOBAL;
+        break;
+
+      case TRACE_EVENT_SCOPE_PROCESS:
+        scope = TRACE_EVENT_SCOPE_NAME_PROCESS;
+        break;
+
+      case TRACE_EVENT_SCOPE_THREAD:
+        scope = TRACE_EVENT_SCOPE_NAME_THREAD;
+        break;
+    }
+    StringAppendF(out, ",\"s\":\"%c\"", scope);
+  }
+
+  *out += "}";
+}
+
+void TraceEvent::AppendPrettyPrinted(std::ostringstream* out) const {
+  *out << name_ << "[";
+  *out << TraceLog::GetCategoryGroupName(category_group_enabled_);
+  *out << "]";
+  if (arg_names_[0]) {
+    *out << ", {";
+    for (int i = 0; i < kTraceMaxNumArgs && arg_names_[i]; ++i) {
+      if (i > 0)
+        *out << ", ";
+      *out << arg_names_[i] << ":";
+      std::string value_as_text;
+
+      if (arg_types_[i] == TRACE_VALUE_TYPE_CONVERTABLE)
+        convertable_values_[i]->AppendAsTraceFormat(&value_as_text);
+      else
+        AppendValueAsJSON(arg_types_[i], arg_values_[i], &value_as_text);
+
+      *out << value_as_text;
+    }
+    *out << "}";
+  }
+}
+
+}  // namespace trace_event
+}  // namespace base
+
+namespace trace_event_internal {
+
+std::unique_ptr<base::trace_event::ConvertableToTraceFormat>
+TraceID::AsConvertableToTraceFormat() const {
+  auto value = std::make_unique<base::trace_event::TracedValue>();
+
+  if (scope_ != kGlobalScope)
+    value->SetString("scope", scope_);
+
+  const char* id_field_name = "id";
+  if (id_flags_ == TRACE_EVENT_FLAG_HAS_GLOBAL_ID) {
+    id_field_name = "global";
+    value->BeginDictionary("id2");
+  } else if (id_flags_ == TRACE_EVENT_FLAG_HAS_LOCAL_ID) {
+    id_field_name = "local";
+    value->BeginDictionary("id2");
+  } else if (id_flags_ != TRACE_EVENT_FLAG_HAS_ID) {
+    NOTREACHED() << "Unrecognized ID flag";
+  }
+
+  if (has_prefix_) {
+    value->SetString(id_field_name,
+                     base::StringPrintf("0x%" PRIx64 "/0x%" PRIx64,
+                                        static_cast<uint64_t>(prefix_),
+                                        static_cast<uint64_t>(raw_id_)));
+  } else {
+    value->SetString(
+        id_field_name,
+        base::StringPrintf("0x%" PRIx64, static_cast<uint64_t>(raw_id_)));
+  }
+
+  if (id_flags_ != TRACE_EVENT_FLAG_HAS_ID)
+    value->EndDictionary();
+
+  return std::move(value);
+}
+
+}  // namespace trace_event_internal
diff --git a/base/trace_event/trace_event_impl.h b/base/trace_event/trace_event_impl.h
new file mode 100644
index 0000000..4b4b88f
--- /dev/null
+++ b/base/trace_event/trace_event_impl.h
@@ -0,0 +1,191 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+
+#ifndef BASE_TRACE_EVENT_TRACE_EVENT_IMPL_H_
+#define BASE_TRACE_EVENT_TRACE_EVENT_IMPL_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/atomicops.h"
+#include "base/base_export.h"
+#include "base/callback.h"
+#include "base/containers/hash_tables.h"
+#include "base/macros.h"
+#include "base/observer_list.h"
+#include "base/single_thread_task_runner.h"
+#include "base/strings/string_util.h"
+#include "base/synchronization/condition_variable.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/thread_local.h"
+#include "base/trace_event/trace_event_memory_overhead.h"
+#include "build/build_config.h"
+
+namespace base {
+namespace trace_event {
+
+typedef base::Callback<bool(const char* arg_name)> ArgumentNameFilterPredicate;
+
+typedef base::Callback<bool(const char* category_group_name,
+                            const char* event_name,
+                            ArgumentNameFilterPredicate*)>
+    ArgumentFilterPredicate;
+
+// For any argument of type TRACE_VALUE_TYPE_CONVERTABLE the provided
+// class must implement this interface.
+class BASE_EXPORT ConvertableToTraceFormat {
+ public:
+  ConvertableToTraceFormat() = default;
+  virtual ~ConvertableToTraceFormat() = default;
+
+  // Append the class info to the provided |out| string. The appended
+  // data must be a valid JSON object. Strings must be properly quoted, and
+  // escaped. There is no processing applied to the content after it is
+  // appended.
+  virtual void AppendAsTraceFormat(std::string* out) const = 0;
+
+  virtual void EstimateTraceMemoryOverhead(TraceEventMemoryOverhead* overhead);
+
+  std::string ToString() const {
+    std::string result;
+    AppendAsTraceFormat(&result);
+    return result;
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ConvertableToTraceFormat);
+};
+
+const int kTraceMaxNumArgs = 2;
+
+struct TraceEventHandle {
+  uint32_t chunk_seq;
+  // These numbers of bits must be kept consistent with
+  // TraceBufferChunk::kMaxTrunkIndex and
+  // TraceBufferChunk::kTraceBufferChunkSize (in trace_buffer.h).
+  unsigned chunk_index : 26;
+  unsigned event_index : 6;
+};
+
+class BASE_EXPORT TraceEvent {
+ public:
+  union TraceValue {
+    bool as_bool;
+    unsigned long long as_uint;
+    long long as_int;
+    double as_double;
+    const void* as_pointer;
+    const char* as_string;
+  };
+
+  TraceEvent();
+  ~TraceEvent();
+
+  void MoveFrom(std::unique_ptr<TraceEvent> other);
+
+  void Initialize(int thread_id,
+                  TimeTicks timestamp,
+                  ThreadTicks thread_timestamp,
+                  char phase,
+                  const unsigned char* category_group_enabled,
+                  const char* name,
+                  const char* scope,
+                  unsigned long long id,
+                  unsigned long long bind_id,
+                  int num_args,
+                  const char* const* arg_names,
+                  const unsigned char* arg_types,
+                  const unsigned long long* arg_values,
+                  std::unique_ptr<ConvertableToTraceFormat>* convertable_values,
+                  unsigned int flags);
+
+  void Reset();
+
+  void UpdateDuration(const TimeTicks& now, const ThreadTicks& thread_now);
+
+  void EstimateTraceMemoryOverhead(TraceEventMemoryOverhead* overhead);
+
+  // Serialize event data to JSON
+  void AppendAsJSON(
+      std::string* out,
+      const ArgumentFilterPredicate& argument_filter_predicate) const;
+  void AppendPrettyPrinted(std::ostringstream* out) const;
+
+  static void AppendValueAsJSON(unsigned char type,
+                                TraceValue value,
+                                std::string* out);
+
+  TimeTicks timestamp() const { return timestamp_; }
+  ThreadTicks thread_timestamp() const { return thread_timestamp_; }
+  char phase() const { return phase_; }
+  int thread_id() const { return thread_id_; }
+  TimeDelta duration() const { return duration_; }
+  TimeDelta thread_duration() const { return thread_duration_; }
+  const char* scope() const { return scope_; }
+  unsigned long long id() const { return id_; }
+  unsigned int flags() const { return flags_; }
+  unsigned long long bind_id() const { return bind_id_; }
+  // Exposed for unittesting:
+
+  const std::string* parameter_copy_storage() const {
+    return parameter_copy_storage_.get();
+  }
+
+  const unsigned char* category_group_enabled() const {
+    return category_group_enabled_;
+  }
+
+  const char* name() const { return name_; }
+
+  unsigned char arg_type(size_t index) const { return arg_types_[index]; }
+  const char* arg_name(size_t index) const { return arg_names_[index]; }
+  const TraceValue& arg_value(size_t index) const { return arg_values_[index]; }
+
+  const ConvertableToTraceFormat* arg_convertible_value(size_t index) const {
+    return convertable_values_[index].get();
+  }
+
+#if defined(OS_ANDROID)
+  void SendToATrace();
+#endif
+
+ private:
+  // Note: these are ordered by size (largest first) for optimal packing.
+  TimeTicks timestamp_;
+  ThreadTicks thread_timestamp_;
+  TimeDelta duration_;
+  TimeDelta thread_duration_;
+  // scope_ and id_ can be used to store phase-specific data.
+  const char* scope_;
+  unsigned long long id_;
+  TraceValue arg_values_[kTraceMaxNumArgs];
+  const char* arg_names_[kTraceMaxNumArgs];
+  std::unique_ptr<ConvertableToTraceFormat>
+      convertable_values_[kTraceMaxNumArgs];
+  const unsigned char* category_group_enabled_;
+  const char* name_;
+  std::unique_ptr<std::string> parameter_copy_storage_;
+  // Depending on TRACE_EVENT_FLAG_HAS_PROCESS_ID the event will have either:
+  //  tid: thread_id_, pid: current_process_id (default case).
+  //  tid: -1, pid: process_id_ (when flags_ & TRACE_EVENT_FLAG_HAS_PROCESS_ID).
+  union {
+    int thread_id_;
+    int process_id_;
+  };
+  unsigned int flags_;
+  unsigned long long bind_id_;
+  unsigned char arg_types_[kTraceMaxNumArgs];
+  char phase_;
+
+  DISALLOW_COPY_AND_ASSIGN(TraceEvent);
+};
+
+}  // namespace trace_event
+}  // namespace base
+
+#endif  // BASE_TRACE_EVENT_TRACE_EVENT_IMPL_H_
diff --git a/base/trace_event/trace_event_memory_overhead.cc b/base/trace_event/trace_event_memory_overhead.cc
new file mode 100644
index 0000000..dd7b302
--- /dev/null
+++ b/base/trace_event/trace_event_memory_overhead.cc
@@ -0,0 +1,179 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/trace_event/trace_event_memory_overhead.h"
+
+#include <algorithm>
+
+#include "base/bits.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/strings/stringprintf.h"
+#include "base/trace_event/memory_allocator_dump.h"
+#include "base/trace_event/memory_usage_estimator.h"
+#include "base/trace_event/process_memory_dump.h"
+#include "base/values.h"
+
+namespace base {
+namespace trace_event {
+
+namespace {
+
+const char* ObjectTypeToString(TraceEventMemoryOverhead::ObjectType type) {
+  switch (type) {
+    case TraceEventMemoryOverhead::kOther:
+      return "(Other)";
+    case TraceEventMemoryOverhead::kTraceBuffer:
+      return "TraceBuffer";
+    case TraceEventMemoryOverhead::kTraceBufferChunk:
+      return "TraceBufferChunk";
+    case TraceEventMemoryOverhead::kTraceEvent:
+      return "TraceEvent";
+    case TraceEventMemoryOverhead::kUnusedTraceEvent:
+      return "TraceEvent(Unused)";
+    case TraceEventMemoryOverhead::kTracedValue:
+      return "TracedValue";
+    case TraceEventMemoryOverhead::kConvertableToTraceFormat:
+      return "ConvertableToTraceFormat";
+    case TraceEventMemoryOverhead::kHeapProfilerAllocationRegister:
+      return "AllocationRegister";
+    case TraceEventMemoryOverhead::kHeapProfilerTypeNameDeduplicator:
+      return "TypeNameDeduplicator";
+    case TraceEventMemoryOverhead::kHeapProfilerStackFrameDeduplicator:
+      return "StackFrameDeduplicator";
+    case TraceEventMemoryOverhead::kStdString:
+      return "std::string";
+    case TraceEventMemoryOverhead::kBaseValue:
+      return "base::Value";
+    case TraceEventMemoryOverhead::kTraceEventMemoryOverhead:
+      return "TraceEventMemoryOverhead";
+    case TraceEventMemoryOverhead::kFrameMetrics:
+      return "FrameMetrics";
+    case TraceEventMemoryOverhead::kLast:
+      NOTREACHED();
+  }
+  NOTREACHED();
+  return "BUG";
+}
+
+}  // namespace
+
+TraceEventMemoryOverhead::TraceEventMemoryOverhead() : allocated_objects_() {}
+
+TraceEventMemoryOverhead::~TraceEventMemoryOverhead() = default;
+
+void TraceEventMemoryOverhead::AddInternal(ObjectType object_type,
+                                           size_t count,
+                                           size_t allocated_size_in_bytes,
+                                           size_t resident_size_in_bytes) {
+  ObjectCountAndSize& count_and_size =
+      allocated_objects_[static_cast<uint32_t>(object_type)];
+  count_and_size.count += count;
+  count_and_size.allocated_size_in_bytes += allocated_size_in_bytes;
+  count_and_size.resident_size_in_bytes += resident_size_in_bytes;
+}
+
+void TraceEventMemoryOverhead::Add(ObjectType object_type,
+                                   size_t allocated_size_in_bytes) {
+  Add(object_type, allocated_size_in_bytes, allocated_size_in_bytes);
+}
+
+void TraceEventMemoryOverhead::Add(ObjectType object_type,
+                                   size_t allocated_size_in_bytes,
+                                   size_t resident_size_in_bytes) {
+  AddInternal(object_type, 1, allocated_size_in_bytes, resident_size_in_bytes);
+}
+
+void TraceEventMemoryOverhead::AddString(const std::string& str) {
+  Add(kStdString, EstimateMemoryUsage(str));
+}
+
+void TraceEventMemoryOverhead::AddRefCountedString(
+    const RefCountedString& str) {
+  Add(kOther, sizeof(RefCountedString));
+  AddString(str.data());
+}
+
+void TraceEventMemoryOverhead::AddValue(const Value& value) {
+  switch (value.type()) {
+    case Value::Type::NONE:
+    case Value::Type::BOOLEAN:
+    case Value::Type::INTEGER:
+    case Value::Type::DOUBLE:
+      Add(kBaseValue, sizeof(Value));
+      break;
+
+    case Value::Type::STRING: {
+      const Value* string_value = nullptr;
+      value.GetAsString(&string_value);
+      Add(kBaseValue, sizeof(Value));
+      AddString(string_value->GetString());
+    } break;
+
+    case Value::Type::BINARY: {
+      Add(kBaseValue, sizeof(Value) + value.GetBlob().size());
+    } break;
+
+    case Value::Type::DICTIONARY: {
+      const DictionaryValue* dictionary_value = nullptr;
+      value.GetAsDictionary(&dictionary_value);
+      Add(kBaseValue, sizeof(DictionaryValue));
+      for (DictionaryValue::Iterator it(*dictionary_value); !it.IsAtEnd();
+           it.Advance()) {
+        AddString(it.key());
+        AddValue(it.value());
+      }
+    } break;
+
+    case Value::Type::LIST: {
+      const ListValue* list_value = nullptr;
+      value.GetAsList(&list_value);
+      Add(kBaseValue, sizeof(ListValue));
+      for (const auto& v : *list_value)
+        AddValue(v);
+    } break;
+
+    default:
+      NOTREACHED();
+  }
+}
+
+void TraceEventMemoryOverhead::AddSelf() {
+  Add(kTraceEventMemoryOverhead, sizeof(*this));
+}
+
+size_t TraceEventMemoryOverhead::GetCount(ObjectType object_type) const {
+  CHECK(object_type < kLast);
+  return allocated_objects_[static_cast<uint32_t>(object_type)].count;
+}
+
+void TraceEventMemoryOverhead::Update(const TraceEventMemoryOverhead& other) {
+  for (uint32_t i = 0; i < kLast; i++) {
+    const ObjectCountAndSize& other_entry = other.allocated_objects_[i];
+    AddInternal(static_cast<ObjectType>(i), other_entry.count,
+                other_entry.allocated_size_in_bytes,
+                other_entry.resident_size_in_bytes);
+  }
+}
+
+void TraceEventMemoryOverhead::DumpInto(const char* base_name,
+                                        ProcessMemoryDump* pmd) const {
+  for (uint32_t i = 0; i < kLast; i++) {
+    const ObjectCountAndSize& count_and_size = allocated_objects_[i];
+    if (count_and_size.allocated_size_in_bytes == 0)
+      continue;
+    std::string dump_name = StringPrintf(
+        "%s/%s", base_name, ObjectTypeToString(static_cast<ObjectType>(i)));
+    MemoryAllocatorDump* mad = pmd->CreateAllocatorDump(dump_name);
+    mad->AddScalar(MemoryAllocatorDump::kNameSize,
+                   MemoryAllocatorDump::kUnitsBytes,
+                   count_and_size.allocated_size_in_bytes);
+    mad->AddScalar("resident_size", MemoryAllocatorDump::kUnitsBytes,
+                   count_and_size.resident_size_in_bytes);
+    mad->AddScalar(MemoryAllocatorDump::kNameObjectCount,
+                   MemoryAllocatorDump::kUnitsObjects, count_and_size.count);
+  }
+}
+
+}  // namespace trace_event
+}  // namespace base
diff --git a/base/trace_event/trace_event_memory_overhead.h b/base/trace_event/trace_event_memory_overhead.h
new file mode 100644
index 0000000..69468d4
--- /dev/null
+++ b/base/trace_event/trace_event_memory_overhead.h
@@ -0,0 +1,95 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TRACE_EVENT_TRACE_EVENT_MEMORY_OVERHEAD_H_
+#define BASE_TRACE_EVENT_TRACE_EVENT_MEMORY_OVERHEAD_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <unordered_map>
+
+#include "base/base_export.h"
+#include "base/macros.h"
+
+namespace base {
+
+class RefCountedString;
+class Value;
+
+namespace trace_event {
+
+class ProcessMemoryDump;
+
+// Used to estimate the memory overhead of the tracing infrastructure.
+class BASE_EXPORT TraceEventMemoryOverhead {
+ public:
+  enum ObjectType : uint32_t {
+    kOther = 0,
+    kTraceBuffer,
+    kTraceBufferChunk,
+    kTraceEvent,
+    kUnusedTraceEvent,
+    kTracedValue,
+    kConvertableToTraceFormat,
+    kHeapProfilerAllocationRegister,
+    kHeapProfilerTypeNameDeduplicator,
+    kHeapProfilerStackFrameDeduplicator,
+    kStdString,
+    kBaseValue,
+    kTraceEventMemoryOverhead,
+    kFrameMetrics,
+    kLast
+  };
+
+  TraceEventMemoryOverhead();
+  ~TraceEventMemoryOverhead();
+
+  // Use this method to account the overhead of an object for which an estimate
+  // is known for both the allocated and resident memory.
+  void Add(ObjectType object_type,
+           size_t allocated_size_in_bytes,
+           size_t resident_size_in_bytes);
+
+  // Similar to Add() above, but assumes that
+  // |resident_size_in_bytes| == |allocated_size_in_bytes|.
+  void Add(ObjectType object_type, size_t allocated_size_in_bytes);
+
+  // Specialized profiling functions for commonly used object types.
+  void AddString(const std::string& str);
+  void AddValue(const Value& value);
+  void AddRefCountedString(const RefCountedString& str);
+
+  // Call this after all the Add* methods above to account the memory used by
+  // this TraceEventMemoryOverhead instance itself.
+  void AddSelf();
+
+  // Retrieves the count, that is, the count of Add*(|object_type|, ...) calls.
+  size_t GetCount(ObjectType object_type) const;
+
+  // Adds up and merges all the values from |other| to this instance.
+  void Update(const TraceEventMemoryOverhead& other);
+
+  void DumpInto(const char* base_name, ProcessMemoryDump* pmd) const;
+
+ private:
+  struct ObjectCountAndSize {
+    size_t count;
+    size_t allocated_size_in_bytes;
+    size_t resident_size_in_bytes;
+  };
+  ObjectCountAndSize allocated_objects_[ObjectType::kLast];
+
+  void AddInternal(ObjectType object_type,
+                   size_t count,
+                   size_t allocated_size_in_bytes,
+                   size_t resident_size_in_bytes);
+
+  DISALLOW_COPY_AND_ASSIGN(TraceEventMemoryOverhead);
+};
+
+}  // namespace trace_event
+}  // namespace base
+
+#endif  // BASE_TRACE_EVENT_TRACE_EVENT_MEMORY_OVERHEAD_H_
diff --git a/base/trace_event/trace_event_system_stats_monitor.cc b/base/trace_event/trace_event_system_stats_monitor.cc
new file mode 100644
index 0000000..7e082f3
--- /dev/null
+++ b/base/trace_event/trace_event_system_stats_monitor.cc
@@ -0,0 +1,132 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/trace_event/trace_event_system_stats_monitor.h"
+
+#include <memory>
+
+#include "base/debug/leak_annotations.h"
+#include "base/json/json_writer.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/process/process_metrics.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/threading/thread_local_storage.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/trace_event/trace_event.h"
+
+namespace base {
+namespace trace_event {
+
+namespace {
+
+/////////////////////////////////////////////////////////////////////////////
+// Holds profiled system stats until the tracing system needs to serialize it.
+class SystemStatsHolder : public base::trace_event::ConvertableToTraceFormat {
+ public:
+  SystemStatsHolder() = default;
+  ~SystemStatsHolder() override = default;
+
+  // Fills system_metrics_ with profiled system memory and disk stats.
+  // Uses the previous stats to compute rates if this is not the first profile.
+  void GetSystemProfilingStats();
+
+  // base::trace_event::ConvertableToTraceFormat overrides:
+  void AppendAsTraceFormat(std::string* out) const override {
+    AppendSystemProfileAsTraceFormat(system_stats_, out);
+  }
+
+ private:
+  SystemMetrics system_stats_;
+
+  DISALLOW_COPY_AND_ASSIGN(SystemStatsHolder);
+};
+
+void SystemStatsHolder::GetSystemProfilingStats() {
+  system_stats_ = SystemMetrics::Sample();
+}
+
+}  // namespace
+
+//////////////////////////////////////////////////////////////////////////////
+
+TraceEventSystemStatsMonitor::TraceEventSystemStatsMonitor(
+    scoped_refptr<SingleThreadTaskRunner> task_runner)
+    : task_runner_(task_runner),
+      weak_factory_(this) {
+  // Force the "system_stats" category to show up in the trace viewer.
+  TraceLog::GetCategoryGroupEnabled(TRACE_DISABLED_BY_DEFAULT("system_stats"));
+
+  // Allow this to be instantiated on unsupported platforms, but don't run.
+  TraceLog::GetInstance()->AddEnabledStateObserver(this);
+}
+
+TraceEventSystemStatsMonitor::~TraceEventSystemStatsMonitor() {
+  if (dump_timer_.IsRunning())
+    StopProfiling();
+  TraceLog::GetInstance()->RemoveEnabledStateObserver(this);
+}
+
+void TraceEventSystemStatsMonitor::OnTraceLogEnabled() {
+  // Check to see if system tracing is enabled.
+  bool enabled;
+
+  TRACE_EVENT_CATEGORY_GROUP_ENABLED(TRACE_DISABLED_BY_DEFAULT(
+                                     "system_stats"), &enabled);
+  if (!enabled)
+    return;
+  task_runner_->PostTask(
+      FROM_HERE, base::BindOnce(&TraceEventSystemStatsMonitor::StartProfiling,
+                                weak_factory_.GetWeakPtr()));
+}
+
+void TraceEventSystemStatsMonitor::OnTraceLogDisabled() {
+  task_runner_->PostTask(
+      FROM_HERE, base::BindOnce(&TraceEventSystemStatsMonitor::StopProfiling,
+                                weak_factory_.GetWeakPtr()));
+}
+
+void TraceEventSystemStatsMonitor::StartProfiling() {
+  // Watch for the tracing framework sending enabling more than once.
+  if (dump_timer_.IsRunning())
+    return;
+
+  dump_timer_.Start(FROM_HERE,
+                    TimeDelta::FromMilliseconds(TraceEventSystemStatsMonitor::
+                                                kSamplingIntervalMilliseconds),
+                    base::Bind(&TraceEventSystemStatsMonitor::
+                               DumpSystemStats,
+                               weak_factory_.GetWeakPtr()));
+}
+
+// If system tracing is enabled, dumps a profile to the tracing system.
+void TraceEventSystemStatsMonitor::DumpSystemStats() {
+  std::unique_ptr<SystemStatsHolder> dump_holder(new SystemStatsHolder());
+  dump_holder->GetSystemProfilingStats();
+
+  TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(
+      TRACE_DISABLED_BY_DEFAULT("system_stats"),
+      "base::TraceEventSystemStatsMonitor::SystemStats", this,
+      std::move(dump_holder));
+}
+
+void TraceEventSystemStatsMonitor::StopProfiling() {
+  dump_timer_.Stop();
+}
+
+bool TraceEventSystemStatsMonitor::IsTimerRunningForTest() const {
+  return dump_timer_.IsRunning();
+}
+
+void AppendSystemProfileAsTraceFormat(const SystemMetrics& system_metrics,
+                                      std::string* output) {
+  std::string tmp;
+  base::JSONWriter::Write(*system_metrics.ToValue(), &tmp);
+  *output += tmp;
+}
+
+}  // namespace trace_event
+}  // namespace base
diff --git a/base/trace_event/trace_event_system_stats_monitor.h b/base/trace_event/trace_event_system_stats_monitor.h
new file mode 100644
index 0000000..14aa568
--- /dev/null
+++ b/base/trace_event/trace_event_system_stats_monitor.h
@@ -0,0 +1,76 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TRACE_EVENT_TRACE_EVENT_SYSTEM_STATS_MONITOR_H_
+#define BASE_TRACE_EVENT_TRACE_EVENT_SYSTEM_STATS_MONITOR_H_
+
+#include "base/base_export.h"
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/process/process_metrics.h"
+#include "base/timer/timer.h"
+#include "base/trace_event/trace_log.h"
+
+namespace base {
+
+class SingleThreadTaskRunner;
+
+namespace trace_event {
+
+// Watches for chrome://tracing to be enabled or disabled. When tracing is
+// enabled, also enables system events profiling. This class is the preferred
+// way to turn system tracing on and off.
+class BASE_EXPORT TraceEventSystemStatsMonitor
+    : public TraceLog::EnabledStateObserver {
+ public:
+  // Length of time interval between stat profiles.
+  static const int kSamplingIntervalMilliseconds = 2000;
+
+  // |task_runner| must be the primary thread for the client
+  // process, e.g. the UI thread in a browser.
+  explicit TraceEventSystemStatsMonitor(
+      scoped_refptr<SingleThreadTaskRunner> task_runner);
+
+  ~TraceEventSystemStatsMonitor() override;
+
+  // base::trace_event::TraceLog::EnabledStateChangedObserver overrides:
+  void OnTraceLogEnabled() override;
+  void OnTraceLogDisabled() override;
+
+  // Retrieves system profiling at the current time.
+  void DumpSystemStats();
+
+ private:
+  FRIEND_TEST_ALL_PREFIXES(TraceSystemStatsMonitorTest,
+                           TraceEventSystemStatsMonitor);
+
+  bool IsTimerRunningForTest() const;
+
+  void StartProfiling();
+
+  void StopProfiling();
+
+  // Ensures the observer starts and stops tracing on the primary thread.
+  scoped_refptr<SingleThreadTaskRunner> task_runner_;
+
+  // Timer to schedule system profile dumps.
+  RepeatingTimer dump_timer_;
+
+  WeakPtrFactory<TraceEventSystemStatsMonitor> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(TraceEventSystemStatsMonitor);
+};
+
+// Converts system memory profiling stats in |input| to
+// trace event compatible JSON and appends to |output|. Visible for testing.
+BASE_EXPORT void AppendSystemProfileAsTraceFormat(const SystemMetrics&
+                                                  system_stats,
+                                                  std::string* output);
+
+}  // namespace trace_event
+}  // namespace base
+
+#endif  // BASE_TRACE_EVENT_TRACE_EVENT_SYSTEM_STATS_MONITOR_H_
diff --git a/base/trace_event/trace_event_system_stats_monitor_unittest.cc b/base/trace_event/trace_event_system_stats_monitor_unittest.cc
new file mode 100644
index 0000000..52a05ba
--- /dev/null
+++ b/base/trace_event/trace_event_system_stats_monitor_unittest.cc
@@ -0,0 +1,68 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/trace_event/trace_event_system_stats_monitor.h"
+
+#include <sstream>
+#include <string>
+
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/trace_event/trace_event_impl.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace trace_event {
+
+#if !defined(OS_IOS)
+// Tests for the system stats monitor.
+// Exists as a class so it can be a friend of TraceEventSystemStatsMonitor.
+class TraceSystemStatsMonitorTest : public testing::Test {
+ public:
+  TraceSystemStatsMonitorTest() = default;
+  ~TraceSystemStatsMonitorTest() override = default;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TraceSystemStatsMonitorTest);
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+TEST_F(TraceSystemStatsMonitorTest, TraceEventSystemStatsMonitor) {
+  MessageLoop message_loop;
+
+  // Start with no observers of the TraceLog.
+  EXPECT_EQ(0u, TraceLog::GetInstance()->GetObserverCountForTest());
+
+  // Creating a system stats monitor adds it to the TraceLog observer list.
+  std::unique_ptr<TraceEventSystemStatsMonitor> system_stats_monitor(
+      new TraceEventSystemStatsMonitor(message_loop.task_runner()));
+  EXPECT_EQ(1u, TraceLog::GetInstance()->GetObserverCountForTest());
+  EXPECT_TRUE(
+      TraceLog::GetInstance()->HasEnabledStateObserver(
+          system_stats_monitor.get()));
+
+  // By default the observer isn't dumping memory profiles.
+  EXPECT_FALSE(system_stats_monitor->IsTimerRunningForTest());
+
+  // Simulate enabling tracing.
+  system_stats_monitor->StartProfiling();
+  RunLoop().RunUntilIdle();
+  EXPECT_TRUE(system_stats_monitor->IsTimerRunningForTest());
+
+  // Simulate disabling tracing.
+  system_stats_monitor->StopProfiling();
+  RunLoop().RunUntilIdle();
+  EXPECT_FALSE(system_stats_monitor->IsTimerRunningForTest());
+
+  // Deleting the observer removes it from the TraceLog observer list.
+  system_stats_monitor.reset();
+  EXPECT_EQ(0u, TraceLog::GetInstance()->GetObserverCountForTest());
+}
+#endif  // !defined(OS_IOS)
+
+}  // namespace trace_event
+}  // namespace base
diff --git a/base/trace_event/trace_event_unittest.cc b/base/trace_event/trace_event_unittest.cc
new file mode 100644
index 0000000..a413ee5
--- /dev/null
+++ b/base/trace_event/trace_event_unittest.cc
@@ -0,0 +1,3169 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/trace_event/trace_event.h"
+
+#include <math.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include <cstdlib>
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/json/json_reader.h"
+#include "base/json/json_writer.h"
+#include "base/location.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/memory/singleton.h"
+#include "base/process/process_handle.h"
+#include "base/single_thread_task_runner.h"
+#include "base/stl_util.h"
+#include "base/strings/pattern.h"
+#include "base/strings/stringprintf.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/platform_thread.h"
+#include "base/threading/thread.h"
+#include "base/time/time.h"
+#include "base/trace_event/event_name_filter.h"
+#include "base/trace_event/heap_profiler_event_filter.h"
+#include "base/trace_event/trace_buffer.h"
+#include "base/trace_event/trace_event.h"
+#include "base/trace_event/trace_event_filter.h"
+#include "base/trace_event/trace_event_filter_test_utils.h"
+#include "base/values.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace trace_event {
+
+namespace {
+
+enum CompareOp {
+  IS_EQUAL,
+  IS_NOT_EQUAL,
+};
+
+struct JsonKeyValue {
+  const char* key;
+  const char* value;
+  CompareOp op;
+};
+
+const int kThreadId = 42;
+const int kAsyncId = 5;
+const char kAsyncIdStr[] = "0x5";
+const int kAsyncId2 = 6;
+const char kAsyncId2Str[] = "0x6";
+const int kFlowId = 7;
+const char kFlowIdStr[] = "0x7";
+
+const  char kRecordAllCategoryFilter[] = "*";
+
+class TraceEventTestFixture : public testing::Test {
+ public:
+  void OnTraceDataCollected(
+      WaitableEvent* flush_complete_event,
+      const scoped_refptr<base::RefCountedString>& events_str,
+      bool has_more_events);
+  DictionaryValue* FindMatchingTraceEntry(const JsonKeyValue* key_values);
+  DictionaryValue* FindNamePhase(const char* name, const char* phase);
+  DictionaryValue* FindNamePhaseKeyValue(const char* name,
+                                         const char* phase,
+                                         const char* key,
+                                         const char* value);
+  void DropTracedMetadataRecords();
+  bool FindMatchingValue(const char* key,
+                         const char* value);
+  bool FindNonMatchingValue(const char* key,
+                            const char* value);
+  void Clear() {
+    trace_parsed_.Clear();
+    json_output_.json_output.clear();
+  }
+
+  void BeginTrace() {
+    BeginSpecificTrace("*");
+  }
+
+  void BeginSpecificTrace(const std::string& filter) {
+    TraceLog::GetInstance()->SetEnabled(TraceConfig(filter, ""),
+                                        TraceLog::RECORDING_MODE);
+  }
+
+  void CancelTrace() {
+    WaitableEvent flush_complete_event(
+        WaitableEvent::ResetPolicy::AUTOMATIC,
+        WaitableEvent::InitialState::NOT_SIGNALED);
+    CancelTraceAsync(&flush_complete_event);
+    flush_complete_event.Wait();
+  }
+
+  void EndTraceAndFlush() {
+    num_flush_callbacks_ = 0;
+    WaitableEvent flush_complete_event(
+        WaitableEvent::ResetPolicy::AUTOMATIC,
+        WaitableEvent::InitialState::NOT_SIGNALED);
+    EndTraceAndFlushAsync(&flush_complete_event);
+    flush_complete_event.Wait();
+  }
+
+  // Used when testing thread-local buffers which requires the thread initiating
+  // flush to have a message loop.
+  void EndTraceAndFlushInThreadWithMessageLoop() {
+    WaitableEvent flush_complete_event(
+        WaitableEvent::ResetPolicy::AUTOMATIC,
+        WaitableEvent::InitialState::NOT_SIGNALED);
+    Thread flush_thread("flush");
+    flush_thread.Start();
+    flush_thread.task_runner()->PostTask(
+        FROM_HERE,
+        base::BindOnce(&TraceEventTestFixture::EndTraceAndFlushAsync,
+                       base::Unretained(this), &flush_complete_event));
+    flush_complete_event.Wait();
+  }
+
+  void CancelTraceAsync(WaitableEvent* flush_complete_event) {
+    TraceLog::GetInstance()->CancelTracing(
+        base::Bind(&TraceEventTestFixture::OnTraceDataCollected,
+                   base::Unretained(static_cast<TraceEventTestFixture*>(this)),
+                   base::Unretained(flush_complete_event)));
+  }
+
+  void EndTraceAndFlushAsync(WaitableEvent* flush_complete_event) {
+    TraceLog::GetInstance()->SetDisabled(TraceLog::RECORDING_MODE |
+                                         TraceLog::FILTERING_MODE);
+    TraceLog::GetInstance()->Flush(
+        base::Bind(&TraceEventTestFixture::OnTraceDataCollected,
+                   base::Unretained(static_cast<TraceEventTestFixture*>(this)),
+                   base::Unretained(flush_complete_event)));
+  }
+
+  void SetUp() override {
+    const char* name = PlatformThread::GetName();
+    old_thread_name_ = name ? strdup(name) : nullptr;
+
+    TraceLog::ResetForTesting();
+    TraceLog* tracelog = TraceLog::GetInstance();
+    ASSERT_TRUE(tracelog);
+    ASSERT_FALSE(tracelog->IsEnabled());
+    trace_buffer_.SetOutputCallback(json_output_.GetCallback());
+    num_flush_callbacks_ = 0;
+  }
+  void TearDown() override {
+    if (TraceLog::GetInstance())
+      EXPECT_FALSE(TraceLog::GetInstance()->IsEnabled());
+    PlatformThread::SetName(old_thread_name_ ? old_thread_name_ : "");
+    free(old_thread_name_);
+    old_thread_name_ = nullptr;
+    // We want our singleton torn down after each test.
+    TraceLog::ResetForTesting();
+  }
+
+  char* old_thread_name_;
+  ListValue trace_parsed_;
+  TraceResultBuffer trace_buffer_;
+  TraceResultBuffer::SimpleOutput json_output_;
+  size_t num_flush_callbacks_;
+
+ private:
+  // We want our singleton torn down after each test.
+  ShadowingAtExitManager at_exit_manager_;
+  Lock lock_;
+};
+
+void TraceEventTestFixture::OnTraceDataCollected(
+    WaitableEvent* flush_complete_event,
+    const scoped_refptr<base::RefCountedString>& events_str,
+    bool has_more_events) {
+  num_flush_callbacks_++;
+  if (num_flush_callbacks_ > 1) {
+    EXPECT_FALSE(events_str->data().empty());
+  }
+  AutoLock lock(lock_);
+  json_output_.json_output.clear();
+  trace_buffer_.Start();
+  trace_buffer_.AddFragment(events_str->data());
+  trace_buffer_.Finish();
+
+  std::unique_ptr<Value> root =
+      base::JSONReader::Read(json_output_.json_output, JSON_PARSE_RFC);
+
+  if (!root.get()) {
+    LOG(ERROR) << json_output_.json_output;
+  }
+
+  ListValue* root_list = nullptr;
+  ASSERT_TRUE(root.get());
+  ASSERT_TRUE(root->GetAsList(&root_list));
+
+  // Move items into our aggregate collection
+  while (root_list->GetSize()) {
+    std::unique_ptr<Value> item;
+    root_list->Remove(0, &item);
+    trace_parsed_.Append(std::move(item));
+  }
+
+  if (!has_more_events)
+    flush_complete_event->Signal();
+}
+
+static bool CompareJsonValues(const std::string& lhs,
+                              const std::string& rhs,
+                              CompareOp op) {
+  switch (op) {
+    case IS_EQUAL:
+      return lhs == rhs;
+    case IS_NOT_EQUAL:
+      return lhs != rhs;
+    default:
+      CHECK(0);
+  }
+  return false;
+}
+
+static bool IsKeyValueInDict(const JsonKeyValue* key_value,
+                             DictionaryValue* dict) {
+  Value* value = nullptr;
+  std::string value_str;
+  if (dict->Get(key_value->key, &value) &&
+      value->GetAsString(&value_str) &&
+      CompareJsonValues(value_str, key_value->value, key_value->op))
+    return true;
+
+  // Recurse to test arguments
+  DictionaryValue* args_dict = nullptr;
+  dict->GetDictionary("args", &args_dict);
+  if (args_dict)
+    return IsKeyValueInDict(key_value, args_dict);
+
+  return false;
+}
+
+static bool IsAllKeyValueInDict(const JsonKeyValue* key_values,
+                                DictionaryValue* dict) {
+  // Scan all key_values, they must all be present and equal.
+  while (key_values && key_values->key) {
+    if (!IsKeyValueInDict(key_values, dict))
+      return false;
+    ++key_values;
+  }
+  return true;
+}
+
+DictionaryValue* TraceEventTestFixture::FindMatchingTraceEntry(
+    const JsonKeyValue* key_values) {
+  // Scan all items
+  size_t trace_parsed_count = trace_parsed_.GetSize();
+  for (size_t i = 0; i < trace_parsed_count; i++) {
+    Value* value = nullptr;
+    trace_parsed_.Get(i, &value);
+    if (!value || value->type() != Value::Type::DICTIONARY)
+      continue;
+    DictionaryValue* dict = static_cast<DictionaryValue*>(value);
+
+    if (IsAllKeyValueInDict(key_values, dict))
+      return dict;
+  }
+  return nullptr;
+}
+
+void TraceEventTestFixture::DropTracedMetadataRecords() {
+  std::unique_ptr<ListValue> old_trace_parsed(trace_parsed_.CreateDeepCopy());
+  size_t old_trace_parsed_size = old_trace_parsed->GetSize();
+  trace_parsed_.Clear();
+
+  for (size_t i = 0; i < old_trace_parsed_size; i++) {
+    Value* value = nullptr;
+    old_trace_parsed->Get(i, &value);
+    if (!value || value->type() != Value::Type::DICTIONARY) {
+      trace_parsed_.Append(value->CreateDeepCopy());
+      continue;
+    }
+    DictionaryValue* dict = static_cast<DictionaryValue*>(value);
+    std::string tmp;
+    if (dict->GetString("ph", &tmp) && tmp == "M")
+      continue;
+
+    trace_parsed_.Append(value->CreateDeepCopy());
+  }
+}
+
+DictionaryValue* TraceEventTestFixture::FindNamePhase(const char* name,
+                                                      const char* phase) {
+  JsonKeyValue key_values[] = {{"name", name, IS_EQUAL},
+                               {"ph", phase, IS_EQUAL},
+                               {nullptr, nullptr, IS_EQUAL}};
+  return FindMatchingTraceEntry(key_values);
+}
+
+DictionaryValue* TraceEventTestFixture::FindNamePhaseKeyValue(
+    const char* name,
+    const char* phase,
+    const char* key,
+    const char* value) {
+  JsonKeyValue key_values[] = {{"name", name, IS_EQUAL},
+                               {"ph", phase, IS_EQUAL},
+                               {key, value, IS_EQUAL},
+                               {nullptr, nullptr, IS_EQUAL}};
+  return FindMatchingTraceEntry(key_values);
+}
+
+bool TraceEventTestFixture::FindMatchingValue(const char* key,
+                                              const char* value) {
+  JsonKeyValue key_values[] = {{key, value, IS_EQUAL},
+                               {nullptr, nullptr, IS_EQUAL}};
+  return FindMatchingTraceEntry(key_values);
+}
+
+bool TraceEventTestFixture::FindNonMatchingValue(const char* key,
+                                                 const char* value) {
+  JsonKeyValue key_values[] = {{key, value, IS_NOT_EQUAL},
+                               {nullptr, nullptr, IS_EQUAL}};
+  return FindMatchingTraceEntry(key_values);
+}
+
+bool IsStringInDict(const char* string_to_match, const DictionaryValue* dict) {
+  for (DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); it.Advance()) {
+    if (it.key().find(string_to_match) != std::string::npos)
+      return true;
+
+    std::string value_str;
+    it.value().GetAsString(&value_str);
+    if (value_str.find(string_to_match) != std::string::npos)
+      return true;
+  }
+
+  // Recurse to test arguments
+  const DictionaryValue* args_dict = nullptr;
+  dict->GetDictionary("args", &args_dict);
+  if (args_dict)
+    return IsStringInDict(string_to_match, args_dict);
+
+  return false;
+}
+
+const DictionaryValue* FindTraceEntry(
+    const ListValue& trace_parsed,
+    const char* string_to_match,
+    const DictionaryValue* match_after_this_item = nullptr) {
+  // Scan all items
+  size_t trace_parsed_count = trace_parsed.GetSize();
+  for (size_t i = 0; i < trace_parsed_count; i++) {
+    const Value* value = nullptr;
+    trace_parsed.Get(i, &value);
+    if (match_after_this_item) {
+      if (value == match_after_this_item)
+        match_after_this_item = nullptr;
+      continue;
+    }
+    if (!value || value->type() != Value::Type::DICTIONARY)
+      continue;
+    const DictionaryValue* dict = static_cast<const DictionaryValue*>(value);
+
+    if (IsStringInDict(string_to_match, dict))
+      return dict;
+  }
+  return nullptr;
+}
+
+std::vector<const DictionaryValue*> FindTraceEntries(
+    const ListValue& trace_parsed,
+    const char* string_to_match) {
+  std::vector<const DictionaryValue*> hits;
+  size_t trace_parsed_count = trace_parsed.GetSize();
+  for (size_t i = 0; i < trace_parsed_count; i++) {
+    const Value* value = nullptr;
+    trace_parsed.Get(i, &value);
+    if (!value || value->type() != Value::Type::DICTIONARY)
+      continue;
+    const DictionaryValue* dict = static_cast<const DictionaryValue*>(value);
+
+    if (IsStringInDict(string_to_match, dict))
+      hits.push_back(dict);
+  }
+  return hits;
+}
+
+const char kControlCharacters[] = "\001\002\003\n\r";
+
+void TraceWithAllMacroVariants(WaitableEvent* task_complete_event) {
+  {
+    TRACE_EVENT0("all", "TRACE_EVENT0 call");
+    TRACE_EVENT1("all", "TRACE_EVENT1 call", "name1", "value1");
+    TRACE_EVENT2("all", "TRACE_EVENT2 call",
+                 "name1", "\"value1\"",
+                 "name2", "value\\2");
+
+    TRACE_EVENT_INSTANT0("all", "TRACE_EVENT_INSTANT0 call",
+                         TRACE_EVENT_SCOPE_GLOBAL);
+    TRACE_EVENT_INSTANT1("all", "TRACE_EVENT_INSTANT1 call",
+                         TRACE_EVENT_SCOPE_PROCESS, "name1", "value1");
+    TRACE_EVENT_INSTANT2("all", "TRACE_EVENT_INSTANT2 call",
+                         TRACE_EVENT_SCOPE_THREAD,
+                         "name1", "value1",
+                         "name2", "value2");
+
+    TRACE_EVENT_BEGIN0("all", "TRACE_EVENT_BEGIN0 call");
+    TRACE_EVENT_BEGIN1("all", "TRACE_EVENT_BEGIN1 call", "name1", "value1");
+    TRACE_EVENT_BEGIN2("all", "TRACE_EVENT_BEGIN2 call",
+                       "name1", "value1",
+                       "name2", "value2");
+
+    TRACE_EVENT_END0("all", "TRACE_EVENT_END0 call");
+    TRACE_EVENT_END1("all", "TRACE_EVENT_END1 call", "name1", "value1");
+    TRACE_EVENT_END2("all", "TRACE_EVENT_END2 call",
+                     "name1", "value1",
+                     "name2", "value2");
+
+    TRACE_EVENT_ASYNC_BEGIN0("all", "TRACE_EVENT_ASYNC_BEGIN0 call", kAsyncId);
+    TRACE_EVENT_ASYNC_BEGIN1("all", "TRACE_EVENT_ASYNC_BEGIN1 call", kAsyncId,
+                             "name1", "value1");
+    TRACE_EVENT_ASYNC_BEGIN2("all", "TRACE_EVENT_ASYNC_BEGIN2 call", kAsyncId,
+                             "name1", "value1",
+                             "name2", "value2");
+
+    TRACE_EVENT_ASYNC_STEP_INTO0("all", "TRACE_EVENT_ASYNC_STEP_INTO0 call",
+                                 kAsyncId, "step_begin1");
+    TRACE_EVENT_ASYNC_STEP_INTO1("all", "TRACE_EVENT_ASYNC_STEP_INTO1 call",
+                                 kAsyncId, "step_begin2", "name1", "value1");
+
+    TRACE_EVENT_ASYNC_END0("all", "TRACE_EVENT_ASYNC_END0 call", kAsyncId);
+    TRACE_EVENT_ASYNC_END1("all", "TRACE_EVENT_ASYNC_END1 call", kAsyncId,
+                           "name1", "value1");
+    TRACE_EVENT_ASYNC_END2("all", "TRACE_EVENT_ASYNC_END2 call", kAsyncId,
+                           "name1", "value1",
+                           "name2", "value2");
+
+    TRACE_EVENT_FLOW_BEGIN0("all", "TRACE_EVENT_FLOW_BEGIN0 call", kFlowId);
+    TRACE_EVENT_FLOW_STEP0("all", "TRACE_EVENT_FLOW_STEP0 call",
+                           kFlowId, "step1");
+    TRACE_EVENT_FLOW_END_BIND_TO_ENCLOSING0("all",
+        "TRACE_EVENT_FLOW_END_BIND_TO_ENCLOSING0 call", kFlowId);
+
+    TRACE_COUNTER1("all", "TRACE_COUNTER1 call", 31415);
+    TRACE_COUNTER2("all", "TRACE_COUNTER2 call",
+                   "a", 30000,
+                   "b", 1415);
+
+    TRACE_COUNTER_WITH_TIMESTAMP1("all", "TRACE_COUNTER_WITH_TIMESTAMP1 call",
+                                  TimeTicks::FromInternalValue(42), 31415);
+    TRACE_COUNTER_WITH_TIMESTAMP2("all", "TRACE_COUNTER_WITH_TIMESTAMP2 call",
+                                  TimeTicks::FromInternalValue(42),
+                                  "a", 30000, "b", 1415);
+
+    TRACE_COUNTER_ID1("all", "TRACE_COUNTER_ID1 call", 0x319009, 31415);
+    TRACE_COUNTER_ID2("all", "TRACE_COUNTER_ID2 call", 0x319009,
+                      "a", 30000, "b", 1415);
+
+    TRACE_EVENT_COPY_BEGIN_WITH_ID_TID_AND_TIMESTAMP0("all",
+        "TRACE_EVENT_COPY_BEGIN_WITH_ID_TID_AND_TIMESTAMP0 call",
+        kAsyncId, kThreadId, TimeTicks::FromInternalValue(12345));
+    TRACE_EVENT_COPY_END_WITH_ID_TID_AND_TIMESTAMP0("all",
+        "TRACE_EVENT_COPY_END_WITH_ID_TID_AND_TIMESTAMP0 call",
+        kAsyncId, kThreadId, TimeTicks::FromInternalValue(23456));
+
+    TRACE_EVENT_BEGIN_WITH_ID_TID_AND_TIMESTAMP0("all",
+        "TRACE_EVENT_BEGIN_WITH_ID_TID_AND_TIMESTAMP0 call",
+        kAsyncId2, kThreadId, TimeTicks::FromInternalValue(34567));
+    TRACE_EVENT_ASYNC_STEP_PAST0("all", "TRACE_EVENT_ASYNC_STEP_PAST0 call",
+                                 kAsyncId2, "step_end1");
+    TRACE_EVENT_ASYNC_STEP_PAST1("all", "TRACE_EVENT_ASYNC_STEP_PAST1 call",
+                                 kAsyncId2, "step_end2", "name1", "value1");
+
+    TRACE_EVENT_END_WITH_ID_TID_AND_TIMESTAMP0("all",
+        "TRACE_EVENT_END_WITH_ID_TID_AND_TIMESTAMP0 call",
+        kAsyncId2, kThreadId, TimeTicks::FromInternalValue(45678));
+
+    TRACE_EVENT_OBJECT_CREATED_WITH_ID("all", "tracked object 1", 0x42);
+    TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(
+        "all", "tracked object 1", 0x42, "hello");
+    TRACE_EVENT_OBJECT_DELETED_WITH_ID("all", "tracked object 1", 0x42);
+
+    TraceScopedTrackableObject<int> trackable("all", "tracked object 2",
+                                              0x2128506);
+    trackable.snapshot("world");
+
+    TRACE_EVENT_OBJECT_CREATED_WITH_ID(
+        "all", "tracked object 3", TRACE_ID_WITH_SCOPE("scope", 0x42));
+    TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(
+        "all", "tracked object 3", TRACE_ID_WITH_SCOPE("scope", 0x42), "hello");
+    TRACE_EVENT_OBJECT_DELETED_WITH_ID(
+        "all", "tracked object 3", TRACE_ID_WITH_SCOPE("scope", 0x42));
+
+    TRACE_EVENT1(kControlCharacters, kControlCharacters,
+                 kControlCharacters, kControlCharacters);
+
+    uint64_t context_id = 0x20151021;
+
+    TRACE_EVENT_ENTER_CONTEXT("all", "TRACE_EVENT_ENTER_CONTEXT call",
+                              TRACE_ID_WITH_SCOPE("scope", context_id));
+    TRACE_EVENT_LEAVE_CONTEXT("all", "TRACE_EVENT_LEAVE_CONTEXT call",
+                              TRACE_ID_WITH_SCOPE("scope", context_id));
+    TRACE_EVENT_SCOPED_CONTEXT("disabled-by-default-cat",
+                               "TRACE_EVENT_SCOPED_CONTEXT disabled call",
+                               context_id);
+    TRACE_EVENT_SCOPED_CONTEXT("all", "TRACE_EVENT_SCOPED_CONTEXT call",
+                               context_id);
+
+    TRACE_LINK_IDS("all", "TRACE_LINK_IDS simple call", 0x1000, 0x2000);
+    TRACE_LINK_IDS("all", "TRACE_LINK_IDS scoped call",
+                   TRACE_ID_WITH_SCOPE("scope 1", 0x1000),
+                   TRACE_ID_WITH_SCOPE("scope 2", 0x2000));
+    TRACE_LINK_IDS("all", "TRACE_LINK_IDS to a local ID", 0x1000,
+                   TRACE_ID_LOCAL(0x2000));
+    TRACE_LINK_IDS("all", "TRACE_LINK_IDS to a global ID", 0x1000,
+                   TRACE_ID_GLOBAL(0x2000));
+    TRACE_LINK_IDS("all", "TRACE_LINK_IDS to a composite ID", 0x1000,
+                   TRACE_ID_WITH_SCOPE("scope 1", 0x2000, 0x3000));
+
+    TRACE_EVENT_ASYNC_BEGIN0("all", "async default process scope", 0x1000);
+    TRACE_EVENT_ASYNC_BEGIN0("all", "async local id", TRACE_ID_LOCAL(0x2000));
+    TRACE_EVENT_ASYNC_BEGIN0("all", "async global id", TRACE_ID_GLOBAL(0x3000));
+    TRACE_EVENT_ASYNC_BEGIN0("all", "async global id with scope string",
+                             TRACE_ID_WITH_SCOPE("scope string",
+                                                 TRACE_ID_GLOBAL(0x4000)));
+  }  // Scope close causes TRACE_EVENT0 etc to send their END events.
+
+  if (task_complete_event)
+    task_complete_event->Signal();
+}
+
+void ValidateAllTraceMacrosCreatedData(const ListValue& trace_parsed) {
+  const DictionaryValue* item = nullptr;
+
+#define EXPECT_FIND_(string) \
+    item = FindTraceEntry(trace_parsed, string); \
+    EXPECT_TRUE(item);
+#define EXPECT_NOT_FIND_(string) \
+    item = FindTraceEntry(trace_parsed, string); \
+    EXPECT_FALSE(item);
+#define EXPECT_SUB_FIND_(string) \
+    if (item) \
+      EXPECT_TRUE(IsStringInDict(string, item));
+
+  EXPECT_FIND_("TRACE_EVENT0 call");
+  {
+    std::string ph;
+    std::string ph_end;
+    EXPECT_TRUE((item = FindTraceEntry(trace_parsed, "TRACE_EVENT0 call")));
+    EXPECT_TRUE((item && item->GetString("ph", &ph)));
+    EXPECT_EQ("X", ph);
+    item = FindTraceEntry(trace_parsed, "TRACE_EVENT0 call", item);
+    EXPECT_FALSE(item);
+  }
+  EXPECT_FIND_("TRACE_EVENT1 call");
+  EXPECT_SUB_FIND_("name1");
+  EXPECT_SUB_FIND_("value1");
+  EXPECT_FIND_("TRACE_EVENT2 call");
+  EXPECT_SUB_FIND_("name1");
+  EXPECT_SUB_FIND_("\"value1\"");
+  EXPECT_SUB_FIND_("name2");
+  EXPECT_SUB_FIND_("value\\2");
+
+  EXPECT_FIND_("TRACE_EVENT_INSTANT0 call");
+  {
+    std::string scope;
+    EXPECT_TRUE((item && item->GetString("s", &scope)));
+    EXPECT_EQ("g", scope);
+  }
+  EXPECT_FIND_("TRACE_EVENT_INSTANT1 call");
+  {
+    std::string scope;
+    EXPECT_TRUE((item && item->GetString("s", &scope)));
+    EXPECT_EQ("p", scope);
+  }
+  EXPECT_SUB_FIND_("name1");
+  EXPECT_SUB_FIND_("value1");
+  EXPECT_FIND_("TRACE_EVENT_INSTANT2 call");
+  {
+    std::string scope;
+    EXPECT_TRUE((item && item->GetString("s", &scope)));
+    EXPECT_EQ("t", scope);
+  }
+  EXPECT_SUB_FIND_("name1");
+  EXPECT_SUB_FIND_("value1");
+  EXPECT_SUB_FIND_("name2");
+  EXPECT_SUB_FIND_("value2");
+
+  EXPECT_FIND_("TRACE_EVENT_BEGIN0 call");
+  EXPECT_FIND_("TRACE_EVENT_BEGIN1 call");
+  EXPECT_SUB_FIND_("name1");
+  EXPECT_SUB_FIND_("value1");
+  EXPECT_FIND_("TRACE_EVENT_BEGIN2 call");
+  EXPECT_SUB_FIND_("name1");
+  EXPECT_SUB_FIND_("value1");
+  EXPECT_SUB_FIND_("name2");
+  EXPECT_SUB_FIND_("value2");
+
+  EXPECT_FIND_("TRACE_EVENT_END0 call");
+  EXPECT_FIND_("TRACE_EVENT_END1 call");
+  EXPECT_SUB_FIND_("name1");
+  EXPECT_SUB_FIND_("value1");
+  EXPECT_FIND_("TRACE_EVENT_END2 call");
+  EXPECT_SUB_FIND_("name1");
+  EXPECT_SUB_FIND_("value1");
+  EXPECT_SUB_FIND_("name2");
+  EXPECT_SUB_FIND_("value2");
+
+  EXPECT_FIND_("TRACE_EVENT_ASYNC_BEGIN0 call");
+  EXPECT_SUB_FIND_("id");
+  EXPECT_SUB_FIND_(kAsyncIdStr);
+  EXPECT_FIND_("TRACE_EVENT_ASYNC_BEGIN1 call");
+  EXPECT_SUB_FIND_("id");
+  EXPECT_SUB_FIND_(kAsyncIdStr);
+  EXPECT_SUB_FIND_("name1");
+  EXPECT_SUB_FIND_("value1");
+  EXPECT_FIND_("TRACE_EVENT_ASYNC_BEGIN2 call");
+  EXPECT_SUB_FIND_("id");
+  EXPECT_SUB_FIND_(kAsyncIdStr);
+  EXPECT_SUB_FIND_("name1");
+  EXPECT_SUB_FIND_("value1");
+  EXPECT_SUB_FIND_("name2");
+  EXPECT_SUB_FIND_("value2");
+
+  EXPECT_FIND_("TRACE_EVENT_ASYNC_STEP_INTO0 call");
+  EXPECT_SUB_FIND_("id");
+  EXPECT_SUB_FIND_(kAsyncIdStr);
+  EXPECT_SUB_FIND_("step_begin1");
+  EXPECT_FIND_("TRACE_EVENT_ASYNC_STEP_INTO1 call");
+  EXPECT_SUB_FIND_("id");
+  EXPECT_SUB_FIND_(kAsyncIdStr);
+  EXPECT_SUB_FIND_("step_begin2");
+  EXPECT_SUB_FIND_("name1");
+  EXPECT_SUB_FIND_("value1");
+
+  EXPECT_FIND_("TRACE_EVENT_ASYNC_END0 call");
+  EXPECT_SUB_FIND_("id");
+  EXPECT_SUB_FIND_(kAsyncIdStr);
+  EXPECT_FIND_("TRACE_EVENT_ASYNC_END1 call");
+  EXPECT_SUB_FIND_("id");
+  EXPECT_SUB_FIND_(kAsyncIdStr);
+  EXPECT_SUB_FIND_("name1");
+  EXPECT_SUB_FIND_("value1");
+  EXPECT_FIND_("TRACE_EVENT_ASYNC_END2 call");
+  EXPECT_SUB_FIND_("id");
+  EXPECT_SUB_FIND_(kAsyncIdStr);
+  EXPECT_SUB_FIND_("name1");
+  EXPECT_SUB_FIND_("value1");
+  EXPECT_SUB_FIND_("name2");
+  EXPECT_SUB_FIND_("value2");
+
+  EXPECT_FIND_("TRACE_EVENT_FLOW_BEGIN0 call");
+  EXPECT_SUB_FIND_("id");
+  EXPECT_SUB_FIND_(kFlowIdStr);
+  EXPECT_FIND_("TRACE_EVENT_FLOW_STEP0 call");
+  EXPECT_SUB_FIND_("id");
+  EXPECT_SUB_FIND_(kFlowIdStr);
+  EXPECT_SUB_FIND_("step1");
+  EXPECT_FIND_("TRACE_EVENT_FLOW_END_BIND_TO_ENCLOSING0 call");
+  EXPECT_SUB_FIND_("id");
+  EXPECT_SUB_FIND_(kFlowIdStr);
+
+  EXPECT_FIND_("TRACE_COUNTER1 call");
+  {
+    std::string ph;
+    EXPECT_TRUE((item && item->GetString("ph", &ph)));
+    EXPECT_EQ("C", ph);
+
+    int value;
+    EXPECT_TRUE((item && item->GetInteger("args.value", &value)));
+    EXPECT_EQ(31415, value);
+  }
+
+  EXPECT_FIND_("TRACE_COUNTER2 call");
+  {
+    std::string ph;
+    EXPECT_TRUE((item && item->GetString("ph", &ph)));
+    EXPECT_EQ("C", ph);
+
+    int value;
+    EXPECT_TRUE((item && item->GetInteger("args.a", &value)));
+    EXPECT_EQ(30000, value);
+
+    EXPECT_TRUE((item && item->GetInteger("args.b", &value)));
+    EXPECT_EQ(1415, value);
+  }
+
+  EXPECT_FIND_("TRACE_COUNTER_WITH_TIMESTAMP1 call");
+  {
+    std::string ph;
+    EXPECT_TRUE((item && item->GetString("ph", &ph)));
+    EXPECT_EQ("C", ph);
+
+    int value;
+    EXPECT_TRUE((item && item->GetInteger("args.value", &value)));
+    EXPECT_EQ(31415, value);
+
+    int ts;
+    EXPECT_TRUE((item && item->GetInteger("ts", &ts)));
+    EXPECT_EQ(42, ts);
+  }
+
+  EXPECT_FIND_("TRACE_COUNTER_WITH_TIMESTAMP2 call");
+  {
+    std::string ph;
+    EXPECT_TRUE((item && item->GetString("ph", &ph)));
+    EXPECT_EQ("C", ph);
+
+    int value;
+    EXPECT_TRUE((item && item->GetInteger("args.a", &value)));
+    EXPECT_EQ(30000, value);
+
+    EXPECT_TRUE((item && item->GetInteger("args.b", &value)));
+    EXPECT_EQ(1415, value);
+
+    int ts;
+    EXPECT_TRUE((item && item->GetInteger("ts", &ts)));
+    EXPECT_EQ(42, ts);
+  }
+
+  EXPECT_FIND_("TRACE_COUNTER_ID1 call");
+  {
+    std::string id;
+    EXPECT_TRUE((item && item->GetString("id", &id)));
+    EXPECT_EQ("0x319009", id);
+
+    std::string ph;
+    EXPECT_TRUE((item && item->GetString("ph", &ph)));
+    EXPECT_EQ("C", ph);
+
+    int value;
+    EXPECT_TRUE((item && item->GetInteger("args.value", &value)));
+    EXPECT_EQ(31415, value);
+  }
+
+  EXPECT_FIND_("TRACE_COUNTER_ID2 call");
+  {
+    std::string id;
+    EXPECT_TRUE((item && item->GetString("id", &id)));
+    EXPECT_EQ("0x319009", id);
+
+    std::string ph;
+    EXPECT_TRUE((item && item->GetString("ph", &ph)));
+    EXPECT_EQ("C", ph);
+
+    int value;
+    EXPECT_TRUE((item && item->GetInteger("args.a", &value)));
+    EXPECT_EQ(30000, value);
+
+    EXPECT_TRUE((item && item->GetInteger("args.b", &value)));
+    EXPECT_EQ(1415, value);
+  }
+
+  EXPECT_FIND_("TRACE_EVENT_COPY_BEGIN_WITH_ID_TID_AND_TIMESTAMP0 call");
+  {
+    int val;
+    EXPECT_TRUE((item && item->GetInteger("ts", &val)));
+    EXPECT_EQ(12345, val);
+    EXPECT_TRUE((item && item->GetInteger("tid", &val)));
+    EXPECT_EQ(kThreadId, val);
+    std::string id;
+    EXPECT_TRUE((item && item->GetString("id", &id)));
+    EXPECT_EQ(kAsyncIdStr, id);
+  }
+
+  EXPECT_FIND_("TRACE_EVENT_COPY_END_WITH_ID_TID_AND_TIMESTAMP0 call");
+  {
+    int val;
+    EXPECT_TRUE((item && item->GetInteger("ts", &val)));
+    EXPECT_EQ(23456, val);
+    EXPECT_TRUE((item && item->GetInteger("tid", &val)));
+    EXPECT_EQ(kThreadId, val);
+    std::string id;
+    EXPECT_TRUE((item && item->GetString("id", &id)));
+    EXPECT_EQ(kAsyncIdStr, id);
+  }
+
+  EXPECT_FIND_("TRACE_EVENT_BEGIN_WITH_ID_TID_AND_TIMESTAMP0 call");
+  {
+    int val;
+    EXPECT_TRUE((item && item->GetInteger("ts", &val)));
+    EXPECT_EQ(34567, val);
+    EXPECT_TRUE((item && item->GetInteger("tid", &val)));
+    EXPECT_EQ(kThreadId, val);
+    std::string id;
+    EXPECT_TRUE((item && item->GetString("id", &id)));
+    EXPECT_EQ(kAsyncId2Str, id);
+  }
+
+  EXPECT_FIND_("TRACE_EVENT_ASYNC_STEP_PAST0 call");
+  {
+    EXPECT_SUB_FIND_("id");
+    EXPECT_SUB_FIND_(kAsyncId2Str);
+    EXPECT_SUB_FIND_("step_end1");
+    EXPECT_FIND_("TRACE_EVENT_ASYNC_STEP_PAST1 call");
+    EXPECT_SUB_FIND_("id");
+    EXPECT_SUB_FIND_(kAsyncId2Str);
+    EXPECT_SUB_FIND_("step_end2");
+    EXPECT_SUB_FIND_("name1");
+    EXPECT_SUB_FIND_("value1");
+  }
+
+  EXPECT_FIND_("TRACE_EVENT_END_WITH_ID_TID_AND_TIMESTAMP0 call");
+  {
+    int val;
+    EXPECT_TRUE((item && item->GetInteger("ts", &val)));
+    EXPECT_EQ(45678, val);
+    EXPECT_TRUE((item && item->GetInteger("tid", &val)));
+    EXPECT_EQ(kThreadId, val);
+    std::string id;
+    EXPECT_TRUE((item && item->GetString("id", &id)));
+    EXPECT_EQ(kAsyncId2Str, id);
+  }
+
+  EXPECT_FIND_("tracked object 1");
+  {
+    std::string phase;
+    std::string id;
+    std::string snapshot;
+
+    EXPECT_TRUE((item && item->GetString("ph", &phase)));
+    EXPECT_EQ("N", phase);
+    EXPECT_FALSE((item && item->HasKey("scope")));
+    EXPECT_TRUE((item && item->GetString("id", &id)));
+    EXPECT_EQ("0x42", id);
+
+    item = FindTraceEntry(trace_parsed, "tracked object 1", item);
+    EXPECT_TRUE(item);
+    EXPECT_TRUE(item && item->GetString("ph", &phase));
+    EXPECT_EQ("O", phase);
+    EXPECT_FALSE((item && item->HasKey("scope")));
+    EXPECT_TRUE(item && item->GetString("id", &id));
+    EXPECT_EQ("0x42", id);
+    EXPECT_TRUE(item && item->GetString("args.snapshot", &snapshot));
+    EXPECT_EQ("hello", snapshot);
+
+    item = FindTraceEntry(trace_parsed, "tracked object 1", item);
+    EXPECT_TRUE(item);
+    EXPECT_TRUE(item && item->GetString("ph", &phase));
+    EXPECT_EQ("D", phase);
+    EXPECT_FALSE((item && item->HasKey("scope")));
+    EXPECT_TRUE(item && item->GetString("id", &id));
+    EXPECT_EQ("0x42", id);
+  }
+
+  EXPECT_FIND_("tracked object 2");
+  {
+    std::string phase;
+    std::string id;
+    std::string snapshot;
+
+    EXPECT_TRUE(item && item->GetString("ph", &phase));
+    EXPECT_EQ("N", phase);
+    EXPECT_TRUE(item && item->GetString("id", &id));
+    EXPECT_EQ("0x2128506", id);
+
+    item = FindTraceEntry(trace_parsed, "tracked object 2", item);
+    EXPECT_TRUE(item);
+    EXPECT_TRUE(item && item->GetString("ph", &phase));
+    EXPECT_EQ("O", phase);
+    EXPECT_TRUE(item && item->GetString("id", &id));
+    EXPECT_EQ("0x2128506", id);
+    EXPECT_TRUE(item && item->GetString("args.snapshot", &snapshot));
+    EXPECT_EQ("world", snapshot);
+
+    item = FindTraceEntry(trace_parsed, "tracked object 2", item);
+    EXPECT_TRUE(item);
+    EXPECT_TRUE(item && item->GetString("ph", &phase));
+    EXPECT_EQ("D", phase);
+    EXPECT_TRUE(item && item->GetString("id", &id));
+    EXPECT_EQ("0x2128506", id);
+  }
+
+  EXPECT_FIND_("tracked object 3");
+  {
+    std::string phase;
+    std::string scope;
+    std::string id;
+    std::string snapshot;
+
+    EXPECT_TRUE((item && item->GetString("ph", &phase)));
+    EXPECT_EQ("N", phase);
+    EXPECT_TRUE((item && item->GetString("scope", &scope)));
+    EXPECT_EQ("scope", scope);
+    EXPECT_TRUE((item && item->GetString("id", &id)));
+    EXPECT_EQ("0x42", id);
+
+    item = FindTraceEntry(trace_parsed, "tracked object 3", item);
+    EXPECT_TRUE(item);
+    EXPECT_TRUE(item && item->GetString("ph", &phase));
+    EXPECT_EQ("O", phase);
+    EXPECT_TRUE((item && item->GetString("scope", &scope)));
+    EXPECT_EQ("scope", scope);
+    EXPECT_TRUE(item && item->GetString("id", &id));
+    EXPECT_EQ("0x42", id);
+    EXPECT_TRUE(item && item->GetString("args.snapshot", &snapshot));
+    EXPECT_EQ("hello", snapshot);
+
+    item = FindTraceEntry(trace_parsed, "tracked object 3", item);
+    EXPECT_TRUE(item);
+    EXPECT_TRUE(item && item->GetString("ph", &phase));
+    EXPECT_EQ("D", phase);
+    EXPECT_TRUE((item && item->GetString("scope", &scope)));
+    EXPECT_EQ("scope", scope);
+    EXPECT_TRUE(item && item->GetString("id", &id));
+    EXPECT_EQ("0x42", id);
+  }
+
+  EXPECT_FIND_(kControlCharacters);
+  EXPECT_SUB_FIND_(kControlCharacters);
+
+  EXPECT_FIND_("TRACE_EVENT_ENTER_CONTEXT call");
+  {
+    std::string ph;
+    EXPECT_TRUE((item && item->GetString("ph", &ph)));
+    EXPECT_EQ("(", ph);
+
+    std::string scope;
+    std::string id;
+    EXPECT_TRUE((item && item->GetString("scope", &scope)));
+    EXPECT_EQ("scope", scope);
+    EXPECT_TRUE((item && item->GetString("id", &id)));
+    EXPECT_EQ("0x20151021", id);
+  }
+
+  EXPECT_FIND_("TRACE_EVENT_LEAVE_CONTEXT call");
+  {
+    std::string ph;
+    EXPECT_TRUE((item && item->GetString("ph", &ph)));
+    EXPECT_EQ(")", ph);
+
+    std::string scope;
+    std::string id;
+    EXPECT_TRUE((item && item->GetString("scope", &scope)));
+    EXPECT_EQ("scope", scope);
+    EXPECT_TRUE((item && item->GetString("id", &id)));
+    EXPECT_EQ("0x20151021", id);
+  }
+
+  std::vector<const DictionaryValue*> scoped_context_calls =
+      FindTraceEntries(trace_parsed, "TRACE_EVENT_SCOPED_CONTEXT call");
+  EXPECT_EQ(2u, scoped_context_calls.size());
+  {
+    item = scoped_context_calls[0];
+    std::string ph;
+    EXPECT_TRUE((item && item->GetString("ph", &ph)));
+    EXPECT_EQ("(", ph);
+
+    std::string id;
+    EXPECT_FALSE((item && item->HasKey("scope")));
+    EXPECT_TRUE((item && item->GetString("id", &id)));
+    EXPECT_EQ("0x20151021", id);
+  }
+
+  {
+    item = scoped_context_calls[1];
+    std::string ph;
+    EXPECT_TRUE((item && item->GetString("ph", &ph)));
+    EXPECT_EQ(")", ph);
+
+    std::string id;
+    EXPECT_FALSE((item && item->HasKey("scope")));
+    EXPECT_TRUE((item && item->GetString("id", &id)));
+    EXPECT_EQ("0x20151021", id);
+  }
+
+  EXPECT_FIND_("TRACE_LINK_IDS simple call");
+  {
+    std::string ph;
+    EXPECT_TRUE((item && item->GetString("ph", &ph)));
+    EXPECT_EQ("=", ph);
+
+    EXPECT_FALSE((item && item->HasKey("scope")));
+    std::string id1;
+    EXPECT_TRUE((item && item->GetString("id", &id1)));
+    EXPECT_EQ("0x1000", id1);
+
+    EXPECT_FALSE((item && item->HasKey("args.linked_id.scope")));
+    std::string id2;
+    EXPECT_TRUE((item && item->GetString("args.linked_id.id", &id2)));
+    EXPECT_EQ("0x2000", id2);
+  }
+
+  EXPECT_FIND_("TRACE_LINK_IDS scoped call");
+  {
+    std::string ph;
+    EXPECT_TRUE((item && item->GetString("ph", &ph)));
+    EXPECT_EQ("=", ph);
+
+    std::string scope1;
+    EXPECT_TRUE((item && item->GetString("scope", &scope1)));
+    EXPECT_EQ("scope 1", scope1);
+    std::string id1;
+    EXPECT_TRUE((item && item->GetString("id", &id1)));
+    EXPECT_EQ("0x1000", id1);
+
+    std::string scope2;
+    EXPECT_TRUE((item && item->GetString("args.linked_id.scope", &scope2)));
+    EXPECT_EQ("scope 2", scope2);
+    std::string id2;
+    EXPECT_TRUE((item && item->GetString("args.linked_id.id", &id2)));
+    EXPECT_EQ("0x2000", id2);
+  }
+
+  EXPECT_FIND_("TRACE_LINK_IDS to a local ID");
+  {
+    std::string ph;
+    EXPECT_TRUE((item && item->GetString("ph", &ph)));
+    EXPECT_EQ("=", ph);
+
+    EXPECT_FALSE((item && item->HasKey("scope")));
+    std::string id1;
+    EXPECT_TRUE((item && item->GetString("id", &id1)));
+    EXPECT_EQ("0x1000", id1);
+
+    EXPECT_FALSE((item && item->HasKey("args.linked_id.scope")));
+    std::string id2;
+    EXPECT_TRUE((item && item->GetString("args.linked_id.id2.local", &id2)));
+    EXPECT_EQ("0x2000", id2);
+  }
+
+  EXPECT_FIND_("TRACE_LINK_IDS to a global ID");
+  {
+    std::string ph;
+    EXPECT_TRUE((item && item->GetString("ph", &ph)));
+    EXPECT_EQ("=", ph);
+
+    EXPECT_FALSE((item && item->HasKey("scope")));
+    std::string id1;
+    EXPECT_TRUE((item && item->GetString("id", &id1)));
+    EXPECT_EQ("0x1000", id1);
+
+    EXPECT_FALSE((item && item->HasKey("args.linked_id.scope")));
+    std::string id2;
+    EXPECT_TRUE((item && item->GetString("args.linked_id.id2.global", &id2)));
+    EXPECT_EQ("0x2000", id2);
+  }
+
+  EXPECT_FIND_("TRACE_LINK_IDS to a composite ID");
+  {
+    std::string ph;
+    EXPECT_TRUE((item && item->GetString("ph", &ph)));
+    EXPECT_EQ("=", ph);
+
+    EXPECT_FALSE(item->HasKey("scope"));
+    std::string id1;
+    EXPECT_TRUE(item->GetString("id", &id1));
+    EXPECT_EQ("0x1000", id1);
+
+    std::string scope;
+    EXPECT_TRUE(item->GetString("args.linked_id.scope", &scope));
+    EXPECT_EQ("scope 1", scope);
+    std::string id2;
+    EXPECT_TRUE(item->GetString("args.linked_id.id", &id2));
+    EXPECT_EQ(id2, "0x2000/0x3000");
+  }
+
+  EXPECT_FIND_("async default process scope");
+  {
+    std::string ph;
+    EXPECT_TRUE((item && item->GetString("ph", &ph)));
+    EXPECT_EQ("S", ph);
+
+    std::string id;
+    EXPECT_TRUE((item && item->GetString("id", &id)));
+    EXPECT_EQ("0x1000", id);
+  }
+
+  EXPECT_FIND_("async local id");
+  {
+    std::string ph;
+    EXPECT_TRUE((item && item->GetString("ph", &ph)));
+    EXPECT_EQ("S", ph);
+
+    std::string id;
+    EXPECT_TRUE((item && item->GetString("id2.local", &id)));
+    EXPECT_EQ("0x2000", id);
+  }
+
+  EXPECT_FIND_("async global id");
+  {
+    std::string ph;
+    EXPECT_TRUE((item && item->GetString("ph", &ph)));
+    EXPECT_EQ("S", ph);
+
+    std::string id;
+    EXPECT_TRUE((item && item->GetString("id2.global", &id)));
+    EXPECT_EQ("0x3000", id);
+  }
+
+  EXPECT_FIND_("async global id with scope string");
+  {
+    std::string ph;
+    EXPECT_TRUE((item && item->GetString("ph", &ph)));
+    EXPECT_EQ("S", ph);
+
+    std::string id;
+    EXPECT_TRUE((item && item->GetString("id2.global", &id)));
+    EXPECT_EQ("0x4000", id);
+    std::string scope;
+    EXPECT_TRUE((item && item->GetString("scope", &scope)));
+    EXPECT_EQ("scope string", scope);
+  }
+}
+
+void TraceManyInstantEvents(int thread_id, int num_events,
+                            WaitableEvent* task_complete_event) {
+  for (int i = 0; i < num_events; i++) {
+    TRACE_EVENT_INSTANT2("all", "multi thread event",
+                         TRACE_EVENT_SCOPE_THREAD,
+                         "thread", thread_id,
+                         "event", i);
+  }
+
+  if (task_complete_event)
+    task_complete_event->Signal();
+}
+
+void ValidateInstantEventPresentOnEveryThread(const ListValue& trace_parsed,
+                                              int num_threads,
+                                              int num_events) {
+  std::map<int, std::map<int, bool> > results;
+
+  size_t trace_parsed_count = trace_parsed.GetSize();
+  for (size_t i = 0; i < trace_parsed_count; i++) {
+    const Value* value = nullptr;
+    trace_parsed.Get(i, &value);
+    if (!value || value->type() != Value::Type::DICTIONARY)
+      continue;
+    const DictionaryValue* dict = static_cast<const DictionaryValue*>(value);
+    std::string name;
+    dict->GetString("name", &name);
+    if (name != "multi thread event")
+      continue;
+
+    int thread = 0;
+    int event = 0;
+    EXPECT_TRUE(dict->GetInteger("args.thread", &thread));
+    EXPECT_TRUE(dict->GetInteger("args.event", &event));
+    results[thread][event] = true;
+  }
+
+  EXPECT_FALSE(results[-1][-1]);
+  for (int thread = 0; thread < num_threads; thread++) {
+    for (int event = 0; event < num_events; event++) {
+      EXPECT_TRUE(results[thread][event]);
+    }
+  }
+}
+
+void CheckTraceDefaultCategoryFilters(const TraceLog& trace_log) {
+  // Default enables all category filters except the disabled-by-default-* ones.
+  EXPECT_TRUE(*trace_log.GetCategoryGroupEnabled("foo"));
+  EXPECT_TRUE(*trace_log.GetCategoryGroupEnabled("bar"));
+  EXPECT_TRUE(*trace_log.GetCategoryGroupEnabled("foo,bar"));
+  EXPECT_TRUE(*trace_log.GetCategoryGroupEnabled(
+        "foo,disabled-by-default-foo"));
+  EXPECT_FALSE(*trace_log.GetCategoryGroupEnabled(
+        "disabled-by-default-foo,disabled-by-default-bar"));
+}
+
+}  // namespace
+
+// Simple Test for emitting data and validating it was received.
+TEST_F(TraceEventTestFixture, DataCaptured) {
+  TraceLog::GetInstance()->SetEnabled(TraceConfig(kRecordAllCategoryFilter, ""),
+                                      TraceLog::RECORDING_MODE);
+
+  TraceWithAllMacroVariants(nullptr);
+
+  EndTraceAndFlush();
+
+  ValidateAllTraceMacrosCreatedData(trace_parsed_);
+}
+
+// Emit some events and validate that only empty strings are received
+// if we tell Flush() to discard events.
+TEST_F(TraceEventTestFixture, DataDiscarded) {
+  TraceLog::GetInstance()->SetEnabled(TraceConfig(kRecordAllCategoryFilter, ""),
+                                      TraceLog::RECORDING_MODE);
+
+  TraceWithAllMacroVariants(nullptr);
+
+  CancelTrace();
+
+  EXPECT_TRUE(trace_parsed_.empty());
+}
+
+class MockEnabledStateChangedObserver :
+      public TraceLog::EnabledStateObserver {
+ public:
+  MOCK_METHOD0(OnTraceLogEnabled, void());
+  MOCK_METHOD0(OnTraceLogDisabled, void());
+};
+
+TEST_F(TraceEventTestFixture, EnabledObserverFiresOnEnable) {
+  MockEnabledStateChangedObserver observer;
+  TraceLog::GetInstance()->AddEnabledStateObserver(&observer);
+
+  EXPECT_CALL(observer, OnTraceLogEnabled())
+      .Times(1);
+  TraceLog::GetInstance()->SetEnabled(TraceConfig(kRecordAllCategoryFilter, ""),
+                                      TraceLog::RECORDING_MODE);
+  testing::Mock::VerifyAndClear(&observer);
+  EXPECT_TRUE(TraceLog::GetInstance()->IsEnabled());
+
+  // Cleanup.
+  TraceLog::GetInstance()->RemoveEnabledStateObserver(&observer);
+  TraceLog::GetInstance()->SetDisabled();
+}
+
+TEST_F(TraceEventTestFixture, EnabledObserverDoesntFireOnSecondEnable) {
+  TraceLog::GetInstance()->SetEnabled(TraceConfig(kRecordAllCategoryFilter, ""),
+                                      TraceLog::RECORDING_MODE);
+
+  testing::StrictMock<MockEnabledStateChangedObserver> observer;
+  TraceLog::GetInstance()->AddEnabledStateObserver(&observer);
+
+  EXPECT_CALL(observer, OnTraceLogEnabled())
+      .Times(0);
+  EXPECT_CALL(observer, OnTraceLogDisabled())
+      .Times(0);
+  TraceLog::GetInstance()->SetEnabled(TraceConfig(kRecordAllCategoryFilter, ""),
+                                      TraceLog::RECORDING_MODE);
+  testing::Mock::VerifyAndClear(&observer);
+  EXPECT_TRUE(TraceLog::GetInstance()->IsEnabled());
+
+  // Cleanup.
+  TraceLog::GetInstance()->RemoveEnabledStateObserver(&observer);
+  TraceLog::GetInstance()->SetDisabled();
+  TraceLog::GetInstance()->SetDisabled();
+}
+
+TEST_F(TraceEventTestFixture, EnabledObserverFiresOnFirstDisable) {
+  TraceConfig tc_inc_all("*", "");
+  TraceLog::GetInstance()->SetEnabled(tc_inc_all, TraceLog::RECORDING_MODE);
+  TraceLog::GetInstance()->SetEnabled(tc_inc_all, TraceLog::RECORDING_MODE);
+
+  testing::StrictMock<MockEnabledStateChangedObserver> observer;
+  TraceLog::GetInstance()->AddEnabledStateObserver(&observer);
+
+  EXPECT_CALL(observer, OnTraceLogEnabled())
+      .Times(0);
+  EXPECT_CALL(observer, OnTraceLogDisabled())
+      .Times(1);
+  TraceLog::GetInstance()->SetDisabled();
+  testing::Mock::VerifyAndClear(&observer);
+
+  // Cleanup.
+  TraceLog::GetInstance()->RemoveEnabledStateObserver(&observer);
+  TraceLog::GetInstance()->SetDisabled();
+}
+
+TEST_F(TraceEventTestFixture, EnabledObserverFiresOnDisable) {
+  TraceLog::GetInstance()->SetEnabled(TraceConfig(kRecordAllCategoryFilter, ""),
+                                      TraceLog::RECORDING_MODE);
+
+  MockEnabledStateChangedObserver observer;
+  TraceLog::GetInstance()->AddEnabledStateObserver(&observer);
+
+  EXPECT_CALL(observer, OnTraceLogDisabled())
+      .Times(1);
+  TraceLog::GetInstance()->SetDisabled();
+  testing::Mock::VerifyAndClear(&observer);
+
+  // Cleanup.
+  TraceLog::GetInstance()->RemoveEnabledStateObserver(&observer);
+}
+
+// Tests the IsEnabled() state of TraceLog changes before callbacks.
+class AfterStateChangeEnabledStateObserver
+    : public TraceLog::EnabledStateObserver {
+ public:
+  AfterStateChangeEnabledStateObserver() = default;
+  ~AfterStateChangeEnabledStateObserver() override = default;
+
+  // TraceLog::EnabledStateObserver overrides:
+  void OnTraceLogEnabled() override {
+    EXPECT_TRUE(TraceLog::GetInstance()->IsEnabled());
+  }
+
+  void OnTraceLogDisabled() override {
+    EXPECT_FALSE(TraceLog::GetInstance()->IsEnabled());
+  }
+};
+
+TEST_F(TraceEventTestFixture, ObserversFireAfterStateChange) {
+  AfterStateChangeEnabledStateObserver observer;
+  TraceLog::GetInstance()->AddEnabledStateObserver(&observer);
+
+  TraceLog::GetInstance()->SetEnabled(TraceConfig(kRecordAllCategoryFilter, ""),
+                                      TraceLog::RECORDING_MODE);
+  EXPECT_TRUE(TraceLog::GetInstance()->IsEnabled());
+
+  TraceLog::GetInstance()->SetDisabled();
+  EXPECT_FALSE(TraceLog::GetInstance()->IsEnabled());
+
+  TraceLog::GetInstance()->RemoveEnabledStateObserver(&observer);
+}
+
+// Tests that a state observer can remove itself during a callback.
+class SelfRemovingEnabledStateObserver
+    : public TraceLog::EnabledStateObserver {
+ public:
+  SelfRemovingEnabledStateObserver() = default;
+  ~SelfRemovingEnabledStateObserver() override = default;
+
+  // TraceLog::EnabledStateObserver overrides:
+  void OnTraceLogEnabled() override {}
+
+  void OnTraceLogDisabled() override {
+    TraceLog::GetInstance()->RemoveEnabledStateObserver(this);
+  }
+};
+
+TEST_F(TraceEventTestFixture, SelfRemovingObserver) {
+  ASSERT_EQ(0u, TraceLog::GetInstance()->GetObserverCountForTest());
+
+  SelfRemovingEnabledStateObserver observer;
+  TraceLog::GetInstance()->AddEnabledStateObserver(&observer);
+  EXPECT_EQ(1u, TraceLog::GetInstance()->GetObserverCountForTest());
+
+  TraceLog::GetInstance()->SetEnabled(TraceConfig(kRecordAllCategoryFilter, ""),
+                                      TraceLog::RECORDING_MODE);
+  TraceLog::GetInstance()->SetDisabled();
+  // The observer removed itself on disable.
+  EXPECT_EQ(0u, TraceLog::GetInstance()->GetObserverCountForTest());
+}
+
+bool IsNewTrace() {
+  bool is_new_trace;
+  TRACE_EVENT_IS_NEW_TRACE(&is_new_trace);
+  return is_new_trace;
+}
+
+TEST_F(TraceEventTestFixture, NewTraceRecording) {
+  ASSERT_FALSE(IsNewTrace());
+  TraceLog::GetInstance()->SetEnabled(TraceConfig(kRecordAllCategoryFilter, ""),
+                                      TraceLog::RECORDING_MODE);
+  // First call to IsNewTrace() should succeed. But, the second shouldn't.
+  ASSERT_TRUE(IsNewTrace());
+  ASSERT_FALSE(IsNewTrace());
+  EndTraceAndFlush();
+
+  // IsNewTrace() should definitely be false now.
+  ASSERT_FALSE(IsNewTrace());
+
+  // Start another trace. IsNewTrace() should become true again, briefly, as
+  // before.
+  TraceLog::GetInstance()->SetEnabled(TraceConfig(kRecordAllCategoryFilter, ""),
+                                      TraceLog::RECORDING_MODE);
+  ASSERT_TRUE(IsNewTrace());
+  ASSERT_FALSE(IsNewTrace());
+
+  // Cleanup.
+  EndTraceAndFlush();
+}
+
+TEST_F(TraceEventTestFixture, TestTraceFlush) {
+  size_t min_traces = 1;
+  size_t max_traces = 1;
+  do {
+    max_traces *= 2;
+    TraceLog::GetInstance()->SetEnabled(TraceConfig(),
+                                        TraceLog::RECORDING_MODE);
+    for (size_t i = 0; i < max_traces; i++) {
+      TRACE_EVENT_INSTANT0("x", "y", TRACE_EVENT_SCOPE_THREAD);
+    }
+    EndTraceAndFlush();
+  } while (num_flush_callbacks_ < 2);
+
+  while (min_traces + 50 <  max_traces) {
+    size_t traces = (min_traces + max_traces) / 2;
+    TraceLog::GetInstance()->SetEnabled(TraceConfig(),
+                                        TraceLog::RECORDING_MODE);
+    for (size_t i = 0; i < traces; i++) {
+      TRACE_EVENT_INSTANT0("x", "y", TRACE_EVENT_SCOPE_THREAD);
+    }
+    EndTraceAndFlush();
+    if (num_flush_callbacks_ < 2) {
+      min_traces = traces - 10;
+    } else {
+      max_traces = traces + 10;
+    }
+  }
+
+  for (size_t traces = min_traces; traces < max_traces; traces++) {
+    TraceLog::GetInstance()->SetEnabled(TraceConfig(),
+                                        TraceLog::RECORDING_MODE);
+    for (size_t i = 0; i < traces; i++) {
+      TRACE_EVENT_INSTANT0("x", "y", TRACE_EVENT_SCOPE_THREAD);
+    }
+    EndTraceAndFlush();
+  }
+}
+
+TEST_F(TraceEventTestFixture, AddMetadataEvent) {
+  int num_calls = 0;
+
+  class Convertable : public ConvertableToTraceFormat {
+   public:
+    explicit Convertable(int* num_calls) : num_calls_(num_calls) {}
+    ~Convertable() override = default;
+    void AppendAsTraceFormat(std::string* out) const override {
+      (*num_calls_)++;
+      out->append("\"metadata_value\"");
+    }
+
+   private:
+    int* num_calls_;
+  };
+
+  std::unique_ptr<ConvertableToTraceFormat> conv1(new Convertable(&num_calls));
+  std::unique_ptr<Convertable> conv2(new Convertable(&num_calls));
+
+  BeginTrace();
+  TRACE_EVENT_API_ADD_METADATA_EVENT(
+      TraceLog::GetCategoryGroupEnabled("__metadata"), "metadata_event_1",
+      "metadata_arg_name", std::move(conv1));
+  TRACE_EVENT_API_ADD_METADATA_EVENT(
+      TraceLog::GetCategoryGroupEnabled("__metadata"), "metadata_event_2",
+      "metadata_arg_name", std::move(conv2));
+  // |AppendAsTraceFormat| should only be called on flush, not when the event
+  // is added.
+  ASSERT_EQ(0, num_calls);
+  EndTraceAndFlush();
+  ASSERT_EQ(2, num_calls);
+  EXPECT_TRUE(FindNamePhaseKeyValue("metadata_event_1", "M",
+                                    "metadata_arg_name", "metadata_value"));
+  EXPECT_TRUE(FindNamePhaseKeyValue("metadata_event_2", "M",
+                                    "metadata_arg_name", "metadata_value"));
+
+  // The metadata event should only be adde to the current trace. In this new
+  // trace, the event should not appear.
+  BeginTrace();
+  EndTraceAndFlush();
+  ASSERT_EQ(2, num_calls);
+}
+
+// Test that categories work.
+TEST_F(TraceEventTestFixture, Categories) {
+  // Test that categories that are used can be retrieved whether trace was
+  // enabled or disabled when the trace event was encountered.
+  TRACE_EVENT_INSTANT0("c1", "name", TRACE_EVENT_SCOPE_THREAD);
+  TRACE_EVENT_INSTANT0("c2", "name", TRACE_EVENT_SCOPE_THREAD);
+  BeginTrace();
+  TRACE_EVENT_INSTANT0("c3", "name", TRACE_EVENT_SCOPE_THREAD);
+  TRACE_EVENT_INSTANT0("c4", "name", TRACE_EVENT_SCOPE_THREAD);
+  // Category groups containing more than one category.
+  TRACE_EVENT_INSTANT0("c5,c6", "name", TRACE_EVENT_SCOPE_THREAD);
+  TRACE_EVENT_INSTANT0("c7,c8", "name", TRACE_EVENT_SCOPE_THREAD);
+  TRACE_EVENT_INSTANT0(TRACE_DISABLED_BY_DEFAULT("c9"), "name",
+                       TRACE_EVENT_SCOPE_THREAD);
+
+  EndTraceAndFlush();
+  std::vector<std::string> cat_groups;
+  TraceLog::GetInstance()->GetKnownCategoryGroups(&cat_groups);
+  EXPECT_TRUE(ContainsValue(cat_groups, "c1"));
+  EXPECT_TRUE(ContainsValue(cat_groups, "c2"));
+  EXPECT_TRUE(ContainsValue(cat_groups, "c3"));
+  EXPECT_TRUE(ContainsValue(cat_groups, "c4"));
+  EXPECT_TRUE(ContainsValue(cat_groups, "c5,c6"));
+  EXPECT_TRUE(ContainsValue(cat_groups, "c7,c8"));
+  EXPECT_TRUE(ContainsValue(cat_groups, "disabled-by-default-c9"));
+  // Make sure metadata isn't returned.
+  EXPECT_FALSE(ContainsValue(cat_groups, "__metadata"));
+
+  const std::vector<std::string> empty_categories;
+  std::vector<std::string> included_categories;
+  std::vector<std::string> excluded_categories;
+
+  // Test that category filtering works.
+
+  // Include nonexistent category -> no events
+  Clear();
+  included_categories.clear();
+  TraceLog::GetInstance()->SetEnabled(TraceConfig("not_found823564786", ""),
+                                      TraceLog::RECORDING_MODE);
+  TRACE_EVENT_INSTANT0("cat1", "name", TRACE_EVENT_SCOPE_THREAD);
+  TRACE_EVENT_INSTANT0("cat2", "name", TRACE_EVENT_SCOPE_THREAD);
+  EndTraceAndFlush();
+  DropTracedMetadataRecords();
+  EXPECT_TRUE(trace_parsed_.empty());
+
+  // Include existent category -> only events of that category
+  Clear();
+  included_categories.clear();
+  TraceLog::GetInstance()->SetEnabled(TraceConfig("inc", ""),
+                                      TraceLog::RECORDING_MODE);
+  TRACE_EVENT_INSTANT0("inc", "name", TRACE_EVENT_SCOPE_THREAD);
+  TRACE_EVENT_INSTANT0("inc2", "name", TRACE_EVENT_SCOPE_THREAD);
+  EndTraceAndFlush();
+  DropTracedMetadataRecords();
+  EXPECT_TRUE(FindMatchingValue("cat", "inc"));
+  EXPECT_FALSE(FindNonMatchingValue("cat", "inc"));
+
+  // Include existent wildcard -> all categories matching wildcard
+  Clear();
+  included_categories.clear();
+  TraceLog::GetInstance()->SetEnabled(
+      TraceConfig("inc_wildcard_*,inc_wildchar_?_end", ""),
+      TraceLog::RECORDING_MODE);
+  TRACE_EVENT_INSTANT0("inc_wildcard_abc", "included",
+      TRACE_EVENT_SCOPE_THREAD);
+  TRACE_EVENT_INSTANT0("inc_wildcard_", "included", TRACE_EVENT_SCOPE_THREAD);
+  TRACE_EVENT_INSTANT0("inc_wildchar_x_end", "included",
+      TRACE_EVENT_SCOPE_THREAD);
+  TRACE_EVENT_INSTANT0("inc_wildchar_bla_end", "not_inc",
+      TRACE_EVENT_SCOPE_THREAD);
+  TRACE_EVENT_INSTANT0("cat1", "not_inc", TRACE_EVENT_SCOPE_THREAD);
+  TRACE_EVENT_INSTANT0("cat2", "not_inc", TRACE_EVENT_SCOPE_THREAD);
+  TRACE_EVENT_INSTANT0("inc_wildcard_category,other_category", "included",
+      TRACE_EVENT_SCOPE_THREAD);
+  TRACE_EVENT_INSTANT0(
+      "non_included_category,inc_wildcard_category", "included",
+      TRACE_EVENT_SCOPE_THREAD);
+  EndTraceAndFlush();
+  EXPECT_TRUE(FindMatchingValue("cat", "inc_wildcard_abc"));
+  EXPECT_TRUE(FindMatchingValue("cat", "inc_wildcard_"));
+  EXPECT_TRUE(FindMatchingValue("cat", "inc_wildchar_x_end"));
+  EXPECT_FALSE(FindMatchingValue("name", "not_inc"));
+  EXPECT_TRUE(FindMatchingValue("cat", "inc_wildcard_category,other_category"));
+  EXPECT_TRUE(FindMatchingValue("cat",
+                                "non_included_category,inc_wildcard_category"));
+
+  included_categories.clear();
+
+  // Exclude nonexistent category -> all events
+  Clear();
+  TraceLog::GetInstance()->SetEnabled(TraceConfig("-not_found823564786", ""),
+                                      TraceLog::RECORDING_MODE);
+  TRACE_EVENT_INSTANT0("cat1", "name", TRACE_EVENT_SCOPE_THREAD);
+  TRACE_EVENT_INSTANT0("cat2", "name", TRACE_EVENT_SCOPE_THREAD);
+  TRACE_EVENT_INSTANT0("category1,category2", "name", TRACE_EVENT_SCOPE_THREAD);
+  EndTraceAndFlush();
+  EXPECT_TRUE(FindMatchingValue("cat", "cat1"));
+  EXPECT_TRUE(FindMatchingValue("cat", "cat2"));
+  EXPECT_TRUE(FindMatchingValue("cat", "category1,category2"));
+
+  // Exclude existent category -> only events of other categories
+  Clear();
+  TraceLog::GetInstance()->SetEnabled(TraceConfig("-inc", ""),
+                                      TraceLog::RECORDING_MODE);
+  TRACE_EVENT_INSTANT0("inc", "name", TRACE_EVENT_SCOPE_THREAD);
+  TRACE_EVENT_INSTANT0("inc2", "name", TRACE_EVENT_SCOPE_THREAD);
+  TRACE_EVENT_INSTANT0("inc2,inc", "name", TRACE_EVENT_SCOPE_THREAD);
+  TRACE_EVENT_INSTANT0("inc,inc2", "name", TRACE_EVENT_SCOPE_THREAD);
+  EndTraceAndFlush();
+  EXPECT_TRUE(FindMatchingValue("cat", "inc2"));
+  EXPECT_FALSE(FindMatchingValue("cat", "inc"));
+  EXPECT_TRUE(FindMatchingValue("cat", "inc2,inc"));
+  EXPECT_TRUE(FindMatchingValue("cat", "inc,inc2"));
+
+  // Exclude existent wildcard -> all categories not matching wildcard
+  Clear();
+  TraceLog::GetInstance()->SetEnabled(
+      TraceConfig("-inc_wildcard_*,-inc_wildchar_?_end", ""),
+      TraceLog::RECORDING_MODE);
+  TRACE_EVENT_INSTANT0("inc_wildcard_abc", "not_inc",
+      TRACE_EVENT_SCOPE_THREAD);
+  TRACE_EVENT_INSTANT0("inc_wildcard_", "not_inc",
+      TRACE_EVENT_SCOPE_THREAD);
+  TRACE_EVENT_INSTANT0("inc_wildchar_x_end", "not_inc",
+      TRACE_EVENT_SCOPE_THREAD);
+  TRACE_EVENT_INSTANT0("inc_wildchar_bla_end", "included",
+      TRACE_EVENT_SCOPE_THREAD);
+  TRACE_EVENT_INSTANT0("cat1", "included", TRACE_EVENT_SCOPE_THREAD);
+  TRACE_EVENT_INSTANT0("cat2", "included", TRACE_EVENT_SCOPE_THREAD);
+  EndTraceAndFlush();
+  EXPECT_TRUE(FindMatchingValue("cat", "inc_wildchar_bla_end"));
+  EXPECT_TRUE(FindMatchingValue("cat", "cat1"));
+  EXPECT_TRUE(FindMatchingValue("cat", "cat2"));
+  EXPECT_FALSE(FindMatchingValue("name", "not_inc"));
+}
+
+
+// Test ASYNC_BEGIN/END events
+TEST_F(TraceEventTestFixture, AsyncBeginEndEvents) {
+  BeginTrace();
+
+  unsigned long long id = 0xfeedbeeffeedbeefull;
+  TRACE_EVENT_ASYNC_BEGIN0("cat", "name1", id);
+  TRACE_EVENT_ASYNC_STEP_INTO0("cat", "name1", id, "step1");
+  TRACE_EVENT_ASYNC_END0("cat", "name1", id);
+  TRACE_EVENT_BEGIN0("cat", "name2");
+  TRACE_EVENT_ASYNC_BEGIN0("cat", "name3", 0);
+  TRACE_EVENT_ASYNC_STEP_PAST0("cat", "name3", 0, "step2");
+
+  EndTraceAndFlush();
+
+  EXPECT_TRUE(FindNamePhase("name1", "S"));
+  EXPECT_TRUE(FindNamePhase("name1", "T"));
+  EXPECT_TRUE(FindNamePhase("name1", "F"));
+
+  std::string id_str;
+  StringAppendF(&id_str, "0x%llx", id);
+
+  EXPECT_TRUE(FindNamePhaseKeyValue("name1", "S", "id", id_str.c_str()));
+  EXPECT_TRUE(FindNamePhaseKeyValue("name1", "T", "id", id_str.c_str()));
+  EXPECT_TRUE(FindNamePhaseKeyValue("name1", "F", "id", id_str.c_str()));
+  EXPECT_TRUE(FindNamePhaseKeyValue("name3", "S", "id", "0x0"));
+  EXPECT_TRUE(FindNamePhaseKeyValue("name3", "p", "id", "0x0"));
+
+  // BEGIN events should not have id
+  EXPECT_FALSE(FindNamePhaseKeyValue("name2", "B", "id", "0"));
+}
+
+// Test ASYNC_BEGIN/END events
+TEST_F(TraceEventTestFixture, AsyncBeginEndPointerMangling) {
+  void* ptr = this;
+
+  TraceLog::GetInstance()->SetProcessID(100);
+  BeginTrace();
+  TRACE_EVENT_ASYNC_BEGIN0("cat", "name1", ptr);
+  TRACE_EVENT_ASYNC_BEGIN0("cat", "name2", ptr);
+  EndTraceAndFlush();
+
+  TraceLog::GetInstance()->SetProcessID(200);
+  BeginTrace();
+  TRACE_EVENT_ASYNC_END0("cat", "name1", ptr);
+  EndTraceAndFlush();
+
+  DictionaryValue* async_begin = FindNamePhase("name1", "S");
+  DictionaryValue* async_begin2 = FindNamePhase("name2", "S");
+  DictionaryValue* async_end = FindNamePhase("name1", "F");
+  EXPECT_TRUE(async_begin);
+  EXPECT_TRUE(async_begin2);
+  EXPECT_TRUE(async_end);
+
+  Value* value = nullptr;
+  std::string async_begin_id_str;
+  std::string async_begin2_id_str;
+  std::string async_end_id_str;
+  ASSERT_TRUE(async_begin->Get("id", &value));
+  ASSERT_TRUE(value->GetAsString(&async_begin_id_str));
+  ASSERT_TRUE(async_begin2->Get("id", &value));
+  ASSERT_TRUE(value->GetAsString(&async_begin2_id_str));
+  ASSERT_TRUE(async_end->Get("id", &value));
+  ASSERT_TRUE(value->GetAsString(&async_end_id_str));
+
+  EXPECT_STREQ(async_begin_id_str.c_str(), async_begin2_id_str.c_str());
+  EXPECT_STRNE(async_begin_id_str.c_str(), async_end_id_str.c_str());
+}
+
+// Test that static strings are not copied.
+TEST_F(TraceEventTestFixture, StaticStringVsString) {
+  TraceLog* tracer = TraceLog::GetInstance();
+  // Make sure old events are flushed:
+  EXPECT_EQ(0u, tracer->GetStatus().event_count);
+  const unsigned char* category_group_enabled =
+      TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED("cat");
+
+  {
+    BeginTrace();
+    // Test that string arguments are copied.
+    TraceEventHandle handle1 =
+        trace_event_internal::AddTraceEvent(
+            TRACE_EVENT_PHASE_INSTANT, category_group_enabled, "name1",
+            trace_event_internal::kGlobalScope, trace_event_internal::kNoId,
+            0, trace_event_internal::kNoId,
+            "arg1", std::string("argval"), "arg2", std::string("argval"));
+    // Test that static TRACE_STR_COPY string arguments are copied.
+    TraceEventHandle handle2 =
+        trace_event_internal::AddTraceEvent(
+            TRACE_EVENT_PHASE_INSTANT, category_group_enabled, "name2",
+            trace_event_internal::kGlobalScope, trace_event_internal::kNoId,
+            0, trace_event_internal::kNoId,
+            "arg1", TRACE_STR_COPY("argval"),
+            "arg2", TRACE_STR_COPY("argval"));
+    EXPECT_GT(tracer->GetStatus().event_count, 1u);
+    const TraceEvent* event1 = tracer->GetEventByHandle(handle1);
+    const TraceEvent* event2 = tracer->GetEventByHandle(handle2);
+    ASSERT_TRUE(event1);
+    ASSERT_TRUE(event2);
+    EXPECT_STREQ("name1", event1->name());
+    EXPECT_STREQ("name2", event2->name());
+    EXPECT_TRUE(event1->parameter_copy_storage() != nullptr);
+    EXPECT_TRUE(event2->parameter_copy_storage() != nullptr);
+    EXPECT_GT(event1->parameter_copy_storage()->size(), 0u);
+    EXPECT_GT(event2->parameter_copy_storage()->size(), 0u);
+    EndTraceAndFlush();
+  }
+
+  {
+    BeginTrace();
+    // Test that static literal string arguments are not copied.
+    TraceEventHandle handle1 =
+        trace_event_internal::AddTraceEvent(
+            TRACE_EVENT_PHASE_INSTANT, category_group_enabled, "name1",
+            trace_event_internal::kGlobalScope, trace_event_internal::kNoId,
+            0, trace_event_internal::kNoId,
+            "arg1", "argval", "arg2", "argval");
+    // Test that static TRACE_STR_COPY NULL string arguments are not copied.
+    const char* str1 = nullptr;
+    const char* str2 = nullptr;
+    TraceEventHandle handle2 =
+        trace_event_internal::AddTraceEvent(
+            TRACE_EVENT_PHASE_INSTANT, category_group_enabled, "name2",
+            trace_event_internal::kGlobalScope, trace_event_internal::kNoId,
+            0, trace_event_internal::kNoId,
+            "arg1", TRACE_STR_COPY(str1),
+            "arg2", TRACE_STR_COPY(str2));
+    EXPECT_GT(tracer->GetStatus().event_count, 1u);
+    const TraceEvent* event1 = tracer->GetEventByHandle(handle1);
+    const TraceEvent* event2 = tracer->GetEventByHandle(handle2);
+    ASSERT_TRUE(event1);
+    ASSERT_TRUE(event2);
+    EXPECT_STREQ("name1", event1->name());
+    EXPECT_STREQ("name2", event2->name());
+    EXPECT_TRUE(event1->parameter_copy_storage() == nullptr);
+    EXPECT_TRUE(event2->parameter_copy_storage() == nullptr);
+    EndTraceAndFlush();
+  }
+}
+
+// Test that data sent from other threads is gathered
+TEST_F(TraceEventTestFixture, DataCapturedOnThread) {
+  BeginTrace();
+
+  Thread thread("1");
+  WaitableEvent task_complete_event(WaitableEvent::ResetPolicy::AUTOMATIC,
+                                    WaitableEvent::InitialState::NOT_SIGNALED);
+  thread.Start();
+
+  thread.task_runner()->PostTask(
+      FROM_HERE,
+      base::BindOnce(&TraceWithAllMacroVariants, &task_complete_event));
+  task_complete_event.Wait();
+  thread.Stop();
+
+  EndTraceAndFlush();
+  ValidateAllTraceMacrosCreatedData(trace_parsed_);
+}
+
+// Test that data sent from multiple threads is gathered
+TEST_F(TraceEventTestFixture, DataCapturedManyThreads) {
+  BeginTrace();
+
+  const int num_threads = 4;
+  const int num_events = 4000;
+  Thread* threads[num_threads];
+  WaitableEvent* task_complete_events[num_threads];
+  for (int i = 0; i < num_threads; i++) {
+    threads[i] = new Thread(StringPrintf("Thread %d", i));
+    task_complete_events[i] =
+        new WaitableEvent(WaitableEvent::ResetPolicy::AUTOMATIC,
+                          WaitableEvent::InitialState::NOT_SIGNALED);
+    threads[i]->Start();
+    threads[i]->task_runner()->PostTask(
+        FROM_HERE, base::BindOnce(&TraceManyInstantEvents, i, num_events,
+                                  task_complete_events[i]));
+  }
+
+  for (int i = 0; i < num_threads; i++) {
+    task_complete_events[i]->Wait();
+  }
+
+  // Let half of the threads end before flush.
+  for (int i = 0; i < num_threads / 2; i++) {
+    threads[i]->Stop();
+    delete threads[i];
+    delete task_complete_events[i];
+  }
+
+  EndTraceAndFlushInThreadWithMessageLoop();
+  ValidateInstantEventPresentOnEveryThread(trace_parsed_,
+                                           num_threads, num_events);
+
+  // Let the other half of the threads end after flush.
+  for (int i = num_threads / 2; i < num_threads; i++) {
+    threads[i]->Stop();
+    delete threads[i];
+    delete task_complete_events[i];
+  }
+}
+
+// Test that thread and process names show up in the trace
+TEST_F(TraceEventTestFixture, ThreadNames) {
+  // Create threads before we enable tracing to make sure
+  // that tracelog still captures them.
+  const int kNumThreads = 4;
+  const int kNumEvents = 10;
+  Thread* threads[kNumThreads];
+  PlatformThreadId thread_ids[kNumThreads];
+  for (int i = 0; i < kNumThreads; i++)
+    threads[i] = new Thread(StringPrintf("Thread %d", i));
+
+  // Enable tracing.
+  BeginTrace();
+
+  // Now run some trace code on these threads.
+  WaitableEvent* task_complete_events[kNumThreads];
+  for (int i = 0; i < kNumThreads; i++) {
+    task_complete_events[i] =
+        new WaitableEvent(WaitableEvent::ResetPolicy::AUTOMATIC,
+                          WaitableEvent::InitialState::NOT_SIGNALED);
+    threads[i]->Start();
+    thread_ids[i] = threads[i]->GetThreadId();
+    threads[i]->task_runner()->PostTask(
+        FROM_HERE, base::BindOnce(&TraceManyInstantEvents, i, kNumEvents,
+                                  task_complete_events[i]));
+  }
+  for (int i = 0; i < kNumThreads; i++) {
+    task_complete_events[i]->Wait();
+  }
+
+  // Shut things down.
+  for (int i = 0; i < kNumThreads; i++) {
+    threads[i]->Stop();
+    delete threads[i];
+    delete task_complete_events[i];
+  }
+
+  EndTraceAndFlush();
+
+  std::string tmp;
+  int tmp_int;
+  const DictionaryValue* item;
+
+  // Make sure we get thread name metadata.
+  // Note, the test suite may have created a ton of threads.
+  // So, we'll have thread names for threads we didn't create.
+  std::vector<const DictionaryValue*> items =
+      FindTraceEntries(trace_parsed_, "thread_name");
+  for (int i = 0; i < static_cast<int>(items.size()); i++) {
+    item = items[i];
+    ASSERT_TRUE(item);
+    EXPECT_TRUE(item->GetInteger("tid", &tmp_int));
+
+    // See if this thread name is one of the threads we just created
+    for (int j = 0; j < kNumThreads; j++) {
+      if (static_cast<int>(thread_ids[j]) != tmp_int)
+        continue;
+
+      std::string expected_name = StringPrintf("Thread %d", j);
+      EXPECT_TRUE(item->GetString("ph", &tmp) && tmp == "M");
+      EXPECT_TRUE(item->GetInteger("pid", &tmp_int) &&
+                  tmp_int == static_cast<int>(base::GetCurrentProcId()));
+      // If the thread name changes or the tid gets reused, the name will be
+      // a comma-separated list of thread names, so look for a substring.
+      EXPECT_TRUE(item->GetString("args.name", &tmp) &&
+                  tmp.find(expected_name) != std::string::npos);
+    }
+  }
+}
+
+TEST_F(TraceEventTestFixture, ThreadNameChanges) {
+  BeginTrace();
+
+  PlatformThread::SetName("");
+  TRACE_EVENT_INSTANT0("drink", "water", TRACE_EVENT_SCOPE_THREAD);
+
+  PlatformThread::SetName("cafe");
+  TRACE_EVENT_INSTANT0("drink", "coffee", TRACE_EVENT_SCOPE_THREAD);
+
+  PlatformThread::SetName("shop");
+  // No event here, so won't appear in combined name.
+
+  PlatformThread::SetName("pub");
+  TRACE_EVENT_INSTANT0("drink", "beer", TRACE_EVENT_SCOPE_THREAD);
+  TRACE_EVENT_INSTANT0("drink", "wine", TRACE_EVENT_SCOPE_THREAD);
+
+  PlatformThread::SetName(" bar");
+  TRACE_EVENT_INSTANT0("drink", "whisky", TRACE_EVENT_SCOPE_THREAD);
+
+  EndTraceAndFlush();
+
+  std::vector<const DictionaryValue*> items =
+      FindTraceEntries(trace_parsed_, "thread_name");
+  EXPECT_EQ(1u, items.size());
+  ASSERT_GT(items.size(), 0u);
+  const DictionaryValue* item = items[0];
+  ASSERT_TRUE(item);
+  int tid;
+  EXPECT_TRUE(item->GetInteger("tid", &tid));
+  EXPECT_EQ(PlatformThread::CurrentId(), static_cast<PlatformThreadId>(tid));
+
+  std::string expected_name = "cafe,pub, bar";
+  std::string tmp;
+  EXPECT_TRUE(item->GetString("args.name", &tmp));
+  EXPECT_EQ(expected_name, tmp);
+}
+
+// Test that the disabled trace categories are included/excluded from the
+// trace output correctly.
+TEST_F(TraceEventTestFixture, DisabledCategories) {
+  BeginTrace();
+  TRACE_EVENT_INSTANT0(TRACE_DISABLED_BY_DEFAULT("cc"), "first",
+                       TRACE_EVENT_SCOPE_THREAD);
+  TRACE_EVENT_INSTANT0("included", "first", TRACE_EVENT_SCOPE_THREAD);
+  EndTraceAndFlush();
+  {
+    const DictionaryValue* item = nullptr;
+    ListValue& trace_parsed = trace_parsed_;
+    EXPECT_NOT_FIND_("disabled-by-default-cc");
+    EXPECT_FIND_("included");
+  }
+  Clear();
+
+  BeginSpecificTrace("disabled-by-default-cc");
+  TRACE_EVENT_INSTANT0(TRACE_DISABLED_BY_DEFAULT("cc"), "second",
+                       TRACE_EVENT_SCOPE_THREAD);
+  TRACE_EVENT_INSTANT0("other_included", "second", TRACE_EVENT_SCOPE_THREAD);
+  EndTraceAndFlush();
+
+  {
+    const DictionaryValue* item = nullptr;
+    ListValue& trace_parsed = trace_parsed_;
+    EXPECT_FIND_("disabled-by-default-cc");
+    EXPECT_FIND_("other_included");
+  }
+
+  Clear();
+
+  BeginSpecificTrace("other_included");
+  TRACE_EVENT_INSTANT0(TRACE_DISABLED_BY_DEFAULT("cc") ",other_included",
+                       "first", TRACE_EVENT_SCOPE_THREAD);
+  TRACE_EVENT_INSTANT0("other_included," TRACE_DISABLED_BY_DEFAULT("cc"),
+                       "second", TRACE_EVENT_SCOPE_THREAD);
+  EndTraceAndFlush();
+
+  {
+    const DictionaryValue* item = nullptr;
+    ListValue& trace_parsed = trace_parsed_;
+    EXPECT_FIND_("disabled-by-default-cc,other_included");
+    EXPECT_FIND_("other_included,disabled-by-default-cc");
+  }
+}
+
+TEST_F(TraceEventTestFixture, NormallyNoDeepCopy) {
+  // Test that the TRACE_EVENT macros do not deep-copy their string. If they
+  // do so it may indicate a performance regression, but more-over it would
+  // make the DEEP_COPY overloads redundant.
+  std::string name_string("event name");
+
+  BeginTrace();
+  TRACE_EVENT_INSTANT0("category", name_string.c_str(),
+                       TRACE_EVENT_SCOPE_THREAD);
+
+  // Modify the string in place (a wholesale reassignment may leave the old
+  // string intact on the heap).
+  name_string[0] = '@';
+
+  EndTraceAndFlush();
+
+  EXPECT_FALSE(FindTraceEntry(trace_parsed_, "event name"));
+  EXPECT_TRUE(FindTraceEntry(trace_parsed_, name_string.c_str()));
+}
+
+TEST_F(TraceEventTestFixture, DeepCopy) {
+  static const char kOriginalName1[] = "name1";
+  static const char kOriginalName2[] = "name2";
+  static const char kOriginalName3[] = "name3";
+  std::string name1(kOriginalName1);
+  std::string name2(kOriginalName2);
+  std::string name3(kOriginalName3);
+  std::string arg1("arg1");
+  std::string arg2("arg2");
+  std::string val1("val1");
+  std::string val2("val2");
+
+  BeginTrace();
+  TRACE_EVENT_COPY_INSTANT0("category", name1.c_str(),
+                            TRACE_EVENT_SCOPE_THREAD);
+  TRACE_EVENT_COPY_BEGIN1("category", name2.c_str(),
+                          arg1.c_str(), 5);
+  TRACE_EVENT_COPY_END2("category", name3.c_str(),
+                        arg1.c_str(), val1,
+                        arg2.c_str(), val2);
+
+  // As per NormallyNoDeepCopy, modify the strings in place.
+  name1[0] = name2[0] = name3[0] = arg1[0] = arg2[0] = val1[0] = val2[0] = '@';
+
+  EndTraceAndFlush();
+
+  EXPECT_FALSE(FindTraceEntry(trace_parsed_, name1.c_str()));
+  EXPECT_FALSE(FindTraceEntry(trace_parsed_, name2.c_str()));
+  EXPECT_FALSE(FindTraceEntry(trace_parsed_, name3.c_str()));
+
+  const DictionaryValue* entry1 = FindTraceEntry(trace_parsed_, kOriginalName1);
+  const DictionaryValue* entry2 = FindTraceEntry(trace_parsed_, kOriginalName2);
+  const DictionaryValue* entry3 = FindTraceEntry(trace_parsed_, kOriginalName3);
+  ASSERT_TRUE(entry1);
+  ASSERT_TRUE(entry2);
+  ASSERT_TRUE(entry3);
+
+  int i;
+  EXPECT_FALSE(entry2->GetInteger("args.@rg1", &i));
+  EXPECT_TRUE(entry2->GetInteger("args.arg1", &i));
+  EXPECT_EQ(5, i);
+
+  std::string s;
+  EXPECT_TRUE(entry3->GetString("args.arg1", &s));
+  EXPECT_EQ("val1", s);
+  EXPECT_TRUE(entry3->GetString("args.arg2", &s));
+  EXPECT_EQ("val2", s);
+}
+
+// Test that TraceResultBuffer outputs the correct result whether it is added
+// in chunks or added all at once.
+TEST_F(TraceEventTestFixture, TraceResultBuffer) {
+  Clear();
+
+  trace_buffer_.Start();
+  trace_buffer_.AddFragment("bla1");
+  trace_buffer_.AddFragment("bla2");
+  trace_buffer_.AddFragment("bla3,bla4");
+  trace_buffer_.Finish();
+  EXPECT_STREQ(json_output_.json_output.c_str(), "[bla1,bla2,bla3,bla4]");
+
+  Clear();
+
+  trace_buffer_.Start();
+  trace_buffer_.AddFragment("bla1,bla2,bla3,bla4");
+  trace_buffer_.Finish();
+  EXPECT_STREQ(json_output_.json_output.c_str(), "[bla1,bla2,bla3,bla4]");
+}
+
+// Test that trace_event parameters are not evaluated if the tracing
+// system is disabled.
+TEST_F(TraceEventTestFixture, TracingIsLazy) {
+  BeginTrace();
+
+  int a = 0;
+  TRACE_EVENT_INSTANT1("category", "test", TRACE_EVENT_SCOPE_THREAD, "a", a++);
+  EXPECT_EQ(1, a);
+
+  TraceLog::GetInstance()->SetDisabled();
+
+  TRACE_EVENT_INSTANT1("category", "test", TRACE_EVENT_SCOPE_THREAD, "a", a++);
+  EXPECT_EQ(1, a);
+
+  EndTraceAndFlush();
+}
+
+TEST_F(TraceEventTestFixture, TraceEnableDisable) {
+  TraceLog* trace_log = TraceLog::GetInstance();
+  TraceConfig tc_inc_all("*", "");
+  trace_log->SetEnabled(tc_inc_all, TraceLog::RECORDING_MODE);
+  EXPECT_TRUE(trace_log->IsEnabled());
+  trace_log->SetDisabled();
+  EXPECT_FALSE(trace_log->IsEnabled());
+
+  trace_log->SetEnabled(tc_inc_all, TraceLog::RECORDING_MODE);
+  EXPECT_TRUE(trace_log->IsEnabled());
+  const std::vector<std::string> empty;
+  trace_log->SetEnabled(TraceConfig(), TraceLog::RECORDING_MODE);
+  EXPECT_TRUE(trace_log->IsEnabled());
+  trace_log->SetDisabled();
+  EXPECT_FALSE(trace_log->IsEnabled());
+  trace_log->SetDisabled();
+  EXPECT_FALSE(trace_log->IsEnabled());
+}
+
+TEST_F(TraceEventTestFixture, TraceCategoriesAfterNestedEnable) {
+  TraceLog* trace_log = TraceLog::GetInstance();
+  trace_log->SetEnabled(TraceConfig("foo,bar", ""), TraceLog::RECORDING_MODE);
+  EXPECT_TRUE(*trace_log->GetCategoryGroupEnabled("foo"));
+  EXPECT_TRUE(*trace_log->GetCategoryGroupEnabled("bar"));
+  EXPECT_FALSE(*trace_log->GetCategoryGroupEnabled("baz"));
+  trace_log->SetEnabled(TraceConfig("foo2", ""), TraceLog::RECORDING_MODE);
+  EXPECT_TRUE(*trace_log->GetCategoryGroupEnabled("foo2"));
+  EXPECT_FALSE(*trace_log->GetCategoryGroupEnabled("baz"));
+  // The "" becomes the default catergory set when applied.
+  trace_log->SetEnabled(TraceConfig(), TraceLog::RECORDING_MODE);
+  EXPECT_TRUE(*trace_log->GetCategoryGroupEnabled("foo"));
+  EXPECT_TRUE(*trace_log->GetCategoryGroupEnabled("baz"));
+  EXPECT_STREQ(
+    "",
+    trace_log->GetCurrentTraceConfig().ToCategoryFilterString().c_str());
+  trace_log->SetDisabled();
+  trace_log->SetDisabled();
+  trace_log->SetDisabled();
+  EXPECT_FALSE(*trace_log->GetCategoryGroupEnabled("foo"));
+  EXPECT_FALSE(*trace_log->GetCategoryGroupEnabled("baz"));
+
+  trace_log->SetEnabled(TraceConfig("-foo,-bar", ""), TraceLog::RECORDING_MODE);
+  EXPECT_FALSE(*trace_log->GetCategoryGroupEnabled("foo"));
+  EXPECT_TRUE(*trace_log->GetCategoryGroupEnabled("baz"));
+  trace_log->SetEnabled(TraceConfig("moo", ""), TraceLog::RECORDING_MODE);
+  EXPECT_TRUE(*trace_log->GetCategoryGroupEnabled("baz"));
+  EXPECT_TRUE(*trace_log->GetCategoryGroupEnabled("moo"));
+  EXPECT_FALSE(*trace_log->GetCategoryGroupEnabled("foo"));
+  EXPECT_STREQ(
+    "-foo,-bar",
+    trace_log->GetCurrentTraceConfig().ToCategoryFilterString().c_str());
+  trace_log->SetDisabled();
+  trace_log->SetDisabled();
+
+  // Make sure disabled categories aren't cleared if we set in the second.
+  trace_log->SetEnabled(TraceConfig("disabled-by-default-cc,foo", ""),
+                        TraceLog::RECORDING_MODE);
+  EXPECT_FALSE(*trace_log->GetCategoryGroupEnabled("bar"));
+  trace_log->SetEnabled(TraceConfig("disabled-by-default-gpu", ""),
+                        TraceLog::RECORDING_MODE);
+  EXPECT_TRUE(*trace_log->GetCategoryGroupEnabled("disabled-by-default-cc"));
+  EXPECT_TRUE(*trace_log->GetCategoryGroupEnabled("disabled-by-default-gpu"));
+  EXPECT_TRUE(*trace_log->GetCategoryGroupEnabled("bar"));
+  EXPECT_STREQ(
+    "disabled-by-default-cc,disabled-by-default-gpu",
+    trace_log->GetCurrentTraceConfig().ToCategoryFilterString().c_str());
+  trace_log->SetDisabled();
+  trace_log->SetDisabled();
+}
+
+TEST_F(TraceEventTestFixture, TraceWithDefaultCategoryFilters) {
+  TraceLog* trace_log = TraceLog::GetInstance();
+
+  trace_log->SetEnabled(TraceConfig(), TraceLog::RECORDING_MODE);
+  CheckTraceDefaultCategoryFilters(*trace_log);
+  trace_log->SetDisabled();
+
+  trace_log->SetEnabled(TraceConfig("", ""), TraceLog::RECORDING_MODE);
+  CheckTraceDefaultCategoryFilters(*trace_log);
+  trace_log->SetDisabled();
+
+  trace_log->SetEnabled(TraceConfig("*", ""), TraceLog::RECORDING_MODE);
+  CheckTraceDefaultCategoryFilters(*trace_log);
+  trace_log->SetDisabled();
+
+  trace_log->SetEnabled(TraceConfig(""), TraceLog::RECORDING_MODE);
+  CheckTraceDefaultCategoryFilters(*trace_log);
+  trace_log->SetDisabled();
+}
+
+TEST_F(TraceEventTestFixture, TraceWithDisabledByDefaultCategoryFilters) {
+  TraceLog* trace_log = TraceLog::GetInstance();
+
+  trace_log->SetEnabled(TraceConfig("foo,disabled-by-default-foo", ""),
+                        TraceLog::RECORDING_MODE);
+  EXPECT_TRUE(*trace_log->GetCategoryGroupEnabled("foo"));
+  EXPECT_TRUE(*trace_log->GetCategoryGroupEnabled("disabled-by-default-foo"));
+  EXPECT_FALSE(*trace_log->GetCategoryGroupEnabled("bar"));
+  EXPECT_FALSE(*trace_log->GetCategoryGroupEnabled("disabled-by-default-bar"));
+  trace_log->SetDisabled();
+
+  // Enabling only the disabled-by-default-* category means the default ones
+  // are also enabled.
+  trace_log->SetEnabled(TraceConfig("disabled-by-default-foo", ""),
+                        TraceLog::RECORDING_MODE);
+  EXPECT_TRUE(*trace_log->GetCategoryGroupEnabled("disabled-by-default-foo"));
+  EXPECT_TRUE(*trace_log->GetCategoryGroupEnabled("foo"));
+  EXPECT_TRUE(*trace_log->GetCategoryGroupEnabled("bar"));
+  EXPECT_FALSE(*trace_log->GetCategoryGroupEnabled("disabled-by-default-bar"));
+  trace_log->SetDisabled();
+}
+
+class MyData : public ConvertableToTraceFormat {
+ public:
+  MyData() = default;
+  ~MyData() override = default;
+
+  void AppendAsTraceFormat(std::string* out) const override {
+    out->append("{\"foo\":1}");
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MyData);
+};
+
+TEST_F(TraceEventTestFixture, ConvertableTypes) {
+  TraceLog::GetInstance()->SetEnabled(TraceConfig(kRecordAllCategoryFilter, ""),
+                                      TraceLog::RECORDING_MODE);
+
+  std::unique_ptr<ConvertableToTraceFormat> data(new MyData());
+  std::unique_ptr<ConvertableToTraceFormat> data1(new MyData());
+  std::unique_ptr<ConvertableToTraceFormat> data2(new MyData());
+  TRACE_EVENT1("foo", "bar", "data", std::move(data));
+  TRACE_EVENT2("foo", "baz", "data1", std::move(data1), "data2",
+               std::move(data2));
+
+  // Check that std::unique_ptr<DerivedClassOfConvertable> are properly treated
+  // as
+  // convertable and not accidentally casted to bool.
+  std::unique_ptr<MyData> convertData1(new MyData());
+  std::unique_ptr<MyData> convertData2(new MyData());
+  std::unique_ptr<MyData> convertData3(new MyData());
+  std::unique_ptr<MyData> convertData4(new MyData());
+  TRACE_EVENT2("foo", "string_first", "str", "string value 1", "convert",
+               std::move(convertData1));
+  TRACE_EVENT2("foo", "string_second", "convert", std::move(convertData2),
+               "str", "string value 2");
+  TRACE_EVENT2("foo", "both_conv", "convert1", std::move(convertData3),
+               "convert2", std::move(convertData4));
+  EndTraceAndFlush();
+
+  // One arg version.
+  DictionaryValue* dict = FindNamePhase("bar", "X");
+  ASSERT_TRUE(dict);
+
+  const DictionaryValue* args_dict = nullptr;
+  dict->GetDictionary("args", &args_dict);
+  ASSERT_TRUE(args_dict);
+
+  const Value* value = nullptr;
+  const DictionaryValue* convertable_dict = nullptr;
+  EXPECT_TRUE(args_dict->Get("data", &value));
+  ASSERT_TRUE(value->GetAsDictionary(&convertable_dict));
+
+  int foo_val;
+  EXPECT_TRUE(convertable_dict->GetInteger("foo", &foo_val));
+  EXPECT_EQ(1, foo_val);
+
+  // Two arg version.
+  dict = FindNamePhase("baz", "X");
+  ASSERT_TRUE(dict);
+
+  args_dict = nullptr;
+  dict->GetDictionary("args", &args_dict);
+  ASSERT_TRUE(args_dict);
+
+  value = nullptr;
+  convertable_dict = nullptr;
+  EXPECT_TRUE(args_dict->Get("data1", &value));
+  ASSERT_TRUE(value->GetAsDictionary(&convertable_dict));
+
+  value = nullptr;
+  convertable_dict = nullptr;
+  EXPECT_TRUE(args_dict->Get("data2", &value));
+  ASSERT_TRUE(value->GetAsDictionary(&convertable_dict));
+
+  // Convertable with other types.
+  dict = FindNamePhase("string_first", "X");
+  ASSERT_TRUE(dict);
+
+  args_dict = nullptr;
+  dict->GetDictionary("args", &args_dict);
+  ASSERT_TRUE(args_dict);
+
+  std::string str_value;
+  EXPECT_TRUE(args_dict->GetString("str", &str_value));
+  EXPECT_STREQ("string value 1", str_value.c_str());
+
+  value = nullptr;
+  convertable_dict = nullptr;
+  foo_val = 0;
+  EXPECT_TRUE(args_dict->Get("convert", &value));
+  ASSERT_TRUE(value->GetAsDictionary(&convertable_dict));
+  EXPECT_TRUE(convertable_dict->GetInteger("foo", &foo_val));
+  EXPECT_EQ(1, foo_val);
+
+  dict = FindNamePhase("string_second", "X");
+  ASSERT_TRUE(dict);
+
+  args_dict = nullptr;
+  dict->GetDictionary("args", &args_dict);
+  ASSERT_TRUE(args_dict);
+
+  EXPECT_TRUE(args_dict->GetString("str", &str_value));
+  EXPECT_STREQ("string value 2", str_value.c_str());
+
+  value = nullptr;
+  convertable_dict = nullptr;
+  foo_val = 0;
+  EXPECT_TRUE(args_dict->Get("convert", &value));
+  ASSERT_TRUE(value->GetAsDictionary(&convertable_dict));
+  EXPECT_TRUE(convertable_dict->GetInteger("foo", &foo_val));
+  EXPECT_EQ(1, foo_val);
+
+  dict = FindNamePhase("both_conv", "X");
+  ASSERT_TRUE(dict);
+
+  args_dict = nullptr;
+  dict->GetDictionary("args", &args_dict);
+  ASSERT_TRUE(args_dict);
+
+  value = nullptr;
+  convertable_dict = nullptr;
+  foo_val = 0;
+  EXPECT_TRUE(args_dict->Get("convert1", &value));
+  ASSERT_TRUE(value->GetAsDictionary(&convertable_dict));
+  EXPECT_TRUE(args_dict->Get("convert2", &value));
+  ASSERT_TRUE(value->GetAsDictionary(&convertable_dict));
+}
+
+TEST_F(TraceEventTestFixture, PrimitiveArgs) {
+  TraceLog::GetInstance()->SetEnabled(TraceConfig(kRecordAllCategoryFilter, ""),
+                                      TraceLog::RECORDING_MODE);
+
+  TRACE_EVENT1("foo", "event1", "int_one", 1);
+  TRACE_EVENT1("foo", "event2", "int_neg_ten", -10);
+  TRACE_EVENT1("foo", "event3", "float_one", 1.0f);
+  TRACE_EVENT1("foo", "event4", "float_half", .5f);
+  TRACE_EVENT1("foo", "event5", "float_neghalf", -.5f);
+  TRACE_EVENT1("foo", "event6", "float_infinity",
+      std::numeric_limits<float>::infinity());
+  TRACE_EVENT1("foo", "event6b", "float_neg_infinity",
+      -std::numeric_limits<float>::infinity());
+  TRACE_EVENT1("foo", "event7", "double_nan",
+      std::numeric_limits<double>::quiet_NaN());
+  void* p = nullptr;
+  TRACE_EVENT1("foo", "event8", "pointer_null", p);
+  p = reinterpret_cast<void*>(0xbadf00d);
+  TRACE_EVENT1("foo", "event9", "pointer_badf00d", p);
+  TRACE_EVENT1("foo", "event10", "bool_true", true);
+  TRACE_EVENT1("foo", "event11", "bool_false", false);
+  TRACE_EVENT1("foo", "event12", "time_null",
+      base::Time());
+  TRACE_EVENT1("foo", "event13", "time_one",
+      base::Time::FromInternalValue(1));
+  TRACE_EVENT1("foo", "event14", "timeticks_null",
+      base::TimeTicks());
+  TRACE_EVENT1("foo", "event15", "timeticks_one",
+      base::TimeTicks::FromInternalValue(1));
+  EndTraceAndFlush();
+
+  const DictionaryValue* args_dict = nullptr;
+  DictionaryValue* dict = nullptr;
+  const Value* value = nullptr;
+  std::string str_value;
+  int int_value;
+  double double_value;
+  bool bool_value;
+
+  dict = FindNamePhase("event1", "X");
+  ASSERT_TRUE(dict);
+  dict->GetDictionary("args", &args_dict);
+  ASSERT_TRUE(args_dict);
+  EXPECT_TRUE(args_dict->GetInteger("int_one", &int_value));
+  EXPECT_EQ(1, int_value);
+
+  dict = FindNamePhase("event2", "X");
+  ASSERT_TRUE(dict);
+  dict->GetDictionary("args", &args_dict);
+  ASSERT_TRUE(args_dict);
+  EXPECT_TRUE(args_dict->GetInteger("int_neg_ten", &int_value));
+  EXPECT_EQ(-10, int_value);
+
+  // 1f must be serlized to JSON as "1.0" in order to be a double, not an int.
+  dict = FindNamePhase("event3", "X");
+  ASSERT_TRUE(dict);
+  dict->GetDictionary("args", &args_dict);
+  ASSERT_TRUE(args_dict);
+  EXPECT_TRUE(args_dict->Get("float_one", &value));
+  EXPECT_TRUE(value->is_double());
+  EXPECT_TRUE(value->GetAsDouble(&double_value));
+  EXPECT_EQ(1, double_value);
+
+  // .5f must be serlized to JSON as "0.5".
+  dict = FindNamePhase("event4", "X");
+  ASSERT_TRUE(dict);
+  dict->GetDictionary("args", &args_dict);
+  ASSERT_TRUE(args_dict);
+  EXPECT_TRUE(args_dict->Get("float_half", &value));
+  EXPECT_TRUE(value->is_double());
+  EXPECT_TRUE(value->GetAsDouble(&double_value));
+  EXPECT_EQ(0.5, double_value);
+
+  // -.5f must be serlized to JSON as "-0.5".
+  dict = FindNamePhase("event5", "X");
+  ASSERT_TRUE(dict);
+  dict->GetDictionary("args", &args_dict);
+  ASSERT_TRUE(args_dict);
+  EXPECT_TRUE(args_dict->Get("float_neghalf", &value));
+  EXPECT_TRUE(value->is_double());
+  EXPECT_TRUE(value->GetAsDouble(&double_value));
+  EXPECT_EQ(-0.5, double_value);
+
+  // Infinity is serialized to JSON as a string.
+  dict = FindNamePhase("event6", "X");
+  ASSERT_TRUE(dict);
+  dict->GetDictionary("args", &args_dict);
+  ASSERT_TRUE(args_dict);
+  EXPECT_TRUE(args_dict->GetString("float_infinity", &str_value));
+  EXPECT_STREQ("Infinity", str_value.c_str());
+  dict = FindNamePhase("event6b", "X");
+  ASSERT_TRUE(dict);
+  dict->GetDictionary("args", &args_dict);
+  ASSERT_TRUE(args_dict);
+  EXPECT_TRUE(args_dict->GetString("float_neg_infinity", &str_value));
+  EXPECT_STREQ("-Infinity", str_value.c_str());
+
+  // NaN is serialized to JSON as a string.
+  dict = FindNamePhase("event7", "X");
+  ASSERT_TRUE(dict);
+  dict->GetDictionary("args", &args_dict);
+  ASSERT_TRUE(args_dict);
+  EXPECT_TRUE(args_dict->GetString("double_nan", &str_value));
+  EXPECT_STREQ("NaN", str_value.c_str());
+
+  // NULL pointers should be serialized as "0x0".
+  dict = FindNamePhase("event8", "X");
+  ASSERT_TRUE(dict);
+  dict->GetDictionary("args", &args_dict);
+  ASSERT_TRUE(args_dict);
+  EXPECT_TRUE(args_dict->GetString("pointer_null", &str_value));
+  EXPECT_STREQ("0x0", str_value.c_str());
+
+  // Other pointers should be serlized as a hex string.
+  dict = FindNamePhase("event9", "X");
+  ASSERT_TRUE(dict);
+  dict->GetDictionary("args", &args_dict);
+  ASSERT_TRUE(args_dict);
+  EXPECT_TRUE(args_dict->GetString("pointer_badf00d", &str_value));
+  EXPECT_STREQ("0xbadf00d", str_value.c_str());
+
+  dict = FindNamePhase("event10", "X");
+  ASSERT_TRUE(dict);
+  dict->GetDictionary("args", &args_dict);
+  ASSERT_TRUE(args_dict);
+  EXPECT_TRUE(args_dict->GetBoolean("bool_true", &bool_value));
+  EXPECT_TRUE(bool_value);
+
+  dict = FindNamePhase("event11", "X");
+  ASSERT_TRUE(dict);
+  dict->GetDictionary("args", &args_dict);
+  ASSERT_TRUE(args_dict);
+  EXPECT_TRUE(args_dict->GetBoolean("bool_false", &bool_value));
+  EXPECT_FALSE(bool_value);
+
+  dict = FindNamePhase("event12", "X");
+  ASSERT_TRUE(dict);
+  dict->GetDictionary("args", &args_dict);
+  ASSERT_TRUE(args_dict);
+  EXPECT_TRUE(args_dict->GetInteger("time_null", &int_value));
+  EXPECT_EQ(0, int_value);
+
+  dict = FindNamePhase("event13", "X");
+  ASSERT_TRUE(dict);
+  dict->GetDictionary("args", &args_dict);
+  ASSERT_TRUE(args_dict);
+  EXPECT_TRUE(args_dict->GetInteger("time_one", &int_value));
+  EXPECT_EQ(1, int_value);
+
+  dict = FindNamePhase("event14", "X");
+  ASSERT_TRUE(dict);
+  dict->GetDictionary("args", &args_dict);
+  ASSERT_TRUE(args_dict);
+  EXPECT_TRUE(args_dict->GetInteger("timeticks_null", &int_value));
+  EXPECT_EQ(0, int_value);
+
+  dict = FindNamePhase("event15", "X");
+  ASSERT_TRUE(dict);
+  dict->GetDictionary("args", &args_dict);
+  ASSERT_TRUE(args_dict);
+  EXPECT_TRUE(args_dict->GetInteger("timeticks_one", &int_value));
+  EXPECT_EQ(1, int_value);
+}
+
+TEST_F(TraceEventTestFixture, NameIsEscaped) {
+  TraceLog::GetInstance()->SetEnabled(TraceConfig(kRecordAllCategoryFilter, ""),
+                                      TraceLog::RECORDING_MODE);
+  TRACE_EVENT0("category", "name\\with\\backspaces");
+  EndTraceAndFlush();
+
+  EXPECT_TRUE(FindMatchingValue("cat", "category"));
+  EXPECT_TRUE(FindMatchingValue("name", "name\\with\\backspaces"));
+}
+
+namespace {
+
+bool IsArgNameWhitelisted(const char* arg_name) {
+  return base::MatchPattern(arg_name, "granular_arg_whitelisted");
+}
+
+bool IsTraceEventArgsWhitelisted(const char* category_group_name,
+                                 const char* event_name,
+                                 ArgumentNameFilterPredicate* arg_filter) {
+  if (base::MatchPattern(category_group_name, "toplevel") &&
+      base::MatchPattern(event_name, "*")) {
+    return true;
+  }
+
+  if (base::MatchPattern(category_group_name, "benchmark") &&
+      base::MatchPattern(event_name, "granularly_whitelisted")) {
+    *arg_filter = base::Bind(&IsArgNameWhitelisted);
+    return true;
+  }
+
+  return false;
+}
+
+}  // namespace
+
+TEST_F(TraceEventTestFixture, ArgsWhitelisting) {
+  TraceLog::GetInstance()->SetArgumentFilterPredicate(
+      base::Bind(&IsTraceEventArgsWhitelisted));
+
+  TraceLog::GetInstance()->SetEnabled(
+    TraceConfig(kRecordAllCategoryFilter, "enable-argument-filter"),
+    TraceLog::RECORDING_MODE);
+
+  TRACE_EVENT1("toplevel", "event1", "int_one", 1);
+  TRACE_EVENT1("whitewashed", "event2", "int_two", 1);
+
+  TRACE_EVENT2("benchmark", "granularly_whitelisted",
+               "granular_arg_whitelisted", "whitelisted_value",
+               "granular_arg_blacklisted", "blacklisted_value");
+
+  EndTraceAndFlush();
+
+  const DictionaryValue* args_dict = nullptr;
+  DictionaryValue* dict = nullptr;
+  int int_value;
+
+  dict = FindNamePhase("event1", "X");
+  ASSERT_TRUE(dict);
+  dict->GetDictionary("args", &args_dict);
+  ASSERT_TRUE(args_dict);
+  EXPECT_TRUE(args_dict->GetInteger("int_one", &int_value));
+  EXPECT_EQ(1, int_value);
+
+  dict = FindNamePhase("event2", "X");
+  ASSERT_TRUE(dict);
+  dict->GetDictionary("args", &args_dict);
+  ASSERT_TRUE(args_dict);
+  EXPECT_FALSE(args_dict->GetInteger("int_two", &int_value));
+
+  std::string args_string;
+  EXPECT_TRUE(dict->GetString("args", &args_string));
+  EXPECT_EQ(args_string, "__stripped__");
+
+  dict = FindNamePhase("granularly_whitelisted", "X");
+  ASSERT_TRUE(dict);
+  dict->GetDictionary("args", &args_dict);
+  ASSERT_TRUE(args_dict);
+
+  EXPECT_TRUE(args_dict->GetString("granular_arg_whitelisted", &args_string));
+  EXPECT_EQ(args_string, "whitelisted_value");
+
+  EXPECT_TRUE(args_dict->GetString("granular_arg_blacklisted", &args_string));
+  EXPECT_EQ(args_string, "__stripped__");
+}
+
+TEST_F(TraceEventTestFixture, TraceBufferVectorReportFull) {
+  TraceLog* trace_log = TraceLog::GetInstance();
+  trace_log->SetEnabled(
+      TraceConfig(kRecordAllCategoryFilter, ""), TraceLog::RECORDING_MODE);
+  trace_log->logged_events_.reset(
+      TraceBuffer::CreateTraceBufferVectorOfSize(100));
+  do {
+    TRACE_EVENT_BEGIN_WITH_ID_TID_AND_TIMESTAMP0(
+        "all", "with_timestamp", 0, 0, TimeTicks::Now());
+    TRACE_EVENT_END_WITH_ID_TID_AND_TIMESTAMP0(
+        "all", "with_timestamp", 0, 0, TimeTicks::Now());
+  } while (!trace_log->BufferIsFull());
+
+  EndTraceAndFlush();
+
+  const DictionaryValue* trace_full_metadata = nullptr;
+
+  trace_full_metadata = FindTraceEntry(trace_parsed_,
+                                       "overflowed_at_ts");
+  std::string phase;
+  double buffer_limit_reached_timestamp = 0;
+
+  EXPECT_TRUE(trace_full_metadata);
+  EXPECT_TRUE(trace_full_metadata->GetString("ph", &phase));
+  EXPECT_EQ("M", phase);
+  EXPECT_TRUE(trace_full_metadata->GetDouble(
+      "args.overflowed_at_ts", &buffer_limit_reached_timestamp));
+  EXPECT_DOUBLE_EQ(
+      static_cast<double>(
+          trace_log->buffer_limit_reached_timestamp_.ToInternalValue()),
+      buffer_limit_reached_timestamp);
+
+  // Test that buffer_limit_reached_timestamp's value is between the timestamp
+  // of the last trace event and current time.
+  DropTracedMetadataRecords();
+  const DictionaryValue* last_trace_event = nullptr;
+  double last_trace_event_timestamp = 0;
+  EXPECT_TRUE(trace_parsed_.GetDictionary(trace_parsed_.GetSize() - 1,
+                                          &last_trace_event));
+  EXPECT_TRUE(last_trace_event->GetDouble("ts", &last_trace_event_timestamp));
+  EXPECT_LE(last_trace_event_timestamp, buffer_limit_reached_timestamp);
+  EXPECT_LE(buffer_limit_reached_timestamp,
+            trace_log->OffsetNow().ToInternalValue());
+}
+
+TEST_F(TraceEventTestFixture, TraceBufferRingBufferGetReturnChunk) {
+  TraceLog::GetInstance()->SetEnabled(
+      TraceConfig(kRecordAllCategoryFilter, RECORD_CONTINUOUSLY),
+      TraceLog::RECORDING_MODE);
+  TraceBuffer* buffer = TraceLog::GetInstance()->trace_buffer();
+  size_t capacity = buffer->Capacity();
+  size_t num_chunks = capacity / TraceBufferChunk::kTraceBufferChunkSize;
+  uint32_t last_seq = 0;
+  size_t chunk_index;
+  EXPECT_EQ(0u, buffer->Size());
+
+  std::unique_ptr<TraceBufferChunk* []> chunks(
+      new TraceBufferChunk*[num_chunks]);
+  for (size_t i = 0; i < num_chunks; ++i) {
+    chunks[i] = buffer->GetChunk(&chunk_index).release();
+    EXPECT_TRUE(chunks[i]);
+    EXPECT_EQ(i, chunk_index);
+    EXPECT_GT(chunks[i]->seq(), last_seq);
+    EXPECT_EQ((i + 1) * TraceBufferChunk::kTraceBufferChunkSize,
+              buffer->Size());
+    last_seq = chunks[i]->seq();
+  }
+
+  // Ring buffer is never full.
+  EXPECT_FALSE(buffer->IsFull());
+
+  // Return all chunks in original order.
+  for (size_t i = 0; i < num_chunks; ++i)
+    buffer->ReturnChunk(i, std::unique_ptr<TraceBufferChunk>(chunks[i]));
+
+  // Should recycle the chunks in the returned order.
+  for (size_t i = 0; i < num_chunks; ++i) {
+    chunks[i] = buffer->GetChunk(&chunk_index).release();
+    EXPECT_TRUE(chunks[i]);
+    EXPECT_EQ(i, chunk_index);
+    EXPECT_GT(chunks[i]->seq(), last_seq);
+    last_seq = chunks[i]->seq();
+  }
+
+  // Return all chunks in reverse order.
+  for (size_t i = 0; i < num_chunks; ++i) {
+    buffer->ReturnChunk(num_chunks - i - 1, std::unique_ptr<TraceBufferChunk>(
+                                                chunks[num_chunks - i - 1]));
+  }
+
+  // Should recycle the chunks in the returned order.
+  for (size_t i = 0; i < num_chunks; ++i) {
+    chunks[i] = buffer->GetChunk(&chunk_index).release();
+    EXPECT_TRUE(chunks[i]);
+    EXPECT_EQ(num_chunks - i - 1, chunk_index);
+    EXPECT_GT(chunks[i]->seq(), last_seq);
+    last_seq = chunks[i]->seq();
+  }
+
+  for (size_t i = 0; i < num_chunks; ++i)
+    buffer->ReturnChunk(i, std::unique_ptr<TraceBufferChunk>(chunks[i]));
+
+  TraceLog::GetInstance()->SetDisabled();
+}
+
+TEST_F(TraceEventTestFixture, TraceBufferRingBufferHalfIteration) {
+  TraceLog::GetInstance()->SetEnabled(
+      TraceConfig(kRecordAllCategoryFilter, RECORD_CONTINUOUSLY),
+      TraceLog::RECORDING_MODE);
+  TraceBuffer* buffer = TraceLog::GetInstance()->trace_buffer();
+  size_t capacity = buffer->Capacity();
+  size_t num_chunks = capacity / TraceBufferChunk::kTraceBufferChunkSize;
+  size_t chunk_index;
+  EXPECT_EQ(0u, buffer->Size());
+  EXPECT_FALSE(buffer->NextChunk());
+
+  size_t half_chunks = num_chunks / 2;
+  std::unique_ptr<TraceBufferChunk* []> chunks(
+      new TraceBufferChunk*[half_chunks]);
+
+  for (size_t i = 0; i < half_chunks; ++i) {
+    chunks[i] = buffer->GetChunk(&chunk_index).release();
+    EXPECT_TRUE(chunks[i]);
+    EXPECT_EQ(i, chunk_index);
+  }
+  for (size_t i = 0; i < half_chunks; ++i)
+    buffer->ReturnChunk(i, std::unique_ptr<TraceBufferChunk>(chunks[i]));
+
+  for (size_t i = 0; i < half_chunks; ++i)
+    EXPECT_EQ(chunks[i], buffer->NextChunk());
+  EXPECT_FALSE(buffer->NextChunk());
+  TraceLog::GetInstance()->SetDisabled();
+}
+
+TEST_F(TraceEventTestFixture, TraceBufferRingBufferFullIteration) {
+  TraceLog::GetInstance()->SetEnabled(
+      TraceConfig(kRecordAllCategoryFilter, RECORD_CONTINUOUSLY),
+      TraceLog::RECORDING_MODE);
+  TraceBuffer* buffer = TraceLog::GetInstance()->trace_buffer();
+  size_t capacity = buffer->Capacity();
+  size_t num_chunks = capacity / TraceBufferChunk::kTraceBufferChunkSize;
+  size_t chunk_index;
+  EXPECT_EQ(0u, buffer->Size());
+  EXPECT_FALSE(buffer->NextChunk());
+
+  std::unique_ptr<TraceBufferChunk* []> chunks(
+      new TraceBufferChunk*[num_chunks]);
+
+  for (size_t i = 0; i < num_chunks; ++i) {
+    chunks[i] = buffer->GetChunk(&chunk_index).release();
+    EXPECT_TRUE(chunks[i]);
+    EXPECT_EQ(i, chunk_index);
+  }
+  for (size_t i = 0; i < num_chunks; ++i)
+    buffer->ReturnChunk(i, std::unique_ptr<TraceBufferChunk>(chunks[i]));
+
+  for (size_t i = 0; i < num_chunks; ++i)
+    EXPECT_TRUE(chunks[i] == buffer->NextChunk());
+  EXPECT_FALSE(buffer->NextChunk());
+  TraceLog::GetInstance()->SetDisabled();
+}
+
+TEST_F(TraceEventTestFixture, TraceRecordAsMuchAsPossibleMode) {
+  TraceLog::GetInstance()->SetEnabled(
+    TraceConfig(kRecordAllCategoryFilter, RECORD_AS_MUCH_AS_POSSIBLE),
+    TraceLog::RECORDING_MODE);
+  TraceBuffer* buffer = TraceLog::GetInstance()->trace_buffer();
+  EXPECT_EQ(512000000UL, buffer->Capacity());
+  TraceLog::GetInstance()->SetDisabled();
+}
+
+void BlockUntilStopped(WaitableEvent* task_start_event,
+                       WaitableEvent* task_stop_event) {
+  task_start_event->Signal();
+  task_stop_event->Wait();
+}
+
+TEST_F(TraceEventTestFixture, SetCurrentThreadBlocksMessageLoopBeforeTracing) {
+  BeginTrace();
+
+  Thread thread("1");
+  WaitableEvent task_complete_event(WaitableEvent::ResetPolicy::AUTOMATIC,
+                                    WaitableEvent::InitialState::NOT_SIGNALED);
+  thread.Start();
+  thread.task_runner()->PostTask(
+      FROM_HERE, BindOnce(&TraceLog::SetCurrentThreadBlocksMessageLoop,
+                          Unretained(TraceLog::GetInstance())));
+
+  thread.task_runner()->PostTask(
+      FROM_HERE, BindOnce(&TraceWithAllMacroVariants, &task_complete_event));
+  task_complete_event.Wait();
+
+  WaitableEvent task_start_event(WaitableEvent::ResetPolicy::AUTOMATIC,
+                                 WaitableEvent::InitialState::NOT_SIGNALED);
+  WaitableEvent task_stop_event(WaitableEvent::ResetPolicy::AUTOMATIC,
+                                WaitableEvent::InitialState::NOT_SIGNALED);
+  thread.task_runner()->PostTask(
+      FROM_HERE,
+      BindOnce(&BlockUntilStopped, &task_start_event, &task_stop_event));
+  task_start_event.Wait();
+
+  EndTraceAndFlush();
+  ValidateAllTraceMacrosCreatedData(trace_parsed_);
+
+  task_stop_event.Signal();
+  thread.Stop();
+}
+
+TEST_F(TraceEventTestFixture, ConvertTraceConfigToInternalOptions) {
+  TraceLog* trace_log = TraceLog::GetInstance();
+  EXPECT_EQ(TraceLog::kInternalRecordUntilFull,
+            trace_log->GetInternalOptionsFromTraceConfig(
+                TraceConfig(kRecordAllCategoryFilter, RECORD_UNTIL_FULL)));
+
+  EXPECT_EQ(TraceLog::kInternalRecordContinuously,
+            trace_log->GetInternalOptionsFromTraceConfig(
+                TraceConfig(kRecordAllCategoryFilter, RECORD_CONTINUOUSLY)));
+
+  EXPECT_EQ(TraceLog::kInternalEchoToConsole,
+            trace_log->GetInternalOptionsFromTraceConfig(
+                TraceConfig(kRecordAllCategoryFilter, ECHO_TO_CONSOLE)));
+
+  EXPECT_EQ(TraceLog::kInternalEchoToConsole,
+            trace_log->GetInternalOptionsFromTraceConfig(
+                TraceConfig("*", "trace-to-console,enable-systrace")));
+}
+
+void SetBlockingFlagAndBlockUntilStopped(WaitableEvent* task_start_event,
+                                         WaitableEvent* task_stop_event) {
+  TraceLog::GetInstance()->SetCurrentThreadBlocksMessageLoop();
+  BlockUntilStopped(task_start_event, task_stop_event);
+}
+
+TEST_F(TraceEventTestFixture, SetCurrentThreadBlocksMessageLoopAfterTracing) {
+  BeginTrace();
+
+  Thread thread("1");
+  WaitableEvent task_complete_event(WaitableEvent::ResetPolicy::AUTOMATIC,
+                                    WaitableEvent::InitialState::NOT_SIGNALED);
+  thread.Start();
+
+  thread.task_runner()->PostTask(
+      FROM_HERE, BindOnce(&TraceWithAllMacroVariants, &task_complete_event));
+  task_complete_event.Wait();
+
+  WaitableEvent task_start_event(WaitableEvent::ResetPolicy::AUTOMATIC,
+                                 WaitableEvent::InitialState::NOT_SIGNALED);
+  WaitableEvent task_stop_event(WaitableEvent::ResetPolicy::AUTOMATIC,
+                                WaitableEvent::InitialState::NOT_SIGNALED);
+  thread.task_runner()->PostTask(FROM_HERE,
+                                 BindOnce(&SetBlockingFlagAndBlockUntilStopped,
+                                          &task_start_event, &task_stop_event));
+  task_start_event.Wait();
+
+  EndTraceAndFlush();
+  ValidateAllTraceMacrosCreatedData(trace_parsed_);
+
+  task_stop_event.Signal();
+  thread.Stop();
+}
+
+TEST_F(TraceEventTestFixture, ThreadOnceBlocking) {
+  BeginTrace();
+
+  Thread thread("1");
+  WaitableEvent task_complete_event(WaitableEvent::ResetPolicy::AUTOMATIC,
+                                    WaitableEvent::InitialState::NOT_SIGNALED);
+  thread.Start();
+
+  thread.task_runner()->PostTask(
+      FROM_HERE, BindOnce(&TraceWithAllMacroVariants, &task_complete_event));
+  task_complete_event.Wait();
+
+  WaitableEvent task_start_event(WaitableEvent::ResetPolicy::AUTOMATIC,
+                                 WaitableEvent::InitialState::NOT_SIGNALED);
+  WaitableEvent task_stop_event(WaitableEvent::ResetPolicy::AUTOMATIC,
+                                WaitableEvent::InitialState::NOT_SIGNALED);
+  thread.task_runner()->PostTask(
+      FROM_HERE,
+      BindOnce(&BlockUntilStopped, &task_start_event, &task_stop_event));
+  task_start_event.Wait();
+
+  // The thread will timeout in this flush.
+  EndTraceAndFlushInThreadWithMessageLoop();
+  Clear();
+
+  // Let the thread's message loop continue to spin.
+  task_stop_event.Signal();
+
+  // The following sequence ensures that the FlushCurrentThread task has been
+  // executed in the thread before continuing.
+  thread.task_runner()->PostTask(
+      FROM_HERE,
+      BindOnce(&BlockUntilStopped, &task_start_event, &task_stop_event));
+  task_start_event.Wait();
+  task_stop_event.Signal();
+  Clear();
+
+  // TraceLog should discover the generation mismatch and recover the thread
+  // local buffer for the thread without any error.
+  BeginTrace();
+  thread.task_runner()->PostTask(
+      FROM_HERE, BindOnce(&TraceWithAllMacroVariants, &task_complete_event));
+  task_complete_event.Wait();
+  EndTraceAndFlushInThreadWithMessageLoop();
+  ValidateAllTraceMacrosCreatedData(trace_parsed_);
+}
+
+std::string* g_log_buffer = nullptr;
+bool MockLogMessageHandler(int, const char*, int, size_t,
+                           const std::string& str) {
+  if (!g_log_buffer)
+    g_log_buffer = new std::string();
+  g_log_buffer->append(str);
+  return false;
+}
+
+TEST_F(TraceEventTestFixture, EchoToConsole) {
+  logging::LogMessageHandlerFunction old_log_message_handler =
+      logging::GetLogMessageHandler();
+  logging::SetLogMessageHandler(MockLogMessageHandler);
+
+  TraceLog::GetInstance()->SetEnabled(
+      TraceConfig(kRecordAllCategoryFilter, ECHO_TO_CONSOLE),
+      TraceLog::RECORDING_MODE);
+  TRACE_EVENT_BEGIN0("a", "begin_end");
+  {
+    TRACE_EVENT0("b", "duration");
+    TRACE_EVENT0("b1", "duration1");
+  }
+  TRACE_EVENT_INSTANT0("c", "instant", TRACE_EVENT_SCOPE_GLOBAL);
+  TRACE_EVENT_END0("a", "begin_end");
+
+  EXPECT_NE(std::string::npos, g_log_buffer->find("begin_end[a]\x1b"));
+  EXPECT_NE(std::string::npos, g_log_buffer->find("| duration[b]\x1b"));
+  EXPECT_NE(std::string::npos, g_log_buffer->find("| | duration1[b1]\x1b"));
+  EXPECT_NE(std::string::npos, g_log_buffer->find("| | duration1[b1] ("));
+  EXPECT_NE(std::string::npos, g_log_buffer->find("| duration[b] ("));
+  EXPECT_NE(std::string::npos, g_log_buffer->find("| instant[c]\x1b"));
+  EXPECT_NE(std::string::npos, g_log_buffer->find("begin_end[a] ("));
+
+  EndTraceAndFlush();
+  delete g_log_buffer;
+  logging::SetLogMessageHandler(old_log_message_handler);
+  g_log_buffer = nullptr;
+}
+
+bool LogMessageHandlerWithTraceEvent(int, const char*, int, size_t,
+                                     const std::string&) {
+  TRACE_EVENT0("log", "trace_event");
+  return false;
+}
+
+TEST_F(TraceEventTestFixture, EchoToConsoleTraceEventRecursion) {
+  logging::LogMessageHandlerFunction old_log_message_handler =
+      logging::GetLogMessageHandler();
+  logging::SetLogMessageHandler(LogMessageHandlerWithTraceEvent);
+
+  TraceLog::GetInstance()->SetEnabled(
+      TraceConfig(kRecordAllCategoryFilter, ECHO_TO_CONSOLE),
+      TraceLog::RECORDING_MODE);
+  {
+    // This should not cause deadlock or infinite recursion.
+    TRACE_EVENT0("b", "duration");
+  }
+
+  EndTraceAndFlush();
+  logging::SetLogMessageHandler(old_log_message_handler);
+}
+
+TEST_F(TraceEventTestFixture, TimeOffset) {
+  BeginTrace();
+  // Let TraceLog timer start from 0.
+  TimeDelta time_offset = TimeTicks::Now() - TimeTicks();
+  TraceLog::GetInstance()->SetTimeOffset(time_offset);
+
+  {
+    TRACE_EVENT0("all", "duration1");
+    TRACE_EVENT0("all", "duration2");
+  }
+  TRACE_EVENT_BEGIN_WITH_ID_TID_AND_TIMESTAMP0(
+      "all", "with_timestamp", 0, 0, TimeTicks::Now());
+  TRACE_EVENT_END_WITH_ID_TID_AND_TIMESTAMP0(
+      "all", "with_timestamp", 0, 0, TimeTicks::Now());
+
+  EndTraceAndFlush();
+  DropTracedMetadataRecords();
+
+  double end_time = static_cast<double>(
+      (TimeTicks::Now() - time_offset).ToInternalValue());
+  double last_timestamp = 0;
+  for (size_t i = 0; i < trace_parsed_.GetSize(); ++i) {
+    const DictionaryValue* item;
+    EXPECT_TRUE(trace_parsed_.GetDictionary(i, &item));
+    double timestamp;
+    EXPECT_TRUE(item->GetDouble("ts", &timestamp));
+    EXPECT_GE(timestamp, last_timestamp);
+    EXPECT_LE(timestamp, end_time);
+    last_timestamp = timestamp;
+  }
+}
+
+TEST_F(TraceEventTestFixture, TraceFilteringMode) {
+  const char config_json[] =
+      "{"
+      "  \"event_filters\": ["
+      "     {"
+      "       \"filter_predicate\": \"testing_predicate\", "
+      "       \"included_categories\": [\"*\"]"
+      "     }"
+      "  ]"
+      "}";
+
+  // Run RECORDING_MODE within FILTERING_MODE:
+  TestEventFilter::HitsCounter filter_hits_counter;
+  TestEventFilter::set_filter_return_value(true);
+  TraceLog::GetInstance()->SetFilterFactoryForTesting(TestEventFilter::Factory);
+
+  // Only filtering mode is enabled with test filters.
+  TraceLog::GetInstance()->SetEnabled(TraceConfig(config_json),
+                                      TraceLog::FILTERING_MODE);
+  EXPECT_EQ(TraceLog::FILTERING_MODE, TraceLog::GetInstance()->enabled_modes());
+  {
+    void* ptr = this;
+    TRACE_EVENT0("c0", "name0");
+    TRACE_EVENT_ASYNC_BEGIN0("c1", "name1", ptr);
+    TRACE_EVENT_INSTANT0("c0", "name0", TRACE_EVENT_SCOPE_THREAD);
+    TRACE_EVENT_ASYNC_END0("c1", "name1", ptr);
+  }
+
+  // Recording mode is enabled when filtering mode is turned on.
+  TraceLog::GetInstance()->SetEnabled(TraceConfig("", ""),
+                                      TraceLog::RECORDING_MODE);
+  EXPECT_EQ(TraceLog::RECORDING_MODE | TraceLog::FILTERING_MODE,
+            TraceLog::GetInstance()->enabled_modes());
+  {
+    TRACE_EVENT0("c2", "name2");
+  }
+  // Only recording mode is disabled and filtering mode will continue to run.
+  TraceLog::GetInstance()->SetDisabled(TraceLog::RECORDING_MODE);
+  EXPECT_EQ(TraceLog::FILTERING_MODE, TraceLog::GetInstance()->enabled_modes());
+
+  {
+    TRACE_EVENT0("c0", "name0");
+  }
+  // Filtering mode is disabled and no tracing mode should be enabled.
+  TraceLog::GetInstance()->SetDisabled(TraceLog::FILTERING_MODE);
+  EXPECT_EQ(0, TraceLog::GetInstance()->enabled_modes());
+
+  EndTraceAndFlush();
+  EXPECT_FALSE(FindMatchingValue("cat", "c0"));
+  EXPECT_FALSE(FindMatchingValue("cat", "c1"));
+  EXPECT_FALSE(FindMatchingValue("name", "name0"));
+  EXPECT_FALSE(FindMatchingValue("name", "name1"));
+  EXPECT_TRUE(FindMatchingValue("cat", "c2"));
+  EXPECT_TRUE(FindMatchingValue("name", "name2"));
+  EXPECT_EQ(6u, filter_hits_counter.filter_trace_event_hit_count);
+  EXPECT_EQ(3u, filter_hits_counter.end_event_hit_count);
+  Clear();
+  filter_hits_counter.Reset();
+
+  // Run FILTERING_MODE within RECORDING_MODE:
+  // Only recording mode is enabled and all events must be recorded.
+  TraceLog::GetInstance()->SetEnabled(TraceConfig("", ""),
+                                      TraceLog::RECORDING_MODE);
+  EXPECT_EQ(TraceLog::RECORDING_MODE, TraceLog::GetInstance()->enabled_modes());
+  {
+    TRACE_EVENT0("c0", "name0");
+  }
+
+  // Filtering mode is also enabled and all events must be filtered-out.
+  TestEventFilter::set_filter_return_value(false);
+  TraceLog::GetInstance()->SetEnabled(TraceConfig(config_json),
+                                      TraceLog::FILTERING_MODE);
+  EXPECT_EQ(TraceLog::RECORDING_MODE | TraceLog::FILTERING_MODE,
+            TraceLog::GetInstance()->enabled_modes());
+  {
+    TRACE_EVENT0("c1", "name1");
+  }
+  // Only filtering mode is disabled and recording mode should continue to run
+  // with all events being recorded.
+  TraceLog::GetInstance()->SetDisabled(TraceLog::FILTERING_MODE);
+  EXPECT_EQ(TraceLog::RECORDING_MODE, TraceLog::GetInstance()->enabled_modes());
+
+  {
+    TRACE_EVENT0("c2", "name2");
+  }
+  // Recording mode is disabled and no tracing mode should be enabled.
+  TraceLog::GetInstance()->SetDisabled(TraceLog::RECORDING_MODE);
+  EXPECT_EQ(0, TraceLog::GetInstance()->enabled_modes());
+
+  EndTraceAndFlush();
+  EXPECT_TRUE(FindMatchingValue("cat", "c0"));
+  EXPECT_TRUE(FindMatchingValue("cat", "c2"));
+  EXPECT_TRUE(FindMatchingValue("name", "name0"));
+  EXPECT_TRUE(FindMatchingValue("name", "name2"));
+  EXPECT_FALSE(FindMatchingValue("cat", "c1"));
+  EXPECT_FALSE(FindMatchingValue("name", "name1"));
+  EXPECT_EQ(1u, filter_hits_counter.filter_trace_event_hit_count);
+  EXPECT_EQ(1u, filter_hits_counter.end_event_hit_count);
+  Clear();
+}
+
+TEST_F(TraceEventTestFixture, EventFiltering) {
+  const char config_json[] =
+      "{"
+      "  \"included_categories\": ["
+      "    \"filtered_cat\","
+      "    \"unfiltered_cat\","
+      "    \"" TRACE_DISABLED_BY_DEFAULT("filtered_cat") "\","
+      "    \"" TRACE_DISABLED_BY_DEFAULT("unfiltered_cat") "\"],"
+      "  \"event_filters\": ["
+      "     {"
+      "       \"filter_predicate\": \"testing_predicate\", "
+      "       \"included_categories\": ["
+      "         \"filtered_cat\","
+      "         \"" TRACE_DISABLED_BY_DEFAULT("filtered_cat") "\"]"
+      "     }"
+      "    "
+      "  ]"
+      "}";
+
+  TestEventFilter::HitsCounter filter_hits_counter;
+  TestEventFilter::set_filter_return_value(true);
+  TraceLog::GetInstance()->SetFilterFactoryForTesting(TestEventFilter::Factory);
+
+  TraceConfig trace_config(config_json);
+  TraceLog::GetInstance()->SetEnabled(
+      trace_config, TraceLog::RECORDING_MODE | TraceLog::FILTERING_MODE);
+  ASSERT_TRUE(TraceLog::GetInstance()->IsEnabled());
+
+  TRACE_EVENT0("filtered_cat", "a snake");
+  TRACE_EVENT0("filtered_cat", "a mushroom");
+  TRACE_EVENT0("unfiltered_cat", "a horse");
+
+  TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("filtered_cat"), "a dog");
+  TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("unfiltered_cat"), "a pony");
+
+  // This is scoped so we can test the end event being filtered.
+  { TRACE_EVENT0("filtered_cat", "another cat whoa"); }
+
+  EndTraceAndFlush();
+
+  EXPECT_EQ(4u, filter_hits_counter.filter_trace_event_hit_count);
+  EXPECT_EQ(1u, filter_hits_counter.end_event_hit_count);
+}
+
+TEST_F(TraceEventTestFixture, EventWhitelistFiltering) {
+  std::string config_json = StringPrintf(
+      "{"
+      "  \"included_categories\": ["
+      "    \"filtered_cat\","
+      "    \"unfiltered_cat\","
+      "    \"" TRACE_DISABLED_BY_DEFAULT("filtered_cat") "\"],"
+      "  \"event_filters\": ["
+      "     {"
+      "       \"filter_predicate\": \"%s\", "
+      "       \"included_categories\": ["
+      "         \"filtered_cat\","
+      "         \"" TRACE_DISABLED_BY_DEFAULT("*") "\"], "
+      "       \"filter_args\": {"
+      "           \"event_name_whitelist\": [\"a snake\", \"a dog\"]"
+      "         }"
+      "     }"
+      "    "
+      "  ]"
+      "}",
+      EventNameFilter::kName);
+
+  TraceConfig trace_config(config_json);
+  TraceLog::GetInstance()->SetEnabled(
+      trace_config, TraceLog::RECORDING_MODE | TraceLog::FILTERING_MODE);
+  EXPECT_TRUE(TraceLog::GetInstance()->IsEnabled());
+
+  TRACE_EVENT0("filtered_cat", "a snake");
+  TRACE_EVENT0("filtered_cat", "a mushroom");
+  TRACE_EVENT0("unfiltered_cat", "a cat");
+  TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("filtered_cat"), "a dog");
+  TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("filtered_cat"), "a pony");
+
+  EndTraceAndFlush();
+
+  EXPECT_TRUE(FindMatchingValue("name", "a snake"));
+  EXPECT_FALSE(FindMatchingValue("name", "a mushroom"));
+  EXPECT_TRUE(FindMatchingValue("name", "a cat"));
+  EXPECT_TRUE(FindMatchingValue("name", "a dog"));
+  EXPECT_FALSE(FindMatchingValue("name", "a pony"));
+}
+
+TEST_F(TraceEventTestFixture, HeapProfilerFiltering) {
+  std::string config_json = StringPrintf(
+      "{"
+      "  \"included_categories\": ["
+      "    \"filtered_cat\","
+      "    \"unfiltered_cat\","
+      "    \"" TRACE_DISABLED_BY_DEFAULT("filtered_cat") "\","
+      "    \"" TRACE_DISABLED_BY_DEFAULT("unfiltered_cat") "\"],"
+      "  \"excluded_categories\": [\"excluded_cat\"],"
+      "  \"event_filters\": ["
+      "     {"
+      "       \"filter_predicate\": \"%s\", "
+      "       \"included_categories\": ["
+      "         \"*\","
+      "         \"" TRACE_DISABLED_BY_DEFAULT("filtered_cat") "\"]"
+      "     }"
+      "  ]"
+      "}",
+      HeapProfilerEventFilter::kName);
+
+  TraceConfig trace_config(config_json);
+  TraceLog::GetInstance()->SetEnabled(
+      trace_config, TraceLog::RECORDING_MODE | TraceLog::FILTERING_MODE);
+  EXPECT_TRUE(TraceLog::GetInstance()->IsEnabled());
+
+  TRACE_EVENT0("filtered_cat", "a snake");
+  TRACE_EVENT0("excluded_cat", "a mushroom");
+  TRACE_EVENT0("unfiltered_cat", "a cat");
+  TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("filtered_cat"), "a dog");
+  TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("unfiltered_cat"), "a pony");
+
+  EndTraceAndFlush();
+
+  // The predicate should not change behavior of the trace events.
+  EXPECT_TRUE(FindMatchingValue("name", "a snake"));
+  EXPECT_FALSE(FindMatchingValue("name", "a mushroom"));
+  EXPECT_TRUE(FindMatchingValue("name", "a cat"));
+  EXPECT_TRUE(FindMatchingValue("name", "a dog"));
+  EXPECT_TRUE(FindMatchingValue("name", "a pony"));
+}
+
+TEST_F(TraceEventTestFixture, ClockSyncEventsAreAlwaysAddedToTrace) {
+  BeginSpecificTrace("-*");
+  TRACE_EVENT_CLOCK_SYNC_RECEIVER(1);
+  EndTraceAndFlush();
+  EXPECT_TRUE(FindNamePhase("clock_sync", "c"));
+}
+
+}  // namespace trace_event
+}  // namespace base
diff --git a/base/trace_event/trace_log.cc b/base/trace_event/trace_log.cc
new file mode 100644
index 0000000..4eb6958
--- /dev/null
+++ b/base/trace_event/trace_log.cc
@@ -0,0 +1,1787 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/trace_event/trace_log.h"
+
+#include <algorithm>
+#include <cmath>
+#include <memory>
+#include <utility>
+
+#include "base/base_switches.h"
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/debug/leak_annotations.h"
+#include "base/location.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/message_loop/message_loop.h"
+#include "base/message_loop/message_loop_current.h"
+#include "base/no_destructor.h"
+#include "base/process/process_info.h"
+#include "base/process/process_metrics.h"
+#include "base/stl_util.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_tokenizer.h"
+#include "base/strings/stringprintf.h"
+#include "base/sys_info.h"
+#include "base/task_scheduler/post_task.h"
+#include "base/threading/platform_thread.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "base/threading/thread_id_name_manager.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/time/time.h"
+#include "base/trace_event/category_registry.h"
+#include "base/trace_event/event_name_filter.h"
+#include "base/trace_event/heap_profiler.h"
+#include "base/trace_event/heap_profiler_allocation_context_tracker.h"
+#include "base/trace_event/heap_profiler_event_filter.h"
+#include "base/trace_event/memory_dump_manager.h"
+#include "base/trace_event/memory_dump_provider.h"
+#include "base/trace_event/process_memory_dump.h"
+#include "base/trace_event/trace_buffer.h"
+#include "base/trace_event/trace_event.h"
+#include "build/build_config.h"
+
+#if defined(OS_WIN)
+#include "base/trace_event/trace_event_etw_export_win.h"
+#endif
+
+#if defined(OS_ANDROID)
+// The linker assigns the virtual address of the start of current library to
+// this symbol.
+extern char __executable_start;
+#endif
+
+namespace base {
+namespace trace_event {
+
+namespace {
+
+// Controls the number of trace events we will buffer in-memory
+// before throwing them away.
+const size_t kTraceBufferChunkSize = TraceBufferChunk::kTraceBufferChunkSize;
+
+const size_t kTraceEventVectorBigBufferChunks =
+    512000000 / kTraceBufferChunkSize;
+static_assert(
+    kTraceEventVectorBigBufferChunks <= TraceBufferChunk::kMaxChunkIndex,
+    "Too many big buffer chunks");
+const size_t kTraceEventVectorBufferChunks = 256000 / kTraceBufferChunkSize;
+static_assert(
+    kTraceEventVectorBufferChunks <= TraceBufferChunk::kMaxChunkIndex,
+    "Too many vector buffer chunks");
+const size_t kTraceEventRingBufferChunks = kTraceEventVectorBufferChunks / 4;
+
+// ECHO_TO_CONSOLE needs a small buffer to hold the unfinished COMPLETE events.
+const size_t kEchoToConsoleTraceEventBufferChunks = 256;
+
+const size_t kTraceEventBufferSizeInBytes = 100 * 1024;
+const int kThreadFlushTimeoutMs = 3000;
+
+TraceLog* g_trace_log_for_testing = nullptr;
+
+#define MAX_TRACE_EVENT_FILTERS 32
+
+// List of TraceEventFilter objects from the most recent tracing session.
+std::vector<std::unique_ptr<TraceEventFilter>>& GetCategoryGroupFilters() {
+  static auto* filters = new std::vector<std::unique_ptr<TraceEventFilter>>();
+  return *filters;
+}
+
+ThreadTicks ThreadNow() {
+  return ThreadTicks::IsSupported()
+             ? base::subtle::ThreadTicksNowIgnoringOverride()
+             : ThreadTicks();
+}
+
+template <typename T>
+void InitializeMetadataEvent(TraceEvent* trace_event,
+                             int thread_id,
+                             const char* metadata_name,
+                             const char* arg_name,
+                             const T& value) {
+  if (!trace_event)
+    return;
+
+  int num_args = 1;
+  unsigned char arg_type;
+  unsigned long long arg_value;
+  ::trace_event_internal::SetTraceValue(value, &arg_type, &arg_value);
+  trace_event->Initialize(
+      thread_id,
+      TimeTicks(),
+      ThreadTicks(),
+      TRACE_EVENT_PHASE_METADATA,
+      CategoryRegistry::kCategoryMetadata->state_ptr(),
+      metadata_name,
+      trace_event_internal::kGlobalScope,  // scope
+      trace_event_internal::kNoId,  // id
+      trace_event_internal::kNoId,  // bind_id
+      num_args,
+      &arg_name,
+      &arg_type,
+      &arg_value,
+      nullptr,
+      TRACE_EVENT_FLAG_NONE);
+}
+
+class AutoThreadLocalBoolean {
+ public:
+  explicit AutoThreadLocalBoolean(ThreadLocalBoolean* thread_local_boolean)
+      : thread_local_boolean_(thread_local_boolean) {
+    DCHECK(!thread_local_boolean_->Get());
+    thread_local_boolean_->Set(true);
+  }
+  ~AutoThreadLocalBoolean() { thread_local_boolean_->Set(false); }
+
+ private:
+  ThreadLocalBoolean* thread_local_boolean_;
+  DISALLOW_COPY_AND_ASSIGN(AutoThreadLocalBoolean);
+};
+
+// Use this function instead of TraceEventHandle constructor to keep the
+// overhead of ScopedTracer (trace_event.h) constructor minimum.
+void MakeHandle(uint32_t chunk_seq,
+                size_t chunk_index,
+                size_t event_index,
+                TraceEventHandle* handle) {
+  DCHECK(chunk_seq);
+  DCHECK(chunk_index <= TraceBufferChunk::kMaxChunkIndex);
+  DCHECK(event_index < TraceBufferChunk::kTraceBufferChunkSize);
+  DCHECK(chunk_index <= std::numeric_limits<uint16_t>::max());
+  handle->chunk_seq = chunk_seq;
+  handle->chunk_index = static_cast<uint16_t>(chunk_index);
+  handle->event_index = static_cast<uint16_t>(event_index);
+}
+
+template <typename Function>
+void ForEachCategoryFilter(const unsigned char* category_group_enabled,
+                           Function filter_fn) {
+  const TraceCategory* category =
+      CategoryRegistry::GetCategoryByStatePtr(category_group_enabled);
+  uint32_t filter_bitmap = category->enabled_filters();
+  for (int index = 0; filter_bitmap != 0; filter_bitmap >>= 1, index++) {
+    if (filter_bitmap & 1 && GetCategoryGroupFilters()[index])
+      filter_fn(GetCategoryGroupFilters()[index].get());
+  }
+}
+
+}  // namespace
+
+// A helper class that allows the lock to be acquired in the middle of the scope
+// and unlocks at the end of scope if locked.
+class TraceLog::OptionalAutoLock {
+ public:
+  explicit OptionalAutoLock(Lock* lock) : lock_(lock), locked_(false) {}
+
+  ~OptionalAutoLock() {
+    if (locked_)
+      lock_->Release();
+  }
+
+  void EnsureAcquired() {
+    if (!locked_) {
+      lock_->Acquire();
+      locked_ = true;
+    }
+  }
+
+ private:
+  Lock* lock_;
+  bool locked_;
+  DISALLOW_COPY_AND_ASSIGN(OptionalAutoLock);
+};
+
+class TraceLog::ThreadLocalEventBuffer
+    : public MessageLoopCurrent::DestructionObserver,
+      public MemoryDumpProvider {
+ public:
+  explicit ThreadLocalEventBuffer(TraceLog* trace_log);
+  ~ThreadLocalEventBuffer() override;
+
+  TraceEvent* AddTraceEvent(TraceEventHandle* handle);
+
+  TraceEvent* GetEventByHandle(TraceEventHandle handle) {
+    if (!chunk_ || handle.chunk_seq != chunk_->seq() ||
+        handle.chunk_index != chunk_index_) {
+      return nullptr;
+    }
+
+    return chunk_->GetEventAt(handle.event_index);
+  }
+
+  int generation() const { return generation_; }
+
+ private:
+  // MessageLoopCurrent::DestructionObserver
+  void WillDestroyCurrentMessageLoop() override;
+
+  // MemoryDumpProvider implementation.
+  bool OnMemoryDump(const MemoryDumpArgs& args,
+                    ProcessMemoryDump* pmd) override;
+
+  void FlushWhileLocked();
+
+  void CheckThisIsCurrentBuffer() const {
+    DCHECK(trace_log_->thread_local_event_buffer_.Get() == this);
+  }
+
+  // Since TraceLog is a leaky singleton, trace_log_ will always be valid
+  // as long as the thread exists.
+  TraceLog* trace_log_;
+  std::unique_ptr<TraceBufferChunk> chunk_;
+  size_t chunk_index_;
+  int generation_;
+
+  DISALLOW_COPY_AND_ASSIGN(ThreadLocalEventBuffer);
+};
+
+TraceLog::ThreadLocalEventBuffer::ThreadLocalEventBuffer(TraceLog* trace_log)
+    : trace_log_(trace_log),
+      chunk_index_(0),
+      generation_(trace_log->generation()) {
+  // ThreadLocalEventBuffer is created only if the thread has a message loop, so
+  // the following message_loop won't be NULL.
+  MessageLoop* message_loop = MessageLoop::current();
+  message_loop->AddDestructionObserver(this);
+
+  // This is to report the local memory usage when memory-infra is enabled.
+  MemoryDumpManager::GetInstance()->RegisterDumpProvider(
+      this, "ThreadLocalEventBuffer", ThreadTaskRunnerHandle::Get());
+
+  AutoLock lock(trace_log->lock_);
+  trace_log->thread_message_loops_.insert(message_loop);
+}
+
+TraceLog::ThreadLocalEventBuffer::~ThreadLocalEventBuffer() {
+  CheckThisIsCurrentBuffer();
+  MessageLoop::current()->RemoveDestructionObserver(this);
+  MemoryDumpManager::GetInstance()->UnregisterDumpProvider(this);
+
+  {
+    AutoLock lock(trace_log_->lock_);
+    FlushWhileLocked();
+    trace_log_->thread_message_loops_.erase(MessageLoop::current());
+  }
+  trace_log_->thread_local_event_buffer_.Set(nullptr);
+}
+
+TraceEvent* TraceLog::ThreadLocalEventBuffer::AddTraceEvent(
+    TraceEventHandle* handle) {
+  CheckThisIsCurrentBuffer();
+
+  if (chunk_ && chunk_->IsFull()) {
+    AutoLock lock(trace_log_->lock_);
+    FlushWhileLocked();
+    chunk_.reset();
+  }
+  if (!chunk_) {
+    AutoLock lock(trace_log_->lock_);
+    chunk_ = trace_log_->logged_events_->GetChunk(&chunk_index_);
+    trace_log_->CheckIfBufferIsFullWhileLocked();
+  }
+  if (!chunk_)
+    return nullptr;
+
+  size_t event_index;
+  TraceEvent* trace_event = chunk_->AddTraceEvent(&event_index);
+  if (trace_event && handle)
+    MakeHandle(chunk_->seq(), chunk_index_, event_index, handle);
+
+  return trace_event;
+}
+
+void TraceLog::ThreadLocalEventBuffer::WillDestroyCurrentMessageLoop() {
+  delete this;
+}
+
+bool TraceLog::ThreadLocalEventBuffer::OnMemoryDump(const MemoryDumpArgs& args,
+                                                    ProcessMemoryDump* pmd) {
+  if (!chunk_)
+    return true;
+  std::string dump_base_name = StringPrintf(
+      "tracing/thread_%d", static_cast<int>(PlatformThread::CurrentId()));
+  TraceEventMemoryOverhead overhead;
+  chunk_->EstimateTraceMemoryOverhead(&overhead);
+  overhead.DumpInto(dump_base_name.c_str(), pmd);
+  return true;
+}
+
+void TraceLog::ThreadLocalEventBuffer::FlushWhileLocked() {
+  if (!chunk_)
+    return;
+
+  trace_log_->lock_.AssertAcquired();
+  if (trace_log_->CheckGeneration(generation_)) {
+    // Return the chunk to the buffer only if the generation matches.
+    trace_log_->logged_events_->ReturnChunk(chunk_index_, std::move(chunk_));
+  }
+  // Otherwise this method may be called from the destructor, or TraceLog will
+  // find the generation mismatch and delete this buffer soon.
+}
+
+void TraceLog::SetAddTraceEventOverride(
+    const AddTraceEventOverrideCallback& override) {
+  subtle::NoBarrier_Store(&trace_event_override_,
+                          reinterpret_cast<subtle::AtomicWord>(override));
+}
+
+struct TraceLog::RegisteredAsyncObserver {
+  explicit RegisteredAsyncObserver(WeakPtr<AsyncEnabledStateObserver> observer)
+      : observer(observer), task_runner(ThreadTaskRunnerHandle::Get()) {}
+  ~RegisteredAsyncObserver() = default;
+
+  WeakPtr<AsyncEnabledStateObserver> observer;
+  scoped_refptr<SequencedTaskRunner> task_runner;
+};
+
+TraceLogStatus::TraceLogStatus() : event_capacity(0), event_count(0) {}
+
+TraceLogStatus::~TraceLogStatus() = default;
+
+// static
+TraceLog* TraceLog::GetInstance() {
+  static base::NoDestructor<TraceLog> instance;
+  return instance.get();
+}
+
+// static
+void TraceLog::ResetForTesting() {
+  if (!g_trace_log_for_testing)
+    return;
+  CategoryRegistry::ResetForTesting();
+  g_trace_log_for_testing->~TraceLog();
+  new (g_trace_log_for_testing) TraceLog;
+}
+
+TraceLog::TraceLog()
+    : enabled_modes_(0),
+      num_traces_recorded_(0),
+      dispatching_to_observer_list_(false),
+      process_sort_index_(0),
+      process_id_hash_(0),
+      process_id_(0),
+      trace_options_(kInternalRecordUntilFull),
+      trace_config_(TraceConfig()),
+      thread_shared_chunk_index_(0),
+      generation_(0),
+      use_worker_thread_(false),
+      trace_event_override_(0),
+      filter_factory_for_testing_(nullptr) {
+  CategoryRegistry::Initialize();
+
+#if defined(OS_NACL)  // NaCl shouldn't expose the process id.
+  SetProcessID(0);
+#else
+  SetProcessID(static_cast<int>(GetCurrentProcId()));
+#endif
+
+// Linux renderer processes and Android O processes are not allowed to read
+// "proc/stat" file, crbug.com/788870.
+#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS))
+  process_creation_time_ = CurrentProcessInfo::CreationTime();
+#else
+  // Use approximate time when creation time is not available.
+  process_creation_time_ = TRACE_TIME_NOW();
+#endif
+
+  logged_events_.reset(CreateTraceBuffer());
+
+  MemoryDumpManager::GetInstance()->RegisterDumpProvider(this, "TraceLog",
+                                                         nullptr);
+  g_trace_log_for_testing = this;
+}
+
+TraceLog::~TraceLog() = default;
+
+void TraceLog::InitializeThreadLocalEventBufferIfSupported() {
+  // A ThreadLocalEventBuffer needs the message loop
+  // - to know when the thread exits;
+  // - to handle the final flush.
+  // For a thread without a message loop or the message loop may be blocked, the
+  // trace events will be added into the main buffer directly.
+  if (thread_blocks_message_loop_.Get() || !MessageLoopCurrent::IsSet())
+    return;
+  HEAP_PROFILER_SCOPED_IGNORE;
+  auto* thread_local_event_buffer = thread_local_event_buffer_.Get();
+  if (thread_local_event_buffer &&
+      !CheckGeneration(thread_local_event_buffer->generation())) {
+    delete thread_local_event_buffer;
+    thread_local_event_buffer = nullptr;
+  }
+  if (!thread_local_event_buffer) {
+    thread_local_event_buffer = new ThreadLocalEventBuffer(this);
+    thread_local_event_buffer_.Set(thread_local_event_buffer);
+  }
+}
+
+bool TraceLog::OnMemoryDump(const MemoryDumpArgs& args,
+                            ProcessMemoryDump* pmd) {
+  // TODO(ssid): Use MemoryDumpArgs to create light dumps when requested
+  // (crbug.com/499731).
+  TraceEventMemoryOverhead overhead;
+  overhead.Add(TraceEventMemoryOverhead::kOther, sizeof(*this));
+  {
+    AutoLock lock(lock_);
+    if (logged_events_)
+      logged_events_->EstimateTraceMemoryOverhead(&overhead);
+
+    for (auto& metadata_event : metadata_events_)
+      metadata_event->EstimateTraceMemoryOverhead(&overhead);
+  }
+  overhead.AddSelf();
+  overhead.DumpInto("tracing/main_trace_log", pmd);
+  return true;
+}
+
+const unsigned char* TraceLog::GetCategoryGroupEnabled(
+    const char* category_group) {
+  TraceLog* tracelog = GetInstance();
+  if (!tracelog) {
+    DCHECK(!CategoryRegistry::kCategoryAlreadyShutdown->is_enabled());
+    return CategoryRegistry::kCategoryAlreadyShutdown->state_ptr();
+  }
+  TraceCategory* category = CategoryRegistry::GetCategoryByName(category_group);
+  if (!category) {
+    // Slow path: in the case of a new category we have to repeat the check
+    // holding the lock, as multiple threads might have reached this point
+    // at the same time.
+    auto category_initializer = [](TraceCategory* category) {
+      TraceLog::GetInstance()->UpdateCategoryState(category);
+    };
+    AutoLock lock(tracelog->lock_);
+    CategoryRegistry::GetOrCreateCategoryLocked(
+        category_group, category_initializer, &category);
+  }
+  DCHECK(category->state_ptr());
+  return category->state_ptr();
+}
+
+const char* TraceLog::GetCategoryGroupName(
+    const unsigned char* category_group_enabled) {
+  return CategoryRegistry::GetCategoryByStatePtr(category_group_enabled)
+      ->name();
+}
+
+void TraceLog::UpdateCategoryState(TraceCategory* category) {
+  lock_.AssertAcquired();
+  DCHECK(category->is_valid());
+  unsigned char state_flags = 0;
+  if (enabled_modes_ & RECORDING_MODE &&
+      trace_config_.IsCategoryGroupEnabled(category->name())) {
+    state_flags |= TraceCategory::ENABLED_FOR_RECORDING;
+  }
+
+  // TODO(primiano): this is a temporary workaround for catapult:#2341,
+  // to guarantee that metadata events are always added even if the category
+  // filter is "-*". See crbug.com/618054 for more details and long-term fix.
+  if (enabled_modes_ & RECORDING_MODE &&
+      category == CategoryRegistry::kCategoryMetadata) {
+    state_flags |= TraceCategory::ENABLED_FOR_RECORDING;
+  }
+
+#if defined(OS_WIN)
+  if (base::trace_event::TraceEventETWExport::IsCategoryGroupEnabled(
+          category->name())) {
+    state_flags |= TraceCategory::ENABLED_FOR_ETW_EXPORT;
+  }
+#endif
+
+  uint32_t enabled_filters_bitmap = 0;
+  int index = 0;
+  for (const auto& event_filter : enabled_event_filters_) {
+    if (event_filter.IsCategoryGroupEnabled(category->name())) {
+      state_flags |= TraceCategory::ENABLED_FOR_FILTERING;
+      DCHECK(GetCategoryGroupFilters()[index]);
+      enabled_filters_bitmap |= 1 << index;
+    }
+    if (index++ >= MAX_TRACE_EVENT_FILTERS) {
+      NOTREACHED();
+      break;
+    }
+  }
+  category->set_enabled_filters(enabled_filters_bitmap);
+  category->set_state(state_flags);
+}
+
+void TraceLog::UpdateCategoryRegistry() {
+  lock_.AssertAcquired();
+  CreateFiltersForTraceConfig();
+  for (TraceCategory& category : CategoryRegistry::GetAllCategories()) {
+    UpdateCategoryState(&category);
+  }
+}
+
+void TraceLog::CreateFiltersForTraceConfig() {
+  if (!(enabled_modes_ & FILTERING_MODE))
+    return;
+
+  // Filters were already added and tracing could be enabled. Filters list
+  // cannot be changed when trace events are using them.
+  if (GetCategoryGroupFilters().size())
+    return;
+
+  for (auto& filter_config : enabled_event_filters_) {
+    if (GetCategoryGroupFilters().size() >= MAX_TRACE_EVENT_FILTERS) {
+      NOTREACHED()
+          << "Too many trace event filters installed in the current session";
+      break;
+    }
+
+    std::unique_ptr<TraceEventFilter> new_filter;
+    const std::string& predicate_name = filter_config.predicate_name();
+    if (predicate_name == EventNameFilter::kName) {
+      auto whitelist = std::make_unique<std::unordered_set<std::string>>();
+      CHECK(filter_config.GetArgAsSet("event_name_whitelist", &*whitelist));
+      new_filter = std::make_unique<EventNameFilter>(std::move(whitelist));
+    } else if (predicate_name == HeapProfilerEventFilter::kName) {
+      new_filter = std::make_unique<HeapProfilerEventFilter>();
+    } else {
+      if (filter_factory_for_testing_)
+        new_filter = filter_factory_for_testing_(predicate_name);
+      CHECK(new_filter) << "Unknown trace filter " << predicate_name;
+    }
+    GetCategoryGroupFilters().push_back(std::move(new_filter));
+  }
+}
+
+void TraceLog::GetKnownCategoryGroups(
+    std::vector<std::string>* category_groups) {
+  for (const auto& category : CategoryRegistry::GetAllCategories()) {
+    if (!CategoryRegistry::IsBuiltinCategory(&category))
+      category_groups->push_back(category.name());
+  }
+}
+
+void TraceLog::SetEnabled(const TraceConfig& trace_config,
+                          uint8_t modes_to_enable) {
+  DCHECK(trace_config.process_filter_config().IsEnabled(process_id_));
+
+  std::vector<EnabledStateObserver*> observer_list;
+  std::map<AsyncEnabledStateObserver*, RegisteredAsyncObserver> observer_map;
+  {
+    AutoLock lock(lock_);
+
+    // Can't enable tracing when Flush() is in progress.
+    DCHECK(!flush_task_runner_);
+
+    InternalTraceOptions new_options =
+        GetInternalOptionsFromTraceConfig(trace_config);
+
+    InternalTraceOptions old_options = trace_options();
+
+    if (dispatching_to_observer_list_) {
+      // TODO(ssid): Change to NOTREACHED after fixing crbug.com/625170.
+      DLOG(ERROR)
+          << "Cannot manipulate TraceLog::Enabled state from an observer.";
+      return;
+    }
+
+    // Clear all filters from previous tracing session. These filters are not
+    // cleared at the end of tracing because some threads which hit trace event
+    // when disabling, could try to use the filters.
+    if (!enabled_modes_)
+      GetCategoryGroupFilters().clear();
+
+    // Update trace config for recording.
+    const bool already_recording = enabled_modes_ & RECORDING_MODE;
+    if (modes_to_enable & RECORDING_MODE) {
+      if (already_recording) {
+        // TODO(ssid): Stop suporting enabling of RECODING_MODE when already
+        // enabled crbug.com/625170.
+        DCHECK_EQ(new_options, old_options) << "Attempting to re-enable "
+                                               "tracing with a different set "
+                                               "of options.";
+        trace_config_.Merge(trace_config);
+      } else {
+        trace_config_ = trace_config;
+      }
+    }
+
+    // Update event filters only if filtering was not enabled.
+    if (modes_to_enable & FILTERING_MODE && enabled_event_filters_.empty()) {
+      DCHECK(!trace_config.event_filters().empty());
+      enabled_event_filters_ = trace_config.event_filters();
+    }
+    // Keep the |trace_config_| updated with only enabled filters in case anyone
+    // tries to read it using |GetCurrentTraceConfig| (even if filters are
+    // empty).
+    trace_config_.SetEventFilters(enabled_event_filters_);
+
+    enabled_modes_ |= modes_to_enable;
+    UpdateCategoryRegistry();
+
+    // Do not notify observers or create trace buffer if only enabled for
+    // filtering or if recording was already enabled.
+    if (!(modes_to_enable & RECORDING_MODE) || already_recording)
+      return;
+
+    if (new_options != old_options) {
+      subtle::NoBarrier_Store(&trace_options_, new_options);
+      UseNextTraceBuffer();
+    }
+
+    num_traces_recorded_++;
+
+    UpdateCategoryRegistry();
+
+    dispatching_to_observer_list_ = true;
+    observer_list = enabled_state_observer_list_;
+    observer_map = async_observers_;
+  }
+  // Notify observers outside the lock in case they trigger trace events.
+  for (EnabledStateObserver* observer : observer_list)
+    observer->OnTraceLogEnabled();
+  for (const auto& it : observer_map) {
+    it.second.task_runner->PostTask(
+        FROM_HERE, BindOnce(&AsyncEnabledStateObserver::OnTraceLogEnabled,
+                            it.second.observer));
+  }
+
+  {
+    AutoLock lock(lock_);
+    dispatching_to_observer_list_ = false;
+  }
+}
+
+void TraceLog::SetArgumentFilterPredicate(
+    const ArgumentFilterPredicate& argument_filter_predicate) {
+  AutoLock lock(lock_);
+  DCHECK(!argument_filter_predicate.is_null());
+  DCHECK(argument_filter_predicate_.is_null());
+  argument_filter_predicate_ = argument_filter_predicate;
+}
+
+TraceLog::InternalTraceOptions TraceLog::GetInternalOptionsFromTraceConfig(
+    const TraceConfig& config) {
+  InternalTraceOptions ret = config.IsArgumentFilterEnabled()
+                                 ? kInternalEnableArgumentFilter
+                                 : kInternalNone;
+  switch (config.GetTraceRecordMode()) {
+    case RECORD_UNTIL_FULL:
+      return ret | kInternalRecordUntilFull;
+    case RECORD_CONTINUOUSLY:
+      return ret | kInternalRecordContinuously;
+    case ECHO_TO_CONSOLE:
+      return ret | kInternalEchoToConsole;
+    case RECORD_AS_MUCH_AS_POSSIBLE:
+      return ret | kInternalRecordAsMuchAsPossible;
+  }
+  NOTREACHED();
+  return kInternalNone;
+}
+
+TraceConfig TraceLog::GetCurrentTraceConfig() const {
+  AutoLock lock(lock_);
+  return trace_config_;
+}
+
+void TraceLog::SetDisabled() {
+  AutoLock lock(lock_);
+  SetDisabledWhileLocked(RECORDING_MODE);
+}
+
+void TraceLog::SetDisabled(uint8_t modes_to_disable) {
+  AutoLock lock(lock_);
+  SetDisabledWhileLocked(modes_to_disable);
+}
+
+void TraceLog::SetDisabledWhileLocked(uint8_t modes_to_disable) {
+  lock_.AssertAcquired();
+
+  if (!(enabled_modes_ & modes_to_disable))
+    return;
+
+  if (dispatching_to_observer_list_) {
+    // TODO(ssid): Change to NOTREACHED after fixing crbug.com/625170.
+    DLOG(ERROR)
+        << "Cannot manipulate TraceLog::Enabled state from an observer.";
+    return;
+  }
+
+  bool is_recording_mode_disabled =
+      (enabled_modes_ & RECORDING_MODE) && (modes_to_disable & RECORDING_MODE);
+  enabled_modes_ &= ~modes_to_disable;
+
+  if (modes_to_disable & FILTERING_MODE)
+    enabled_event_filters_.clear();
+
+  if (modes_to_disable & RECORDING_MODE)
+    trace_config_.Clear();
+
+  UpdateCategoryRegistry();
+
+  // Add metadata events and notify observers only if recording mode was
+  // disabled now.
+  if (!is_recording_mode_disabled)
+    return;
+
+  AddMetadataEventsWhileLocked();
+
+  // Remove metadata events so they will not get added to a subsequent trace.
+  metadata_events_.clear();
+
+  dispatching_to_observer_list_ = true;
+  std::vector<EnabledStateObserver*> observer_list =
+      enabled_state_observer_list_;
+  std::map<AsyncEnabledStateObserver*, RegisteredAsyncObserver> observer_map =
+      async_observers_;
+
+  {
+    // Dispatch to observers outside the lock in case the observer triggers a
+    // trace event.
+    AutoUnlock unlock(lock_);
+    for (EnabledStateObserver* observer : observer_list)
+      observer->OnTraceLogDisabled();
+    for (const auto& it : observer_map) {
+      it.second.task_runner->PostTask(
+          FROM_HERE, BindOnce(&AsyncEnabledStateObserver::OnTraceLogDisabled,
+                              it.second.observer));
+    }
+  }
+  dispatching_to_observer_list_ = false;
+}
+
+int TraceLog::GetNumTracesRecorded() {
+  AutoLock lock(lock_);
+  if (!IsEnabled())
+    return -1;
+  return num_traces_recorded_;
+}
+
+void TraceLog::AddEnabledStateObserver(EnabledStateObserver* listener) {
+  AutoLock lock(lock_);
+  enabled_state_observer_list_.push_back(listener);
+}
+
+void TraceLog::RemoveEnabledStateObserver(EnabledStateObserver* listener) {
+  AutoLock lock(lock_);
+  std::vector<EnabledStateObserver*>::iterator it =
+      std::find(enabled_state_observer_list_.begin(),
+                enabled_state_observer_list_.end(), listener);
+  if (it != enabled_state_observer_list_.end())
+    enabled_state_observer_list_.erase(it);
+}
+
+bool TraceLog::HasEnabledStateObserver(EnabledStateObserver* listener) const {
+  AutoLock lock(lock_);
+  return ContainsValue(enabled_state_observer_list_, listener);
+}
+
+TraceLogStatus TraceLog::GetStatus() const {
+  AutoLock lock(lock_);
+  TraceLogStatus result;
+  result.event_capacity = static_cast<uint32_t>(logged_events_->Capacity());
+  result.event_count = static_cast<uint32_t>(logged_events_->Size());
+  return result;
+}
+
+bool TraceLog::BufferIsFull() const {
+  AutoLock lock(lock_);
+  return logged_events_->IsFull();
+}
+
+TraceEvent* TraceLog::AddEventToThreadSharedChunkWhileLocked(
+    TraceEventHandle* handle,
+    bool check_buffer_is_full) {
+  lock_.AssertAcquired();
+
+  if (thread_shared_chunk_ && thread_shared_chunk_->IsFull()) {
+    logged_events_->ReturnChunk(thread_shared_chunk_index_,
+                                std::move(thread_shared_chunk_));
+  }
+
+  if (!thread_shared_chunk_) {
+    thread_shared_chunk_ =
+        logged_events_->GetChunk(&thread_shared_chunk_index_);
+    if (check_buffer_is_full)
+      CheckIfBufferIsFullWhileLocked();
+  }
+  if (!thread_shared_chunk_)
+    return nullptr;
+
+  size_t event_index;
+  TraceEvent* trace_event = thread_shared_chunk_->AddTraceEvent(&event_index);
+  if (trace_event && handle) {
+    MakeHandle(thread_shared_chunk_->seq(), thread_shared_chunk_index_,
+               event_index, handle);
+  }
+  return trace_event;
+}
+
+void TraceLog::CheckIfBufferIsFullWhileLocked() {
+  lock_.AssertAcquired();
+  if (logged_events_->IsFull()) {
+    if (buffer_limit_reached_timestamp_.is_null()) {
+      buffer_limit_reached_timestamp_ = OffsetNow();
+    }
+    SetDisabledWhileLocked(RECORDING_MODE);
+  }
+}
+
+// Flush() works as the following:
+// 1. Flush() is called in thread A whose task runner is saved in
+//    flush_task_runner_;
+// 2. If thread_message_loops_ is not empty, thread A posts task to each message
+//    loop to flush the thread local buffers; otherwise finish the flush;
+// 3. FlushCurrentThread() deletes the thread local event buffer:
+//    - The last batch of events of the thread are flushed into the main buffer;
+//    - The message loop will be removed from thread_message_loops_;
+//    If this is the last message loop, finish the flush;
+// 4. If any thread hasn't finish its flush in time, finish the flush.
+void TraceLog::Flush(const TraceLog::OutputCallback& cb,
+                     bool use_worker_thread) {
+  FlushInternal(cb, use_worker_thread, false);
+}
+
+void TraceLog::CancelTracing(const OutputCallback& cb) {
+  SetDisabled();
+  FlushInternal(cb, false, true);
+}
+
+void TraceLog::FlushInternal(const TraceLog::OutputCallback& cb,
+                             bool use_worker_thread,
+                             bool discard_events) {
+  use_worker_thread_ = use_worker_thread;
+  if (IsEnabled()) {
+    // Can't flush when tracing is enabled because otherwise PostTask would
+    // - generate more trace events;
+    // - deschedule the calling thread on some platforms causing inaccurate
+    //   timing of the trace events.
+    scoped_refptr<RefCountedString> empty_result = new RefCountedString;
+    if (!cb.is_null())
+      cb.Run(empty_result, false);
+    LOG(WARNING) << "Ignored TraceLog::Flush called when tracing is enabled";
+    return;
+  }
+
+  int gen = generation();
+  // Copy of thread_message_loops_ to be used without locking.
+  std::vector<scoped_refptr<SingleThreadTaskRunner>>
+      thread_message_loop_task_runners;
+  {
+    AutoLock lock(lock_);
+    DCHECK(!flush_task_runner_);
+    flush_task_runner_ = SequencedTaskRunnerHandle::IsSet()
+                             ? SequencedTaskRunnerHandle::Get()
+                             : nullptr;
+    DCHECK(thread_message_loops_.empty() || flush_task_runner_);
+    flush_output_callback_ = cb;
+
+    if (thread_shared_chunk_) {
+      logged_events_->ReturnChunk(thread_shared_chunk_index_,
+                                  std::move(thread_shared_chunk_));
+    }
+
+    for (MessageLoop* loop : thread_message_loops_)
+      thread_message_loop_task_runners.push_back(loop->task_runner());
+  }
+
+  if (!thread_message_loop_task_runners.empty()) {
+    for (auto& task_runner : thread_message_loop_task_runners) {
+      task_runner->PostTask(
+          FROM_HERE, BindOnce(&TraceLog::FlushCurrentThread, Unretained(this),
+                              gen, discard_events));
+    }
+    flush_task_runner_->PostDelayedTask(
+        FROM_HERE,
+        BindOnce(&TraceLog::OnFlushTimeout, Unretained(this), gen,
+                 discard_events),
+        TimeDelta::FromMilliseconds(kThreadFlushTimeoutMs));
+    return;
+  }
+
+  FinishFlush(gen, discard_events);
+}
+
+// Usually it runs on a different thread.
+void TraceLog::ConvertTraceEventsToTraceFormat(
+    std::unique_ptr<TraceBuffer> logged_events,
+    const OutputCallback& flush_output_callback,
+    const ArgumentFilterPredicate& argument_filter_predicate) {
+  if (flush_output_callback.is_null())
+    return;
+
+  HEAP_PROFILER_SCOPED_IGNORE;
+  // The callback need to be called at least once even if there is no events
+  // to let the caller know the completion of flush.
+  scoped_refptr<RefCountedString> json_events_str_ptr = new RefCountedString();
+  const size_t kReserveCapacity = kTraceEventBufferSizeInBytes * 5 / 4;
+  json_events_str_ptr->data().reserve(kReserveCapacity);
+  while (const TraceBufferChunk* chunk = logged_events->NextChunk()) {
+    for (size_t j = 0; j < chunk->size(); ++j) {
+      size_t size = json_events_str_ptr->size();
+      if (size > kTraceEventBufferSizeInBytes) {
+        flush_output_callback.Run(json_events_str_ptr, true);
+        json_events_str_ptr = new RefCountedString();
+        json_events_str_ptr->data().reserve(kReserveCapacity);
+      } else if (size) {
+        json_events_str_ptr->data().append(",\n");
+      }
+      chunk->GetEventAt(j)->AppendAsJSON(&(json_events_str_ptr->data()),
+                                         argument_filter_predicate);
+    }
+  }
+  flush_output_callback.Run(json_events_str_ptr, false);
+}
+
+void TraceLog::FinishFlush(int generation, bool discard_events) {
+  std::unique_ptr<TraceBuffer> previous_logged_events;
+  OutputCallback flush_output_callback;
+  ArgumentFilterPredicate argument_filter_predicate;
+
+  if (!CheckGeneration(generation))
+    return;
+
+  {
+    AutoLock lock(lock_);
+
+    previous_logged_events.swap(logged_events_);
+    UseNextTraceBuffer();
+    thread_message_loops_.clear();
+
+    flush_task_runner_ = nullptr;
+    flush_output_callback = flush_output_callback_;
+    flush_output_callback_.Reset();
+
+    if (trace_options() & kInternalEnableArgumentFilter) {
+      CHECK(!argument_filter_predicate_.is_null());
+      argument_filter_predicate = argument_filter_predicate_;
+    }
+  }
+
+  if (discard_events) {
+    if (!flush_output_callback.is_null()) {
+      scoped_refptr<RefCountedString> empty_result = new RefCountedString;
+      flush_output_callback.Run(empty_result, false);
+    }
+    return;
+  }
+
+  if (use_worker_thread_) {
+    base::PostTaskWithTraits(
+        FROM_HERE,
+        {MayBlock(), TaskPriority::BACKGROUND,
+         TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
+        BindOnce(&TraceLog::ConvertTraceEventsToTraceFormat,
+                 std::move(previous_logged_events), flush_output_callback,
+                 argument_filter_predicate));
+    return;
+  }
+
+  ConvertTraceEventsToTraceFormat(std::move(previous_logged_events),
+                                  flush_output_callback,
+                                  argument_filter_predicate);
+}
+
+// Run in each thread holding a local event buffer.
+void TraceLog::FlushCurrentThread(int generation, bool discard_events) {
+  {
+    AutoLock lock(lock_);
+    if (!CheckGeneration(generation) || !flush_task_runner_) {
+      // This is late. The corresponding flush has finished.
+      return;
+    }
+  }
+
+  // This will flush the thread local buffer.
+  delete thread_local_event_buffer_.Get();
+
+  // Scheduler uses TRACE_EVENT macros when posting a task, which can lead
+  // to acquiring a tracing lock. Given that posting a task requires grabbing
+  // a scheduler lock, we need to post this task outside tracing lock to avoid
+  // deadlocks.
+  scoped_refptr<SequencedTaskRunner> cached_flush_task_runner;
+  {
+    AutoLock lock(lock_);
+    cached_flush_task_runner = flush_task_runner_;
+    if (!CheckGeneration(generation) || !flush_task_runner_ ||
+        !thread_message_loops_.empty())
+      return;
+  }
+  cached_flush_task_runner->PostTask(
+      FROM_HERE, BindOnce(&TraceLog::FinishFlush, Unretained(this), generation,
+                          discard_events));
+}
+
+void TraceLog::OnFlushTimeout(int generation, bool discard_events) {
+  {
+    AutoLock lock(lock_);
+    if (!CheckGeneration(generation) || !flush_task_runner_) {
+      // Flush has finished before timeout.
+      return;
+    }
+
+    LOG(WARNING)
+        << "The following threads haven't finished flush in time. "
+           "If this happens stably for some thread, please call "
+           "TraceLog::GetInstance()->SetCurrentThreadBlocksMessageLoop() from "
+           "the thread to avoid its trace events from being lost.";
+    for (hash_set<MessageLoop*>::const_iterator it =
+             thread_message_loops_.begin();
+         it != thread_message_loops_.end(); ++it) {
+      LOG(WARNING) << "Thread: " << (*it)->GetThreadName();
+    }
+  }
+  FinishFlush(generation, discard_events);
+}
+
+void TraceLog::UseNextTraceBuffer() {
+  logged_events_.reset(CreateTraceBuffer());
+  subtle::NoBarrier_AtomicIncrement(&generation_, 1);
+  thread_shared_chunk_.reset();
+  thread_shared_chunk_index_ = 0;
+}
+
+TraceEventHandle TraceLog::AddTraceEvent(
+    char phase,
+    const unsigned char* category_group_enabled,
+    const char* name,
+    const char* scope,
+    unsigned long long id,
+    int num_args,
+    const char* const* arg_names,
+    const unsigned char* arg_types,
+    const unsigned long long* arg_values,
+    std::unique_ptr<ConvertableToTraceFormat>* convertable_values,
+    unsigned int flags) {
+  int thread_id = static_cast<int>(base::PlatformThread::CurrentId());
+  base::TimeTicks now = TRACE_TIME_TICKS_NOW();
+  return AddTraceEventWithThreadIdAndTimestamp(
+      phase,
+      category_group_enabled,
+      name,
+      scope,
+      id,
+      trace_event_internal::kNoId,  // bind_id
+      thread_id,
+      now,
+      num_args,
+      arg_names,
+      arg_types,
+      arg_values,
+      convertable_values,
+      flags);
+}
+
+TraceEventHandle TraceLog::AddTraceEventWithBindId(
+    char phase,
+    const unsigned char* category_group_enabled,
+    const char* name,
+    const char* scope,
+    unsigned long long id,
+    unsigned long long bind_id,
+    int num_args,
+    const char* const* arg_names,
+    const unsigned char* arg_types,
+    const unsigned long long* arg_values,
+    std::unique_ptr<ConvertableToTraceFormat>* convertable_values,
+    unsigned int flags) {
+  int thread_id = static_cast<int>(base::PlatformThread::CurrentId());
+  base::TimeTicks now = TRACE_TIME_TICKS_NOW();
+  return AddTraceEventWithThreadIdAndTimestamp(
+      phase,
+      category_group_enabled,
+      name,
+      scope,
+      id,
+      bind_id,
+      thread_id,
+      now,
+      num_args,
+      arg_names,
+      arg_types,
+      arg_values,
+      convertable_values,
+      flags | TRACE_EVENT_FLAG_HAS_CONTEXT_ID);
+}
+
+TraceEventHandle TraceLog::AddTraceEventWithProcessId(
+    char phase,
+    const unsigned char* category_group_enabled,
+    const char* name,
+    const char* scope,
+    unsigned long long id,
+    int process_id,
+    int num_args,
+    const char* const* arg_names,
+    const unsigned char* arg_types,
+    const unsigned long long* arg_values,
+    std::unique_ptr<ConvertableToTraceFormat>* convertable_values,
+    unsigned int flags) {
+  base::TimeTicks now = TRACE_TIME_TICKS_NOW();
+  return AddTraceEventWithThreadIdAndTimestamp(
+      phase,
+      category_group_enabled,
+      name,
+      scope,
+      id,
+      trace_event_internal::kNoId,  // bind_id
+      process_id,
+      now,
+      num_args,
+      arg_names,
+      arg_types,
+      arg_values,
+      convertable_values,
+      flags | TRACE_EVENT_FLAG_HAS_PROCESS_ID);
+}
+
+// Handle legacy calls to AddTraceEventWithThreadIdAndTimestamp
+// with kNoId as bind_id
+TraceEventHandle TraceLog::AddTraceEventWithThreadIdAndTimestamp(
+    char phase,
+    const unsigned char* category_group_enabled,
+    const char* name,
+    const char* scope,
+    unsigned long long id,
+    int thread_id,
+    const TimeTicks& timestamp,
+    int num_args,
+    const char* const* arg_names,
+    const unsigned char* arg_types,
+    const unsigned long long* arg_values,
+    std::unique_ptr<ConvertableToTraceFormat>* convertable_values,
+    unsigned int flags) {
+  return AddTraceEventWithThreadIdAndTimestamp(
+      phase,
+      category_group_enabled,
+      name,
+      scope,
+      id,
+      trace_event_internal::kNoId,  // bind_id
+      thread_id,
+      timestamp,
+      num_args,
+      arg_names,
+      arg_types,
+      arg_values,
+      convertable_values,
+      flags);
+}
+
+TraceEventHandle TraceLog::AddTraceEventWithThreadIdAndTimestamp(
+    char phase,
+    const unsigned char* category_group_enabled,
+    const char* name,
+    const char* scope,
+    unsigned long long id,
+    unsigned long long bind_id,
+    int thread_id,
+    const TimeTicks& timestamp,
+    int num_args,
+    const char* const* arg_names,
+    const unsigned char* arg_types,
+    const unsigned long long* arg_values,
+    std::unique_ptr<ConvertableToTraceFormat>* convertable_values,
+    unsigned int flags) {
+  TraceEventHandle handle = {0, 0, 0};
+  if (!*category_group_enabled)
+    return handle;
+
+  // Avoid re-entrance of AddTraceEvent. This may happen in GPU process when
+  // ECHO_TO_CONSOLE is enabled: AddTraceEvent -> LOG(ERROR) ->
+  // GpuProcessLogMessageHandler -> PostPendingTask -> TRACE_EVENT ...
+  if (thread_is_in_trace_event_.Get())
+    return handle;
+
+  AutoThreadLocalBoolean thread_is_in_trace_event(&thread_is_in_trace_event_);
+
+  DCHECK(name);
+  DCHECK(!timestamp.is_null());
+
+  if (flags & TRACE_EVENT_FLAG_MANGLE_ID) {
+    if ((flags & TRACE_EVENT_FLAG_FLOW_IN) ||
+        (flags & TRACE_EVENT_FLAG_FLOW_OUT))
+      bind_id = MangleEventId(bind_id);
+    id = MangleEventId(id);
+  }
+
+  TimeTicks offset_event_timestamp = OffsetTimestamp(timestamp);
+  ThreadTicks thread_now = ThreadNow();
+
+  ThreadLocalEventBuffer* thread_local_event_buffer = nullptr;
+  if (*category_group_enabled & RECORDING_MODE) {
+    // |thread_local_event_buffer_| can be null if the current thread doesn't
+    // have a message loop or the message loop is blocked.
+    InitializeThreadLocalEventBufferIfSupported();
+    thread_local_event_buffer = thread_local_event_buffer_.Get();
+  }
+
+  // Check and update the current thread name only if the event is for the
+  // current thread to avoid locks in most cases.
+  if (thread_id == static_cast<int>(PlatformThread::CurrentId())) {
+    const char* new_name =
+        ThreadIdNameManager::GetInstance()->GetName(thread_id);
+    // Check if the thread name has been set or changed since the previous
+    // call (if any), but don't bother if the new name is empty. Note this will
+    // not detect a thread name change within the same char* buffer address: we
+    // favor common case performance over corner case correctness.
+    static auto* current_thread_name = new ThreadLocalPointer<const char>();
+    if (new_name != current_thread_name->Get() && new_name && *new_name) {
+      current_thread_name->Set(new_name);
+
+      AutoLock thread_info_lock(thread_info_lock_);
+
+      auto existing_name = thread_names_.find(thread_id);
+      if (existing_name == thread_names_.end()) {
+        // This is a new thread id, and a new name.
+        thread_names_[thread_id] = new_name;
+      } else {
+        // This is a thread id that we've seen before, but potentially with a
+        // new name.
+        std::vector<StringPiece> existing_names = base::SplitStringPiece(
+            existing_name->second, ",", base::KEEP_WHITESPACE,
+            base::SPLIT_WANT_NONEMPTY);
+        if (!ContainsValue(existing_names, new_name)) {
+          if (!existing_names.empty())
+            existing_name->second.push_back(',');
+          existing_name->second.append(new_name);
+        }
+      }
+    }
+  }
+
+#if defined(OS_WIN)
+  // This is done sooner rather than later, to avoid creating the event and
+  // acquiring the lock, which is not needed for ETW as it's already threadsafe.
+  if (*category_group_enabled & TraceCategory::ENABLED_FOR_ETW_EXPORT)
+    TraceEventETWExport::AddEvent(phase, category_group_enabled, name, id,
+                                  num_args, arg_names, arg_types, arg_values,
+                                  convertable_values);
+#endif  // OS_WIN
+
+  AddTraceEventOverrideCallback trace_event_override =
+      reinterpret_cast<AddTraceEventOverrideCallback>(
+          subtle::NoBarrier_Load(&trace_event_override_));
+  if (trace_event_override) {
+    TraceEvent new_trace_event;
+    // If we have an override in place for events, rather than sending
+    // them to the tracelog, we don't have a way of going back and updating
+    // the duration of _COMPLETE events. Instead, we emit separate _BEGIN
+    // and _END events.
+    if (phase == TRACE_EVENT_PHASE_COMPLETE)
+      phase = TRACE_EVENT_PHASE_BEGIN;
+
+    new_trace_event.Initialize(thread_id, offset_event_timestamp, thread_now,
+                               phase, category_group_enabled, name, scope, id,
+                               bind_id, num_args, arg_names, arg_types,
+                               arg_values, convertable_values, flags);
+
+    trace_event_override(new_trace_event);
+    return handle;
+  }
+
+  std::string console_message;
+  std::unique_ptr<TraceEvent> filtered_trace_event;
+  bool disabled_by_filters = false;
+  if (*category_group_enabled & TraceCategory::ENABLED_FOR_FILTERING) {
+    std::unique_ptr<TraceEvent> new_trace_event(new TraceEvent);
+    new_trace_event->Initialize(thread_id, offset_event_timestamp, thread_now,
+                                phase, category_group_enabled, name, scope, id,
+                                bind_id, num_args, arg_names, arg_types,
+                                arg_values, convertable_values, flags);
+
+    disabled_by_filters = true;
+    ForEachCategoryFilter(
+        category_group_enabled, [&new_trace_event, &disabled_by_filters](
+                                    TraceEventFilter* trace_event_filter) {
+          if (trace_event_filter->FilterTraceEvent(*new_trace_event))
+            disabled_by_filters = false;
+        });
+    if (!disabled_by_filters)
+      filtered_trace_event = std::move(new_trace_event);
+  }
+
+  // If enabled for recording, the event should be added only if one of the
+  // filters indicates or category is not enabled for filtering.
+  if ((*category_group_enabled & TraceCategory::ENABLED_FOR_RECORDING) &&
+      !disabled_by_filters) {
+    OptionalAutoLock lock(&lock_);
+
+    TraceEvent* trace_event = nullptr;
+    if (thread_local_event_buffer) {
+      trace_event = thread_local_event_buffer->AddTraceEvent(&handle);
+    } else {
+      lock.EnsureAcquired();
+      trace_event = AddEventToThreadSharedChunkWhileLocked(&handle, true);
+    }
+
+    if (trace_event) {
+      if (filtered_trace_event) {
+        trace_event->MoveFrom(std::move(filtered_trace_event));
+      } else {
+        trace_event->Initialize(thread_id, offset_event_timestamp, thread_now,
+                                phase, category_group_enabled, name, scope, id,
+                                bind_id, num_args, arg_names, arg_types,
+                                arg_values, convertable_values, flags);
+      }
+
+#if defined(OS_ANDROID)
+      trace_event->SendToATrace();
+#endif
+    }
+
+    if (trace_options() & kInternalEchoToConsole) {
+      console_message = EventToConsoleMessage(
+          phase == TRACE_EVENT_PHASE_COMPLETE ? TRACE_EVENT_PHASE_BEGIN : phase,
+          timestamp, trace_event);
+    }
+  }
+
+  if (!console_message.empty())
+    LOG(ERROR) << console_message;
+
+  return handle;
+}
+
+void TraceLog::AddMetadataEvent(
+    const unsigned char* category_group_enabled,
+    const char* name,
+    int num_args,
+    const char* const* arg_names,
+    const unsigned char* arg_types,
+    const unsigned long long* arg_values,
+    std::unique_ptr<ConvertableToTraceFormat>* convertable_values,
+    unsigned int flags) {
+  HEAP_PROFILER_SCOPED_IGNORE;
+  std::unique_ptr<TraceEvent> trace_event(new TraceEvent);
+  int thread_id = static_cast<int>(base::PlatformThread::CurrentId());
+  ThreadTicks thread_now = ThreadNow();
+  TimeTicks now = OffsetNow();
+  AutoLock lock(lock_);
+  trace_event->Initialize(
+      thread_id, now, thread_now, TRACE_EVENT_PHASE_METADATA,
+      category_group_enabled, name,
+      trace_event_internal::kGlobalScope,  // scope
+      trace_event_internal::kNoId,         // id
+      trace_event_internal::kNoId,         // bind_id
+      num_args, arg_names, arg_types, arg_values, convertable_values, flags);
+  metadata_events_.push_back(std::move(trace_event));
+}
+
+// May be called when a COMPELETE event ends and the unfinished event has been
+// recycled (phase == TRACE_EVENT_PHASE_END and trace_event == NULL).
+std::string TraceLog::EventToConsoleMessage(unsigned char phase,
+                                            const TimeTicks& timestamp,
+                                            TraceEvent* trace_event) {
+  HEAP_PROFILER_SCOPED_IGNORE;
+  AutoLock thread_info_lock(thread_info_lock_);
+
+  // The caller should translate TRACE_EVENT_PHASE_COMPLETE to
+  // TRACE_EVENT_PHASE_BEGIN or TRACE_EVENT_END.
+  DCHECK(phase != TRACE_EVENT_PHASE_COMPLETE);
+
+  TimeDelta duration;
+  int thread_id =
+      trace_event ? trace_event->thread_id() : PlatformThread::CurrentId();
+  if (phase == TRACE_EVENT_PHASE_END) {
+    duration = timestamp - thread_event_start_times_[thread_id].top();
+    thread_event_start_times_[thread_id].pop();
+  }
+
+  std::string thread_name = thread_names_[thread_id];
+  if (thread_colors_.find(thread_name) == thread_colors_.end()) {
+    size_t next_color = (thread_colors_.size() % 6) + 1;
+    thread_colors_[thread_name] = next_color;
+  }
+
+  std::ostringstream log;
+  log << base::StringPrintf("%s: \x1b[0;3%dm", thread_name.c_str(),
+                            thread_colors_[thread_name]);
+
+  size_t depth = 0;
+  auto it = thread_event_start_times_.find(thread_id);
+  if (it != thread_event_start_times_.end())
+    depth = it->second.size();
+
+  for (size_t i = 0; i < depth; ++i)
+    log << "| ";
+
+  if (trace_event)
+    trace_event->AppendPrettyPrinted(&log);
+  if (phase == TRACE_EVENT_PHASE_END)
+    log << base::StringPrintf(" (%.3f ms)", duration.InMillisecondsF());
+
+  log << "\x1b[0;m";
+
+  if (phase == TRACE_EVENT_PHASE_BEGIN)
+    thread_event_start_times_[thread_id].push(timestamp);
+
+  return log.str();
+}
+
+void TraceLog::EndFilteredEvent(const unsigned char* category_group_enabled,
+                                const char* name,
+                                TraceEventHandle handle) {
+  const char* category_name = GetCategoryGroupName(category_group_enabled);
+  ForEachCategoryFilter(
+      category_group_enabled,
+      [name, category_name](TraceEventFilter* trace_event_filter) {
+        trace_event_filter->EndEvent(category_name, name);
+      });
+}
+
+void TraceLog::UpdateTraceEventDuration(
+    const unsigned char* category_group_enabled,
+    const char* name,
+    TraceEventHandle handle) {
+  char category_group_enabled_local = *category_group_enabled;
+  if (!category_group_enabled_local)
+    return;
+
+  UpdateTraceEventDurationExplicit(category_group_enabled, name, handle,
+                                   OffsetNow(), ThreadNow());
+}
+
+void TraceLog::UpdateTraceEventDurationExplicit(
+    const unsigned char* category_group_enabled,
+    const char* name,
+    TraceEventHandle handle,
+    const TimeTicks& now,
+    const ThreadTicks& thread_now) {
+  char category_group_enabled_local = *category_group_enabled;
+  if (!category_group_enabled_local)
+    return;
+
+  // Avoid re-entrance of AddTraceEvent. This may happen in GPU process when
+  // ECHO_TO_CONSOLE is enabled: AddTraceEvent -> LOG(ERROR) ->
+  // GpuProcessLogMessageHandler -> PostPendingTask -> TRACE_EVENT ...
+  if (thread_is_in_trace_event_.Get())
+    return;
+  AutoThreadLocalBoolean thread_is_in_trace_event(&thread_is_in_trace_event_);
+
+#if defined(OS_WIN)
+  // Generate an ETW event that marks the end of a complete event.
+  if (category_group_enabled_local & TraceCategory::ENABLED_FOR_ETW_EXPORT)
+    TraceEventETWExport::AddCompleteEndEvent(name);
+#endif  // OS_WIN
+
+  std::string console_message;
+  if (category_group_enabled_local & TraceCategory::ENABLED_FOR_RECORDING) {
+    AddTraceEventOverrideCallback trace_event_override =
+        reinterpret_cast<AddTraceEventOverrideCallback>(
+            subtle::NoBarrier_Load(&trace_event_override_));
+
+    // If we send events off to an override instead of the TraceBuffer,
+    // we don't have way of updating the prior event so we'll emit a
+    // separate _END event instead.
+    if (trace_event_override) {
+      TraceEvent new_trace_event;
+      new_trace_event.Initialize(
+          static_cast<int>(base::PlatformThread::CurrentId()), now, thread_now,
+          TRACE_EVENT_PHASE_END, category_group_enabled, name,
+          trace_event_internal::kGlobalScope,
+          trace_event_internal::kNoId /* id */,
+          trace_event_internal::kNoId /* bind_id */, 0, nullptr, nullptr,
+          nullptr, nullptr, TRACE_EVENT_FLAG_NONE);
+      trace_event_override(new_trace_event);
+      return;
+    }
+
+    OptionalAutoLock lock(&lock_);
+
+    TraceEvent* trace_event = GetEventByHandleInternal(handle, &lock);
+    if (trace_event) {
+      DCHECK(trace_event->phase() == TRACE_EVENT_PHASE_COMPLETE);
+      // TEMP(oysteine) to debug crbug.com/638744
+      if (trace_event->duration().ToInternalValue() != -1) {
+        DVLOG(1) << "TraceHandle: chunk_seq " << handle.chunk_seq
+                 << ", chunk_index " << handle.chunk_index << ", event_index "
+                 << handle.event_index;
+
+        std::string serialized_event;
+        trace_event->AppendAsJSON(&serialized_event, ArgumentFilterPredicate());
+        DVLOG(1) << "TraceEvent: " << serialized_event;
+        lock_.AssertAcquired();
+      }
+
+      trace_event->UpdateDuration(now, thread_now);
+#if defined(OS_ANDROID)
+      trace_event->SendToATrace();
+#endif
+    }
+
+    if (trace_options() & kInternalEchoToConsole) {
+      console_message =
+          EventToConsoleMessage(TRACE_EVENT_PHASE_END, now, trace_event);
+    }
+  }
+
+  if (!console_message.empty())
+    LOG(ERROR) << console_message;
+
+  if (category_group_enabled_local & TraceCategory::ENABLED_FOR_FILTERING)
+    EndFilteredEvent(category_group_enabled, name, handle);
+}
+
+uint64_t TraceLog::MangleEventId(uint64_t id) {
+  return id ^ process_id_hash_;
+}
+
+void TraceLog::AddMetadataEventsWhileLocked() {
+  lock_.AssertAcquired();
+
+  // Move metadata added by |AddMetadataEvent| into the trace log.
+  while (!metadata_events_.empty()) {
+    TraceEvent* event = AddEventToThreadSharedChunkWhileLocked(nullptr, false);
+    event->MoveFrom(std::move(metadata_events_.back()));
+    metadata_events_.pop_back();
+  }
+
+#if !defined(OS_NACL)  // NaCl shouldn't expose the process id.
+  InitializeMetadataEvent(
+      AddEventToThreadSharedChunkWhileLocked(nullptr, false), 0, "num_cpus",
+      "number", base::SysInfo::NumberOfProcessors());
+#endif
+
+  int current_thread_id = static_cast<int>(base::PlatformThread::CurrentId());
+  if (process_sort_index_ != 0) {
+    InitializeMetadataEvent(
+        AddEventToThreadSharedChunkWhileLocked(nullptr, false),
+        current_thread_id, "process_sort_index", "sort_index",
+        process_sort_index_);
+  }
+
+  if (!process_name_.empty()) {
+    InitializeMetadataEvent(
+        AddEventToThreadSharedChunkWhileLocked(nullptr, false),
+        current_thread_id, "process_name", "name", process_name_);
+  }
+
+  TimeDelta process_uptime = TRACE_TIME_NOW() - process_creation_time_;
+  InitializeMetadataEvent(
+      AddEventToThreadSharedChunkWhileLocked(nullptr, false), current_thread_id,
+      "process_uptime_seconds", "uptime", process_uptime.InSeconds());
+
+#if defined(OS_ANDROID)
+  InitializeMetadataEvent(
+      AddEventToThreadSharedChunkWhileLocked(nullptr, false), current_thread_id,
+      "chrome_library_address", "start_address",
+      base::StringPrintf("%p", &__executable_start));
+#endif
+
+  if (!process_labels_.empty()) {
+    std::vector<base::StringPiece> labels;
+    for (const auto& it : process_labels_)
+      labels.push_back(it.second);
+    InitializeMetadataEvent(
+        AddEventToThreadSharedChunkWhileLocked(nullptr, false),
+        current_thread_id, "process_labels", "labels",
+        base::JoinString(labels, ","));
+  }
+
+  // Thread sort indices.
+  for (const auto& it : thread_sort_indices_) {
+    if (it.second == 0)
+      continue;
+    InitializeMetadataEvent(
+        AddEventToThreadSharedChunkWhileLocked(nullptr, false), it.first,
+        "thread_sort_index", "sort_index", it.second);
+  }
+
+  // Thread names.
+  AutoLock thread_info_lock(thread_info_lock_);
+  for (const auto& it : thread_names_) {
+    if (it.second.empty())
+      continue;
+    InitializeMetadataEvent(
+        AddEventToThreadSharedChunkWhileLocked(nullptr, false), it.first,
+        "thread_name", "name", it.second);
+  }
+
+  // If buffer is full, add a metadata record to report this.
+  if (!buffer_limit_reached_timestamp_.is_null()) {
+    InitializeMetadataEvent(
+        AddEventToThreadSharedChunkWhileLocked(nullptr, false),
+        current_thread_id, "trace_buffer_overflowed", "overflowed_at_ts",
+        buffer_limit_reached_timestamp_);
+  }
+}
+
+TraceEvent* TraceLog::GetEventByHandle(TraceEventHandle handle) {
+  return GetEventByHandleInternal(handle, nullptr);
+}
+
+TraceEvent* TraceLog::GetEventByHandleInternal(TraceEventHandle handle,
+                                               OptionalAutoLock* lock) {
+  if (!handle.chunk_seq)
+    return nullptr;
+
+  DCHECK(handle.chunk_seq);
+  DCHECK(handle.chunk_index <= TraceBufferChunk::kMaxChunkIndex);
+  DCHECK(handle.event_index <= TraceBufferChunk::kTraceBufferChunkSize - 1);
+
+  if (thread_local_event_buffer_.Get()) {
+    TraceEvent* trace_event =
+        thread_local_event_buffer_.Get()->GetEventByHandle(handle);
+    if (trace_event)
+      return trace_event;
+  }
+
+  // The event has been out-of-control of the thread local buffer.
+  // Try to get the event from the main buffer with a lock.
+  if (lock)
+    lock->EnsureAcquired();
+
+  if (thread_shared_chunk_ &&
+      handle.chunk_index == thread_shared_chunk_index_) {
+    return handle.chunk_seq == thread_shared_chunk_->seq()
+               ? thread_shared_chunk_->GetEventAt(handle.event_index)
+               : nullptr;
+  }
+
+  return logged_events_->GetEventByHandle(handle);
+}
+
+void TraceLog::SetProcessID(int process_id) {
+  process_id_ = process_id;
+  // Create a FNV hash from the process ID for XORing.
+  // See http://isthe.com/chongo/tech/comp/fnv/ for algorithm details.
+  const unsigned long long kOffsetBasis = 14695981039346656037ull;
+  const unsigned long long kFnvPrime = 1099511628211ull;
+  const unsigned long long pid = static_cast<unsigned long long>(process_id_);
+  process_id_hash_ = (kOffsetBasis ^ pid) * kFnvPrime;
+}
+
+void TraceLog::SetProcessSortIndex(int sort_index) {
+  AutoLock lock(lock_);
+  process_sort_index_ = sort_index;
+}
+
+void TraceLog::UpdateProcessLabel(int label_id,
+                                  const std::string& current_label) {
+  if (!current_label.length())
+    return RemoveProcessLabel(label_id);
+
+  AutoLock lock(lock_);
+  process_labels_[label_id] = current_label;
+}
+
+void TraceLog::RemoveProcessLabel(int label_id) {
+  AutoLock lock(lock_);
+  process_labels_.erase(label_id);
+}
+
+void TraceLog::SetThreadSortIndex(PlatformThreadId thread_id, int sort_index) {
+  AutoLock lock(lock_);
+  thread_sort_indices_[static_cast<int>(thread_id)] = sort_index;
+}
+
+void TraceLog::SetTimeOffset(TimeDelta offset) {
+  time_offset_ = offset;
+}
+
+size_t TraceLog::GetObserverCountForTest() const {
+  return enabled_state_observer_list_.size();
+}
+
+void TraceLog::SetCurrentThreadBlocksMessageLoop() {
+  thread_blocks_message_loop_.Set(true);
+  // This will flush the thread local buffer.
+  delete thread_local_event_buffer_.Get();
+}
+
+TraceBuffer* TraceLog::CreateTraceBuffer() {
+  HEAP_PROFILER_SCOPED_IGNORE;
+  InternalTraceOptions options = trace_options();
+  if (options & kInternalRecordContinuously) {
+    return TraceBuffer::CreateTraceBufferRingBuffer(
+        kTraceEventRingBufferChunks);
+  }
+  if (options & kInternalEchoToConsole) {
+    return TraceBuffer::CreateTraceBufferRingBuffer(
+        kEchoToConsoleTraceEventBufferChunks);
+  }
+  if (options & kInternalRecordAsMuchAsPossible) {
+    return TraceBuffer::CreateTraceBufferVectorOfSize(
+        kTraceEventVectorBigBufferChunks);
+  }
+  return TraceBuffer::CreateTraceBufferVectorOfSize(
+      kTraceEventVectorBufferChunks);
+}
+
+#if defined(OS_WIN)
+void TraceLog::UpdateETWCategoryGroupEnabledFlags() {
+  // Go through each category and set/clear the ETW bit depending on whether the
+  // category is enabled.
+  for (TraceCategory& category : CategoryRegistry::GetAllCategories()) {
+    if (base::trace_event::TraceEventETWExport::IsCategoryGroupEnabled(
+            category.name())) {
+      category.set_state_flag(TraceCategory::ENABLED_FOR_ETW_EXPORT);
+    } else {
+      category.clear_state_flag(TraceCategory::ENABLED_FOR_ETW_EXPORT);
+    }
+  }
+}
+#endif  // defined(OS_WIN)
+
+void TraceLog::SetTraceBufferForTesting(
+    std::unique_ptr<TraceBuffer> trace_buffer) {
+  AutoLock lock(lock_);
+  logged_events_ = std::move(trace_buffer);
+}
+
+void ConvertableToTraceFormat::EstimateTraceMemoryOverhead(
+    TraceEventMemoryOverhead* overhead) {
+  overhead->Add(TraceEventMemoryOverhead::kConvertableToTraceFormat,
+                sizeof(*this));
+}
+
+void TraceLog::AddAsyncEnabledStateObserver(
+    WeakPtr<AsyncEnabledStateObserver> listener) {
+  AutoLock lock(lock_);
+  async_observers_.insert(
+      std::make_pair(listener.get(), RegisteredAsyncObserver(listener)));
+}
+
+void TraceLog::RemoveAsyncEnabledStateObserver(
+    AsyncEnabledStateObserver* listener) {
+  AutoLock lock(lock_);
+  async_observers_.erase(listener);
+}
+
+bool TraceLog::HasAsyncEnabledStateObserver(
+    AsyncEnabledStateObserver* listener) const {
+  AutoLock lock(lock_);
+  return ContainsKey(async_observers_, listener);
+}
+
+}  // namespace trace_event
+}  // namespace base
+
+namespace trace_event_internal {
+
+ScopedTraceBinaryEfficient::ScopedTraceBinaryEfficient(
+    const char* category_group,
+    const char* name) {
+  // The single atom works because for now the category_group can only be "gpu".
+  DCHECK_EQ(strcmp(category_group, "gpu"), 0);
+  static TRACE_EVENT_API_ATOMIC_WORD atomic = 0;
+  INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO_CUSTOM_VARIABLES(
+      category_group, atomic, category_group_enabled_);
+  name_ = name;
+  if (*category_group_enabled_) {
+    event_handle_ =
+        TRACE_EVENT_API_ADD_TRACE_EVENT_WITH_THREAD_ID_AND_TIMESTAMP(
+            TRACE_EVENT_PHASE_COMPLETE, category_group_enabled_, name,
+            trace_event_internal::kGlobalScope,                   // scope
+            trace_event_internal::kNoId,                          // id
+            static_cast<int>(base::PlatformThread::CurrentId()),  // thread_id
+            TRACE_TIME_TICKS_NOW(), trace_event_internal::kZeroNumArgs, nullptr,
+            nullptr, nullptr, nullptr, TRACE_EVENT_FLAG_NONE);
+  }
+}
+
+ScopedTraceBinaryEfficient::~ScopedTraceBinaryEfficient() {
+  if (*category_group_enabled_) {
+    TRACE_EVENT_API_UPDATE_TRACE_EVENT_DURATION(category_group_enabled_, name_,
+                                                event_handle_);
+  }
+}
+
+}  // namespace trace_event_internal
diff --git a/base/trace_event/trace_log.h b/base/trace_event/trace_log.h
new file mode 100644
index 0000000..8fc914d
--- /dev/null
+++ b/base/trace_event/trace_log.h
@@ -0,0 +1,528 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TRACE_EVENT_TRACE_LOG_H_
+#define BASE_TRACE_EVENT_TRACE_LOG_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include "base/atomicops.h"
+#include "base/containers/stack.h"
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/time/time_override.h"
+#include "base/trace_event/memory_dump_provider.h"
+#include "base/trace_event/trace_config.h"
+#include "base/trace_event/trace_event_impl.h"
+#include "build/build_config.h"
+
+namespace base {
+
+class MessageLoop;
+class RefCountedString;
+
+template <typename T>
+class NoDestructor;
+
+namespace trace_event {
+
+struct TraceCategory;
+class TraceBuffer;
+class TraceBufferChunk;
+class TraceEvent;
+class TraceEventFilter;
+class TraceEventMemoryOverhead;
+
+struct BASE_EXPORT TraceLogStatus {
+  TraceLogStatus();
+  ~TraceLogStatus();
+  uint32_t event_capacity;
+  uint32_t event_count;
+};
+
+class BASE_EXPORT TraceLog : public MemoryDumpProvider {
+ public:
+  // Argument passed to TraceLog::SetEnabled.
+  enum Mode : uint8_t {
+    // Enables normal tracing (recording trace events in the trace buffer).
+    RECORDING_MODE = 1 << 0,
+
+    // Trace events are enabled just for filtering but not for recording. Only
+    // event filters config of |trace_config| argument is used.
+    FILTERING_MODE = 1 << 1
+  };
+
+  static TraceLog* GetInstance();
+
+  // Get set of known category groups. This can change as new code paths are
+  // reached. The known category groups are inserted into |category_groups|.
+  void GetKnownCategoryGroups(std::vector<std::string>* category_groups);
+
+  // Retrieves a copy (for thread-safety) of the current TraceConfig.
+  TraceConfig GetCurrentTraceConfig() const;
+
+  // Initializes the thread-local event buffer, if not already initialized and
+  // if the current thread supports that (has a message loop).
+  void InitializeThreadLocalEventBufferIfSupported();
+
+  // See TraceConfig comments for details on how to control which categories
+  // will be traced. SetDisabled must be called distinctly for each mode that is
+  // enabled. If tracing has already been enabled for recording, category filter
+  // (enabled and disabled categories) will be merged into the current category
+  // filter. Enabling RECORDING_MODE does not enable filters. Trace event
+  // filters will be used only if FILTERING_MODE is set on |modes_to_enable|.
+  // Conversely to RECORDING_MODE, FILTERING_MODE doesn't support upgrading,
+  // i.e. filters can only be enabled if not previously enabled.
+  void SetEnabled(const TraceConfig& trace_config, uint8_t modes_to_enable);
+
+  // TODO(ssid): Remove the default SetEnabled and IsEnabled. They should take
+  // Mode as argument.
+
+  // Disables tracing for all categories for the specified |modes_to_disable|
+  // only. Only RECORDING_MODE is taken as default |modes_to_disable|.
+  void SetDisabled();
+  void SetDisabled(uint8_t modes_to_disable);
+
+  // Returns true if TraceLog is enabled on recording mode.
+  // Note: Returns false even if FILTERING_MODE is enabled.
+  bool IsEnabled() { return enabled_modes_ & RECORDING_MODE; }
+
+  // Returns a bitmap of enabled modes from TraceLog::Mode.
+  uint8_t enabled_modes() { return enabled_modes_; }
+
+  // The number of times we have begun recording traces. If tracing is off,
+  // returns -1. If tracing is on, then it returns the number of times we have
+  // recorded a trace. By watching for this number to increment, you can
+  // passively discover when a new trace has begun. This is then used to
+  // implement the TRACE_EVENT_IS_NEW_TRACE() primitive.
+  int GetNumTracesRecorded();
+
+#if defined(OS_ANDROID)
+  void StartATrace();
+  void StopATrace();
+  void AddClockSyncMetadataEvent();
+#endif
+
+  // Enabled state listeners give a callback when tracing is enabled or
+  // disabled. This can be used to tie into other library's tracing systems
+  // on-demand.
+  class BASE_EXPORT EnabledStateObserver {
+   public:
+    virtual ~EnabledStateObserver() = default;
+
+    // Called just after the tracing system becomes enabled, outside of the
+    // |lock_|. TraceLog::IsEnabled() is true at this point.
+    virtual void OnTraceLogEnabled() = 0;
+
+    // Called just after the tracing system disables, outside of the |lock_|.
+    // TraceLog::IsEnabled() is false at this point.
+    virtual void OnTraceLogDisabled() = 0;
+  };
+  void AddEnabledStateObserver(EnabledStateObserver* listener);
+  void RemoveEnabledStateObserver(EnabledStateObserver* listener);
+  bool HasEnabledStateObserver(EnabledStateObserver* listener) const;
+
+  // Asynchronous enabled state listeners. When tracing is enabled or disabled,
+  // for each observer, a task for invoking its appropriate callback is posted
+  // to the thread from which AddAsyncEnabledStateObserver() was called. This
+  // allows the observer to be safely destroyed, provided that it happens on the
+  // same thread that invoked AddAsyncEnabledStateObserver().
+  class BASE_EXPORT AsyncEnabledStateObserver {
+   public:
+    virtual ~AsyncEnabledStateObserver() = default;
+
+    // Posted just after the tracing system becomes enabled, outside |lock_|.
+    // TraceLog::IsEnabled() is true at this point.
+    virtual void OnTraceLogEnabled() = 0;
+
+    // Posted just after the tracing system becomes disabled, outside |lock_|.
+    // TraceLog::IsEnabled() is false at this point.
+    virtual void OnTraceLogDisabled() = 0;
+  };
+  void AddAsyncEnabledStateObserver(
+      WeakPtr<AsyncEnabledStateObserver> listener);
+  void RemoveAsyncEnabledStateObserver(AsyncEnabledStateObserver* listener);
+  bool HasAsyncEnabledStateObserver(AsyncEnabledStateObserver* listener) const;
+
+  TraceLogStatus GetStatus() const;
+  bool BufferIsFull() const;
+
+  // Computes an estimate of the size of the TraceLog including all the retained
+  // objects.
+  void EstimateTraceMemoryOverhead(TraceEventMemoryOverhead* overhead);
+
+  void SetArgumentFilterPredicate(
+      const ArgumentFilterPredicate& argument_filter_predicate);
+
+  // Flush all collected events to the given output callback. The callback will
+  // be called one or more times either synchronously or asynchronously from
+  // the current thread with IPC-bite-size chunks. The string format is
+  // undefined. Use TraceResultBuffer to convert one or more trace strings to
+  // JSON. The callback can be null if the caller doesn't want any data.
+  // Due to the implementation of thread-local buffers, flush can't be
+  // done when tracing is enabled. If called when tracing is enabled, the
+  // callback will be called directly with (empty_string, false) to indicate
+  // the end of this unsuccessful flush. Flush does the serialization
+  // on the same thread if the caller doesn't set use_worker_thread explicitly.
+  typedef base::Callback<void(const scoped_refptr<base::RefCountedString>&,
+                              bool has_more_events)> OutputCallback;
+  void Flush(const OutputCallback& cb, bool use_worker_thread = false);
+
+  // Cancels tracing and discards collected data.
+  void CancelTracing(const OutputCallback& cb);
+
+  typedef void (*AddTraceEventOverrideCallback)(const TraceEvent&);
+  // The callback will be called up until the point where the flush is
+  // finished, i.e. must be callable until OutputCallback is called with
+  // has_more_events==false.
+  void SetAddTraceEventOverride(const AddTraceEventOverrideCallback& override);
+
+  // Called by TRACE_EVENT* macros, don't call this directly.
+  // The name parameter is a category group for example:
+  // TRACE_EVENT0("renderer,webkit", "WebViewImpl::HandleInputEvent")
+  static const unsigned char* GetCategoryGroupEnabled(const char* name);
+  static const char* GetCategoryGroupName(
+      const unsigned char* category_group_enabled);
+
+  // Called by TRACE_EVENT* macros, don't call this directly.
+  // If |copy| is set, |name|, |arg_name1| and |arg_name2| will be deep copied
+  // into the event; see "Memory scoping note" and TRACE_EVENT_COPY_XXX above.
+  TraceEventHandle AddTraceEvent(
+      char phase,
+      const unsigned char* category_group_enabled,
+      const char* name,
+      const char* scope,
+      unsigned long long id,
+      int num_args,
+      const char* const* arg_names,
+      const unsigned char* arg_types,
+      const unsigned long long* arg_values,
+      std::unique_ptr<ConvertableToTraceFormat>* convertable_values,
+      unsigned int flags);
+  TraceEventHandle AddTraceEventWithBindId(
+      char phase,
+      const unsigned char* category_group_enabled,
+      const char* name,
+      const char* scope,
+      unsigned long long id,
+      unsigned long long bind_id,
+      int num_args,
+      const char* const* arg_names,
+      const unsigned char* arg_types,
+      const unsigned long long* arg_values,
+      std::unique_ptr<ConvertableToTraceFormat>* convertable_values,
+      unsigned int flags);
+  TraceEventHandle AddTraceEventWithProcessId(
+      char phase,
+      const unsigned char* category_group_enabled,
+      const char* name,
+      const char* scope,
+      unsigned long long id,
+      int process_id,
+      int num_args,
+      const char* const* arg_names,
+      const unsigned char* arg_types,
+      const unsigned long long* arg_values,
+      std::unique_ptr<ConvertableToTraceFormat>* convertable_values,
+      unsigned int flags);
+  TraceEventHandle AddTraceEventWithThreadIdAndTimestamp(
+      char phase,
+      const unsigned char* category_group_enabled,
+      const char* name,
+      const char* scope,
+      unsigned long long id,
+      int thread_id,
+      const TimeTicks& timestamp,
+      int num_args,
+      const char* const* arg_names,
+      const unsigned char* arg_types,
+      const unsigned long long* arg_values,
+      std::unique_ptr<ConvertableToTraceFormat>* convertable_values,
+      unsigned int flags);
+  TraceEventHandle AddTraceEventWithThreadIdAndTimestamp(
+      char phase,
+      const unsigned char* category_group_enabled,
+      const char* name,
+      const char* scope,
+      unsigned long long id,
+      unsigned long long bind_id,
+      int thread_id,
+      const TimeTicks& timestamp,
+      int num_args,
+      const char* const* arg_names,
+      const unsigned char* arg_types,
+      const unsigned long long* arg_values,
+      std::unique_ptr<ConvertableToTraceFormat>* convertable_values,
+      unsigned int flags);
+
+  // Adds a metadata event that will be written when the trace log is flushed.
+  void AddMetadataEvent(
+      const unsigned char* category_group_enabled,
+      const char* name,
+      int num_args,
+      const char* const* arg_names,
+      const unsigned char* arg_types,
+      const unsigned long long* arg_values,
+      std::unique_ptr<ConvertableToTraceFormat>* convertable_values,
+      unsigned int flags);
+
+  void UpdateTraceEventDuration(const unsigned char* category_group_enabled,
+                                const char* name,
+                                TraceEventHandle handle);
+
+  void UpdateTraceEventDurationExplicit(
+      const unsigned char* category_group_enabled,
+      const char* name,
+      TraceEventHandle handle,
+      const TimeTicks& now,
+      const ThreadTicks& thread_now);
+
+  void EndFilteredEvent(const unsigned char* category_group_enabled,
+                        const char* name,
+                        TraceEventHandle handle);
+
+  int process_id() const { return process_id_; }
+
+  uint64_t MangleEventId(uint64_t id);
+
+  // Exposed for unittesting:
+
+  // Testing factory for TraceEventFilter.
+  typedef std::unique_ptr<TraceEventFilter> (*FilterFactoryForTesting)(
+      const std::string& /* predicate_name */);
+  void SetFilterFactoryForTesting(FilterFactoryForTesting factory) {
+    filter_factory_for_testing_ = factory;
+  }
+
+  // Allows clearing up our singleton instance.
+  static void ResetForTesting();
+
+  // Allow tests to inspect TraceEvents.
+  TraceEvent* GetEventByHandle(TraceEventHandle handle);
+
+  void SetProcessID(int process_id);
+
+  // Process sort indices, if set, override the order of a process will appear
+  // relative to other processes in the trace viewer. Processes are sorted first
+  // on their sort index, ascending, then by their name, and then tid.
+  void SetProcessSortIndex(int sort_index);
+
+  // Sets the name of the process.
+  void set_process_name(const std::string& process_name) {
+    AutoLock lock(lock_);
+    process_name_ = process_name;
+  }
+
+  bool IsProcessNameEmpty() const { return process_name_.empty(); }
+
+  // Processes can have labels in addition to their names. Use labels, for
+  // instance, to list out the web page titles that a process is handling.
+  void UpdateProcessLabel(int label_id, const std::string& current_label);
+  void RemoveProcessLabel(int label_id);
+
+  // Thread sort indices, if set, override the order of a thread will appear
+  // within its process in the trace viewer. Threads are sorted first on their
+  // sort index, ascending, then by their name, and then tid.
+  void SetThreadSortIndex(PlatformThreadId thread_id, int sort_index);
+
+  // Allow setting an offset between the current TimeTicks time and the time
+  // that should be reported.
+  void SetTimeOffset(TimeDelta offset);
+
+  size_t GetObserverCountForTest() const;
+
+  // Call this method if the current thread may block the message loop to
+  // prevent the thread from using the thread-local buffer because the thread
+  // may not handle the flush request in time causing lost of unflushed events.
+  void SetCurrentThreadBlocksMessageLoop();
+
+#if defined(OS_WIN)
+  // This function is called by the ETW exporting module whenever the ETW
+  // keyword (flags) changes. This keyword indicates which categories should be
+  // exported, so whenever it changes, we adjust accordingly.
+  void UpdateETWCategoryGroupEnabledFlags();
+#endif
+
+  // Replaces |logged_events_| with a new TraceBuffer for testing.
+  void SetTraceBufferForTesting(std::unique_ptr<TraceBuffer> trace_buffer);
+
+ private:
+  typedef unsigned int InternalTraceOptions;
+
+  FRIEND_TEST_ALL_PREFIXES(TraceEventTestFixture,
+                           TraceBufferRingBufferGetReturnChunk);
+  FRIEND_TEST_ALL_PREFIXES(TraceEventTestFixture,
+                           TraceBufferRingBufferHalfIteration);
+  FRIEND_TEST_ALL_PREFIXES(TraceEventTestFixture,
+                           TraceBufferRingBufferFullIteration);
+  FRIEND_TEST_ALL_PREFIXES(TraceEventTestFixture, TraceBufferVectorReportFull);
+  FRIEND_TEST_ALL_PREFIXES(TraceEventTestFixture,
+                           ConvertTraceConfigToInternalOptions);
+  FRIEND_TEST_ALL_PREFIXES(TraceEventTestFixture,
+                           TraceRecordAsMuchAsPossibleMode);
+
+  friend class base::NoDestructor<TraceLog>;
+
+  // MemoryDumpProvider implementation.
+  bool OnMemoryDump(const MemoryDumpArgs& args,
+                    ProcessMemoryDump* pmd) override;
+
+  // Enable/disable each category group based on the current mode_,
+  // category_filter_ and event_filters_enabled_.
+  // Enable the category group in the recording mode if category_filter_ matches
+  // the category group, is not null. Enable category for filtering if any
+  // filter in event_filters_enabled_ enables it.
+  void UpdateCategoryRegistry();
+  void UpdateCategoryState(TraceCategory* category);
+
+  void CreateFiltersForTraceConfig();
+
+  InternalTraceOptions GetInternalOptionsFromTraceConfig(
+      const TraceConfig& config);
+
+  class ThreadLocalEventBuffer;
+  class OptionalAutoLock;
+  struct RegisteredAsyncObserver;
+
+  TraceLog();
+  ~TraceLog() override;
+  void AddMetadataEventsWhileLocked();
+
+  InternalTraceOptions trace_options() const {
+    return static_cast<InternalTraceOptions>(
+        subtle::NoBarrier_Load(&trace_options_));
+  }
+
+  TraceBuffer* trace_buffer() const { return logged_events_.get(); }
+  TraceBuffer* CreateTraceBuffer();
+
+  std::string EventToConsoleMessage(unsigned char phase,
+                                    const TimeTicks& timestamp,
+                                    TraceEvent* trace_event);
+
+  TraceEvent* AddEventToThreadSharedChunkWhileLocked(TraceEventHandle* handle,
+                                                     bool check_buffer_is_full);
+  void CheckIfBufferIsFullWhileLocked();
+  void SetDisabledWhileLocked(uint8_t modes);
+
+  TraceEvent* GetEventByHandleInternal(TraceEventHandle handle,
+                                       OptionalAutoLock* lock);
+
+  void FlushInternal(const OutputCallback& cb,
+                     bool use_worker_thread,
+                     bool discard_events);
+
+  // |generation| is used in the following callbacks to check if the callback
+  // is called for the flush of the current |logged_events_|.
+  void FlushCurrentThread(int generation, bool discard_events);
+  // Usually it runs on a different thread.
+  static void ConvertTraceEventsToTraceFormat(
+      std::unique_ptr<TraceBuffer> logged_events,
+      const TraceLog::OutputCallback& flush_output_callback,
+      const ArgumentFilterPredicate& argument_filter_predicate);
+  void FinishFlush(int generation, bool discard_events);
+  void OnFlushTimeout(int generation, bool discard_events);
+
+  int generation() const {
+    return static_cast<int>(subtle::NoBarrier_Load(&generation_));
+  }
+  bool CheckGeneration(int generation) const {
+    return generation == this->generation();
+  }
+  void UseNextTraceBuffer();
+
+  TimeTicks OffsetNow() const {
+    // This should be TRACE_TIME_TICKS_NOW but include order makes that hard.
+    return OffsetTimestamp(base::subtle::TimeTicksNowIgnoringOverride());
+  }
+  TimeTicks OffsetTimestamp(const TimeTicks& timestamp) const {
+    return timestamp - time_offset_;
+  }
+
+  // Internal representation of trace options since we store the currently used
+  // trace option as an AtomicWord.
+  static const InternalTraceOptions kInternalNone;
+  static const InternalTraceOptions kInternalRecordUntilFull;
+  static const InternalTraceOptions kInternalRecordContinuously;
+  static const InternalTraceOptions kInternalEchoToConsole;
+  static const InternalTraceOptions kInternalRecordAsMuchAsPossible;
+  static const InternalTraceOptions kInternalEnableArgumentFilter;
+
+  // This lock protects TraceLog member accesses (except for members protected
+  // by thread_info_lock_) from arbitrary threads.
+  mutable Lock lock_;
+  // This lock protects accesses to thread_names_, thread_event_start_times_
+  // and thread_colors_.
+  Lock thread_info_lock_;
+  uint8_t enabled_modes_;  // See TraceLog::Mode.
+  int num_traces_recorded_;
+  std::unique_ptr<TraceBuffer> logged_events_;
+  std::vector<std::unique_ptr<TraceEvent>> metadata_events_;
+  bool dispatching_to_observer_list_;
+  std::vector<EnabledStateObserver*> enabled_state_observer_list_;
+  std::map<AsyncEnabledStateObserver*, RegisteredAsyncObserver>
+      async_observers_;
+
+  std::string process_name_;
+  std::unordered_map<int, std::string> process_labels_;
+  int process_sort_index_;
+  std::unordered_map<int, int> thread_sort_indices_;
+  std::unordered_map<int, std::string> thread_names_;
+  base::Time process_creation_time_;
+
+  // The following two maps are used only when ECHO_TO_CONSOLE.
+  std::unordered_map<int, base::stack<TimeTicks>> thread_event_start_times_;
+  std::unordered_map<std::string, int> thread_colors_;
+
+  TimeTicks buffer_limit_reached_timestamp_;
+
+  // XORed with TraceID to make it unlikely to collide with other processes.
+  unsigned long long process_id_hash_;
+
+  int process_id_;
+
+  TimeDelta time_offset_;
+
+  subtle::AtomicWord /* Options */ trace_options_;
+
+  TraceConfig trace_config_;
+  TraceConfig::EventFilters enabled_event_filters_;
+
+  ThreadLocalPointer<ThreadLocalEventBuffer> thread_local_event_buffer_;
+  ThreadLocalBoolean thread_blocks_message_loop_;
+  ThreadLocalBoolean thread_is_in_trace_event_;
+
+  // Contains the message loops of threads that have had at least one event
+  // added into the local event buffer. Not using SingleThreadTaskRunner
+  // because we need to know the life time of the message loops.
+  hash_set<MessageLoop*> thread_message_loops_;
+
+  // For events which can't be added into the thread local buffer, e.g. events
+  // from threads without a message loop.
+  std::unique_ptr<TraceBufferChunk> thread_shared_chunk_;
+  size_t thread_shared_chunk_index_;
+
+  // Set when asynchronous Flush is in progress.
+  OutputCallback flush_output_callback_;
+  scoped_refptr<SequencedTaskRunner> flush_task_runner_;
+  ArgumentFilterPredicate argument_filter_predicate_;
+  subtle::AtomicWord generation_;
+  bool use_worker_thread_;
+  subtle::AtomicWord trace_event_override_;
+
+  FilterFactoryForTesting filter_factory_for_testing_;
+
+  DISALLOW_COPY_AND_ASSIGN(TraceLog);
+};
+
+}  // namespace trace_event
+}  // namespace base
+
+#endif  // BASE_TRACE_EVENT_TRACE_LOG_H_
diff --git a/base/trace_event/trace_log_constants.cc b/base/trace_event/trace_log_constants.cc
new file mode 100644
index 0000000..65dca2e
--- /dev/null
+++ b/base/trace_event/trace_log_constants.cc
@@ -0,0 +1,26 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/trace_event/trace_log.h"
+
+namespace base {
+namespace trace_event {
+
+// Constant used by TraceLog's internal implementation of trace_option.
+const TraceLog::InternalTraceOptions
+    TraceLog::kInternalNone = 0;
+const TraceLog::InternalTraceOptions
+    TraceLog::kInternalRecordUntilFull = 1 << 0;
+const TraceLog::InternalTraceOptions
+    TraceLog::kInternalRecordContinuously = 1 << 1;
+// 1 << 2 is reserved for the DEPRECATED kInternalEnableSampling. DO NOT USE.
+const TraceLog::InternalTraceOptions
+    TraceLog::kInternalEchoToConsole = 1 << 3;
+const TraceLog::InternalTraceOptions
+    TraceLog::kInternalRecordAsMuchAsPossible = 1 << 4;
+const TraceLog::InternalTraceOptions
+    TraceLog::kInternalEnableArgumentFilter = 1 << 5;
+
+}  // namespace trace_event
+}  // namespace base
diff --git a/base/trace_event/tracing_agent.cc b/base/trace_event/tracing_agent.cc
new file mode 100644
index 0000000..e48feff
--- /dev/null
+++ b/base/trace_event/tracing_agent.cc
@@ -0,0 +1,24 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/trace_event/tracing_agent.h"
+
+namespace base {
+namespace trace_event {
+
+TracingAgent::~TracingAgent() = default;
+
+bool TracingAgent::SupportsExplicitClockSync() {
+  return false;
+}
+
+void TracingAgent::RecordClockSyncMarker(
+    const std::string& sync_id,
+    RecordClockSyncMarkerCallback callback) {
+  DCHECK(SupportsExplicitClockSync());
+}
+
+
+}  // namespace trace_event
+}  // namespace base
diff --git a/base/trace_event/tracing_agent.h b/base/trace_event/tracing_agent.h
new file mode 100644
index 0000000..f818457
--- /dev/null
+++ b/base/trace_event/tracing_agent.h
@@ -0,0 +1,96 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TRACE_EVENT_TRACING_AGENT_H_
+#define BASE_TRACE_EVENT_TRACING_AGENT_H_
+
+#include "base/base_export.h"
+#include "base/callback.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/values.h"
+
+namespace base {
+
+class TimeTicks;
+
+namespace trace_event {
+
+class TraceConfig;
+
+// A tracing agent is an entity that records its own sort of trace. Each
+// tracing method that produces its own trace log should implement this
+// interface. All tracing agents must only be controlled by TracingController.
+// Some existing examples include TracingControllerImpl for Chrome trace events,
+// DebugDaemonClient for CrOs system trace, EtwTracingAgent for Windows system
+// trace and PowerTracingAgent for BattOr power trace.
+class BASE_EXPORT TracingAgent {
+ public:
+  using StartAgentTracingCallback =
+      base::OnceCallback<void(const std::string& agent_name, bool success)>;
+  // Passing a null or empty events_str_ptr indicates that no trace data is
+  // available for the specified agent.
+  using StopAgentTracingCallback = base::OnceCallback<void(
+      const std::string& agent_name,
+      const std::string& events_label,
+      const scoped_refptr<base::RefCountedString>& events_str_ptr)>;
+  using RecordClockSyncMarkerCallback =
+      base::OnceCallback<void(const std::string& sync_id,
+                              const TimeTicks& issue_ts,
+                              const TimeTicks& issue_end_ts)>;
+
+  virtual ~TracingAgent();
+
+  // Gets the name of the tracing agent. Each tracing agent's name should be
+  // unique.
+  virtual std::string GetTracingAgentName() = 0;
+
+  // Gets the trace event label of this tracing agent. The label will be used to
+  // label this agent's trace when all traces from different tracing agents are
+  // combined. Multiple tracing agents could have the same label. The tracing
+  // agents using the same label should not be able to run at the same time. For
+  // example, ETW on Windows and CrOS system tracing both use
+  // "systemTraceEvents" as the label. Those two agents never run at the same
+  // time because they are for different platforms.
+  virtual std::string GetTraceEventLabel() = 0;
+
+  // Starts tracing on the tracing agent with the trace configuration.
+  virtual void StartAgentTracing(const TraceConfig& trace_config,
+                                 StartAgentTracingCallback callback) = 0;
+
+  // Stops tracing on the tracing agent. The trace data will be passed back to
+  // the TracingController via the callback.
+  virtual void StopAgentTracing(StopAgentTracingCallback callback) = 0;
+
+  // Checks if the tracing agent supports explicit clock synchronization.
+  virtual bool SupportsExplicitClockSync();
+
+  // Records a clock sync marker issued by another tracing agent. This is only
+  // used if the tracing agent supports explicit clock synchronization.
+  //
+  // Two things need to be done:
+  // 1. The issuer asks the receiver to record the clock sync marker.
+  // 2. The issuer records how long the receiver takes to do the recording.
+  //
+  // In Chrome, the receiver thread also runs in Chrome and it will talk to the
+  // real receiver entity, e.g., power monitor or Android device system, via
+  // different communication methods, e.g., through USB or file reading/writing.
+  // The 2nd task measures that communication latency.
+  //
+  // Having a reliable timing measurement for the 2nd task requires synchronous
+  // function call without any cross-thread or cross-process activity. However,
+  // tracing agents in Chrome run in their own threads. Therefore, the issuer
+  // needs to dedicate the 2nd task to the receiver to take time measurements
+  // in the receiver thread, and the receiver thread needs to pass them back to
+  // the issuer in the callback.
+  //
+  // The assumption is that the receiver thread knows the issuer's clock, which
+  // is true in Chrome because all agent threads' clocks are Chrome clock.
+  virtual void RecordClockSyncMarker(const std::string& sync_id,
+                                     RecordClockSyncMarkerCallback callback);
+};
+
+}  // namespace trace_event
+}  // namespace base
+
+#endif  // BASE_TRACE_EVENT_TRACING_AGENT_H_
diff --git a/base/unguessable_token_unittest.cc b/base/unguessable_token_unittest.cc
new file mode 100644
index 0000000..b70cc72
--- /dev/null
+++ b/base/unguessable_token_unittest.cc
@@ -0,0 +1,155 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/unguessable_token.h"
+
+#include <memory>
+#include <sstream>
+#include <type_traits>
+
+#include "base/value_conversions.h"
+#include "base/values.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+void TestSmallerThanOperator(const UnguessableToken& a,
+                             const UnguessableToken& b) {
+  EXPECT_TRUE(a < b);
+  EXPECT_FALSE(b < a);
+}
+
+TEST(UnguessableTokenTest, VerifyEqualityOperators) {
+  // Deserialize is used for testing purposes.
+  // Use UnguessableToken::Create() in production code instead.
+  UnguessableToken token = UnguessableToken::Deserialize(1, 2);
+  UnguessableToken same_token = UnguessableToken::Deserialize(1, 2);
+  UnguessableToken diff_token = UnguessableToken::Deserialize(1, 3);
+
+  EXPECT_TRUE(token == token);
+  EXPECT_FALSE(token != token);
+
+  EXPECT_TRUE(token == same_token);
+  EXPECT_FALSE(token != same_token);
+
+  EXPECT_FALSE(token == diff_token);
+  EXPECT_FALSE(diff_token == token);
+  EXPECT_TRUE(token != diff_token);
+  EXPECT_TRUE(diff_token != token);
+}
+
+TEST(UnguessableTokenTest, VerifyConstructors) {
+  UnguessableToken token = UnguessableToken::Create();
+  EXPECT_FALSE(token.is_empty());
+  EXPECT_TRUE(token);
+
+  UnguessableToken copied_token(token);
+  EXPECT_TRUE(copied_token);
+  EXPECT_EQ(token, copied_token);
+
+  UnguessableToken uninitialized;
+  EXPECT_TRUE(uninitialized.is_empty());
+  EXPECT_FALSE(uninitialized);
+
+  EXPECT_TRUE(UnguessableToken().is_empty());
+  EXPECT_FALSE(UnguessableToken());
+}
+
+TEST(UnguessableTokenTest, VerifySerialization) {
+  UnguessableToken token = UnguessableToken::Create();
+
+  uint64_t high = token.GetHighForSerialization();
+  uint64_t low = token.GetLowForSerialization();
+
+  EXPECT_TRUE(high);
+  EXPECT_TRUE(low);
+
+  UnguessableToken Deserialized = UnguessableToken::Deserialize(high, low);
+  EXPECT_EQ(token, Deserialized);
+}
+
+TEST(UnguessableTokenTest, VerifyValueSerialization) {
+  UnguessableToken token = UnguessableToken::Create();
+  std::unique_ptr<Value> value = CreateUnguessableTokenValue(token);
+
+  UnguessableToken deserialized;
+  EXPECT_TRUE(GetValueAsUnguessableToken(*value, &deserialized));
+  EXPECT_EQ(token, deserialized);
+}
+
+// Common case (~88% of the time) - no leading zeroes in high_ nor low_.
+TEST(UnguessableTokenTest, VerifyToString1) {
+  UnguessableToken token =
+      UnguessableToken::Deserialize(0x1234567890ABCDEF, 0xFEDCBA0987654321);
+  std::string expected = "1234567890ABCDEFFEDCBA0987654321";
+
+  EXPECT_EQ(expected, token.ToString());
+
+  std::string expected_stream = "(1234567890ABCDEFFEDCBA0987654321)";
+  std::stringstream stream;
+  stream << token;
+  EXPECT_EQ(expected_stream, stream.str());
+}
+
+// Less common case - leading zeroes in high_ or low_ (testing with both).
+TEST(UnguessableTokenTest, VerifyToString2) {
+  UnguessableToken token = UnguessableToken::Deserialize(0x123, 0xABC);
+  std::string expected = "00000000000001230000000000000ABC";
+
+  EXPECT_EQ(expected, token.ToString());
+
+  std::string expected_stream = "(00000000000001230000000000000ABC)";
+  std::stringstream stream;
+  stream << token;
+  EXPECT_EQ(expected_stream, stream.str());
+}
+
+TEST(UnguessableTokenTest, VerifyToStringUniqueness) {
+  const UnguessableToken token1 =
+      UnguessableToken::Deserialize(0x0000000012345678, 0x0000000123456789);
+  const UnguessableToken token2 =
+      UnguessableToken::Deserialize(0x0000000123456781, 0x0000000023456789);
+  EXPECT_NE(token1.ToString(), token2.ToString());
+}
+
+TEST(UnguessableTokenTest, VerifySmallerThanOperator) {
+  // Deserialize is used for testing purposes.
+  // Use UnguessableToken::Create() in production code instead.
+  {
+    SCOPED_TRACE("a.low < b.low and a.high == b.high.");
+    TestSmallerThanOperator(UnguessableToken::Deserialize(0, 1),
+                            UnguessableToken::Deserialize(0, 5));
+  }
+  {
+    SCOPED_TRACE("a.low == b.low and a.high < b.high.");
+    TestSmallerThanOperator(UnguessableToken::Deserialize(1, 0),
+                            UnguessableToken::Deserialize(5, 0));
+  }
+  {
+    SCOPED_TRACE("a.low < b.low and a.high < b.high.");
+    TestSmallerThanOperator(UnguessableToken::Deserialize(1, 1),
+                            UnguessableToken::Deserialize(5, 5));
+  }
+  {
+    SCOPED_TRACE("a.low > b.low and a.high < b.high.");
+    TestSmallerThanOperator(UnguessableToken::Deserialize(1, 10),
+                            UnguessableToken::Deserialize(10, 1));
+  }
+}
+
+TEST(UnguessableTokenTest, VerifyHash) {
+  UnguessableToken token = UnguessableToken::Create();
+
+  EXPECT_EQ(base::HashInts64(token.GetHighForSerialization(),
+                             token.GetLowForSerialization()),
+            UnguessableTokenHash()(token));
+}
+
+TEST(UnguessableTokenTest, VerifyBasicUniqueness) {
+  EXPECT_NE(UnguessableToken::Create(), UnguessableToken::Create());
+
+  UnguessableToken token = UnguessableToken::Create();
+  EXPECT_NE(token.GetHighForSerialization(), token.GetLowForSerialization());
+}
+}
diff --git a/base/value_conversions.cc b/base/value_conversions.cc
new file mode 100644
index 0000000..7e3fd94
--- /dev/null
+++ b/base/value_conversions.cc
@@ -0,0 +1,99 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/value_conversions.h"
+
+#include <stdint.h>
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/time/time.h"
+#include "base/unguessable_token.h"
+#include "base/values.h"
+
+namespace base {
+namespace {
+// Helper for serialize/deserialize UnguessableToken.
+union UnguessableTokenRepresentation {
+  struct Field {
+    uint64_t high;
+    uint64_t low;
+  } field;
+
+  uint8_t buffer[sizeof(Field)];
+};
+}  // namespace
+
+// |Value| internally stores strings in UTF-8, so we have to convert from the
+// system native code to UTF-8 and back.
+std::unique_ptr<Value> CreateFilePathValue(const FilePath& in_value) {
+  return std::make_unique<Value>(in_value.AsUTF8Unsafe());
+}
+
+bool GetValueAsFilePath(const Value& value, FilePath* file_path) {
+  std::string str;
+  if (!value.GetAsString(&str))
+    return false;
+  if (file_path)
+    *file_path = FilePath::FromUTF8Unsafe(str);
+  return true;
+}
+
+// |Value| does not support 64-bit integers, and doubles do not have enough
+// precision, so we store the 64-bit time value as a string instead.
+std::unique_ptr<Value> CreateTimeDeltaValue(const TimeDelta& time) {
+  std::string string_value = base::Int64ToString(time.ToInternalValue());
+  return std::make_unique<Value>(string_value);
+}
+
+bool GetValueAsTimeDelta(const Value& value, TimeDelta* time) {
+  std::string str;
+  int64_t int_value;
+  if (!value.GetAsString(&str) || !base::StringToInt64(str, &int_value))
+    return false;
+  if (time)
+    *time = TimeDelta::FromInternalValue(int_value);
+  return true;
+}
+
+std::unique_ptr<Value> CreateUnguessableTokenValue(
+    const UnguessableToken& token) {
+  UnguessableTokenRepresentation representation;
+  representation.field.high = token.GetHighForSerialization();
+  representation.field.low = token.GetLowForSerialization();
+
+  return std::make_unique<Value>(
+      HexEncode(representation.buffer, sizeof(representation.buffer)));
+}
+
+bool GetValueAsUnguessableToken(const Value& value, UnguessableToken* token) {
+  if (!value.is_string()) {
+    return false;
+  }
+
+  // TODO(dcheng|yucliu): Make a function that accepts non vector variant and
+  // reads a fixed number of bytes.
+  std::vector<uint8_t> high_low_bytes;
+  if (!HexStringToBytes(value.GetString(), &high_low_bytes)) {
+    return false;
+  }
+
+  UnguessableTokenRepresentation representation;
+  if (high_low_bytes.size() != sizeof(representation.buffer)) {
+    return false;
+  }
+
+  std::copy(high_low_bytes.begin(), high_low_bytes.end(),
+            std::begin(representation.buffer));
+  *token = UnguessableToken::Deserialize(representation.field.high,
+                                         representation.field.low);
+  return true;
+}
+
+}  // namespace base
diff --git a/base/value_conversions.h b/base/value_conversions.h
new file mode 100644
index 0000000..bd095cd
--- /dev/null
+++ b/base/value_conversions.h
@@ -0,0 +1,35 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_VALUE_CONVERSIONS_H_
+#define BASE_VALUE_CONVERSIONS_H_
+
+// This file contains methods to convert things to a |Value| and back.
+
+#include <memory>
+#include "base/base_export.h"
+
+namespace base {
+
+class FilePath;
+class TimeDelta;
+class UnguessableToken;
+class Value;
+
+// The caller takes ownership of the returned value.
+BASE_EXPORT std::unique_ptr<Value> CreateFilePathValue(
+    const FilePath& in_value);
+BASE_EXPORT bool GetValueAsFilePath(const Value& value, FilePath* file_path);
+
+BASE_EXPORT std::unique_ptr<Value> CreateTimeDeltaValue(const TimeDelta& time);
+BASE_EXPORT bool GetValueAsTimeDelta(const Value& value, TimeDelta* time);
+
+BASE_EXPORT std::unique_ptr<Value> CreateUnguessableTokenValue(
+    const UnguessableToken& token);
+BASE_EXPORT bool GetValueAsUnguessableToken(const Value& value,
+                                            UnguessableToken* token);
+
+}  // namespace base
+
+#endif  // BASE_VALUE_CONVERSIONS_H_
diff --git a/components/json_schema/README b/components/json_schema/README
new file mode 100644
index 0000000..c7453db
--- /dev/null
+++ b/components/json_schema/README
@@ -0,0 +1,6 @@
+The //components/json_schema component provides:
+
+a) JSON schema constants, which can be used to inspect schema objects.
+
+b) The JSONSchemaValidator class, which can be used to parse and validate JSON
+schemas, and to validate JSON objects against the parsed schema.
diff --git a/components/json_schema/json_schema_validator_unittest.cc b/components/json_schema/json_schema_validator_unittest.cc
new file mode 100644
index 0000000..b155953
--- /dev/null
+++ b/components/json_schema/json_schema_validator_unittest.cc
@@ -0,0 +1,177 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+
+#include "base/values.h"
+#include "components/json_schema/json_schema_validator.h"
+#include "components/json_schema/json_schema_validator_unittest_base.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class JSONSchemaValidatorCPPTest : public JSONSchemaValidatorTestBase {
+ public:
+  JSONSchemaValidatorCPPTest() {}
+
+ protected:
+  void ExpectValid(const std::string& test_source,
+                   base::Value* instance,
+                   base::DictionaryValue* schema,
+                   base::ListValue* types) override {
+    JSONSchemaValidator validator(schema, types);
+    if (validator.Validate(instance))
+      return;
+
+    for (size_t i = 0; i < validator.errors().size(); ++i) {
+      ADD_FAILURE() << test_source << ": "
+                    << validator.errors()[i].path << ": "
+                    << validator.errors()[i].message;
+    }
+  }
+
+  void ExpectNotValid(const std::string& test_source,
+                      base::Value* instance,
+                      base::DictionaryValue* schema,
+                      base::ListValue* types,
+                      const std::string& expected_error_path,
+                      const std::string& expected_error_message) override {
+    JSONSchemaValidator validator(schema, types);
+    if (validator.Validate(instance)) {
+      ADD_FAILURE() << test_source;
+      return;
+    }
+
+    ASSERT_EQ(1u, validator.errors().size()) << test_source;
+    EXPECT_EQ(expected_error_path, validator.errors()[0].path) << test_source;
+    EXPECT_EQ(expected_error_message, validator.errors()[0].message)
+        << test_source;
+  }
+};
+
+TEST_F(JSONSchemaValidatorCPPTest, Test) {
+  RunTests();
+}
+
+TEST(JSONSchemaValidator, IsValidSchema) {
+  std::string error;
+  EXPECT_FALSE(JSONSchemaValidator::IsValidSchema("", &error));
+  EXPECT_FALSE(JSONSchemaValidator::IsValidSchema("\0", &error));
+  EXPECT_FALSE(JSONSchemaValidator::IsValidSchema("string", &error));
+  EXPECT_FALSE(JSONSchemaValidator::IsValidSchema("\"string\"", &error));
+  EXPECT_FALSE(JSONSchemaValidator::IsValidSchema("[]", &error));
+  EXPECT_FALSE(JSONSchemaValidator::IsValidSchema("{}", &error));
+  EXPECT_FALSE(JSONSchemaValidator::IsValidSchema(
+      "{ \"type\": 123 }", &error));
+  EXPECT_FALSE(JSONSchemaValidator::IsValidSchema(
+      "{ \"type\": \"invalid\" }", &error));
+  EXPECT_FALSE(JSONSchemaValidator::IsValidSchema(
+      "{"
+      "  \"type\": \"object\","
+      "  \"properties\": []"  // Invalid properties type.
+      "}", &error));
+  EXPECT_FALSE(JSONSchemaValidator::IsValidSchema(
+      "{"
+      "  \"type\": \"string\","
+      "  \"maxLength\": -1"  // Must be >= 0.
+      "}", &error));
+  EXPECT_FALSE(JSONSchemaValidator::IsValidSchema(
+      "{"
+      "  \"type\": \"string\","
+      "  \"enum\": [ {} ]"  // "enum" dict values must contain "name".
+      "}", &error));
+  EXPECT_FALSE(JSONSchemaValidator::IsValidSchema(
+      "{"
+      "  \"type\": \"string\","
+      "  \"enum\": [ { \"name\": {} } ]"  // "enum" name must be a simple value.
+      "}", &error));
+  EXPECT_FALSE(JSONSchemaValidator::IsValidSchema(
+      "{"
+      "  \"type\": \"array\","
+      "  \"items\": [ 123 ],"  // "items" must contain a schema or schemas.
+      "}", &error));
+  EXPECT_TRUE(JSONSchemaValidator::IsValidSchema(
+      "{ \"type\": \"object\" }", &error)) << error;
+  EXPECT_TRUE(JSONSchemaValidator::IsValidSchema(
+      "{ \"type\": [\"object\", \"array\"] }", &error)) << error;
+  EXPECT_TRUE(JSONSchemaValidator::IsValidSchema(
+      "{"
+      "  \"type\": [\"object\", \"array\"],"
+      "  \"properties\": {"
+      "    \"string-property\": {"
+      "      \"type\": \"string\","
+      "      \"minLength\": 1,"
+      "      \"maxLength\": 100,"
+      "      \"title\": \"The String Policy\","
+      "      \"description\": \"This policy controls the String widget.\""
+      "    },"
+      "    \"integer-property\": {"
+      "      \"type\": \"number\","
+      "      \"minimum\": 1000.0,"
+      "      \"maximum\": 9999.0"
+      "    },"
+      "    \"enum-property\": {"
+      "      \"type\": \"integer\","
+      "      \"enum\": [0, 1, {\"name\": 10}, 100]"
+      "    },"
+      "    \"items-property\": {"
+      "      \"type\": \"array\","
+      "      \"items\": {"
+      "        \"type\": \"string\""
+      "      }"
+      "    },"
+      "    \"items-list-property\": {"
+      "      \"type\": \"array\","
+      "      \"items\": ["
+      "        { \"type\": \"string\" },"
+      "        { \"type\": \"integer\" }"
+      "      ]"
+      "    }"
+      "  },"
+      "  \"additionalProperties\": {"
+      "    \"type\": \"any\""
+      "  }"
+      "}", &error)) << error;
+  EXPECT_TRUE(JSONSchemaValidator::IsValidSchema(
+      "{"
+      "  \"type\": \"object\","
+      "  \"patternProperties\": {"
+      "    \".\": { \"type\": \"any\" },"
+      "    \"foo\": { \"type\": \"any\" },"
+      "    \"^foo$\": { \"type\": \"any\" },"
+      "    \"foo+\": { \"type\": \"any\" },"
+      "    \"foo?\": { \"type\": \"any\" },"
+      "    \"fo{2,4}\": { \"type\": \"any\" },"
+      "    \"(left)|(right)\": { \"type\": \"any\" }"
+      "  }"
+      "}", &error)) << error;
+  EXPECT_TRUE(JSONSchemaValidator::IsValidSchema(
+      "{"
+      "  \"type\": \"object\","
+      "  \"unknown attribute\": \"that should just be ignored\""
+      "}",
+      JSONSchemaValidator::OPTIONS_IGNORE_UNKNOWN_ATTRIBUTES,
+      &error)) << error;
+  EXPECT_FALSE(JSONSchemaValidator::IsValidSchema(
+      "{"
+      "  \"type\": \"object\","
+      "  \"unknown attribute\": \"that will cause a failure\""
+      "}", 0, &error)) << error;
+
+  EXPECT_FALSE(JSONSchemaValidator::IsValidSchema(R"(
+      {
+        "type": "object",
+        "properties": {"foo": {"type": "number"}},
+        "required": 123
+      })",
+                                                  0, &error))
+      << error;
+
+  EXPECT_FALSE(JSONSchemaValidator::IsValidSchema(R"(
+      {
+        "type": "object",
+        "properties": {"foo": {"type": "number"}},
+        "required": [ 123 ]
+      })",
+                                                  0, &error))
+      << error;
+}
diff --git a/components/json_schema/json_schema_validator_unittest_base.cc b/components/json_schema/json_schema_validator_unittest_base.cc
new file mode 100644
index 0000000..ce220dc
--- /dev/null
+++ b/components/json_schema/json_schema_validator_unittest_base.cc
@@ -0,0 +1,710 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/json_schema/json_schema_validator_unittest_base.h"
+
+#include <cfloat>
+#include <cmath>
+#include <limits>
+#include <memory>
+
+#include "base/base_paths.h"
+#include "base/files/file_util.h"
+#include "base/json/json_file_value_serializer.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "base/strings/stringprintf.h"
+#include "base/values.h"
+#include "components/json_schema/json_schema_constants.h"
+#include "components/json_schema/json_schema_validator.h"
+
+namespace schema = json_schema_constants;
+
+namespace {
+
+#define TEST_SOURCE base::StringPrintf("%s:%i", __FILE__, __LINE__)
+
+base::Value* LoadValue(const std::string& filename) {
+  base::FilePath path;
+  base::PathService::Get(base::DIR_SOURCE_ROOT, &path);
+  path = path.AppendASCII("components")
+             .AppendASCII("test")
+             .AppendASCII("data")
+             .AppendASCII("json_schema")
+             .AppendASCII(filename);
+  EXPECT_TRUE(base::PathExists(path));
+
+  std::string error_message;
+  JSONFileValueDeserializer deserializer(path);
+  base::Value* result =
+      deserializer.Deserialize(nullptr, &error_message).release();
+  if (!result)
+    ADD_FAILURE() << "Could not parse JSON: " << error_message;
+  return result;
+}
+
+base::Value* LoadValue(const std::string& filename, base::Value::Type type) {
+  std::unique_ptr<base::Value> result(LoadValue(filename));
+  if (!result)
+    return nullptr;
+  if (result->type() != type) {
+    ADD_FAILURE() << "Expected type " << type << ", got: " << result->type();
+    return nullptr;
+  }
+  return result.release();
+}
+
+base::ListValue* LoadList(const std::string& filename) {
+  return static_cast<base::ListValue*>(
+      LoadValue(filename, base::Value::Type::LIST));
+}
+
+base::DictionaryValue* LoadDictionary(const std::string& filename) {
+  return static_cast<base::DictionaryValue*>(
+      LoadValue(filename, base::Value::Type::DICTIONARY));
+}
+
+}  // namespace
+
+
+JSONSchemaValidatorTestBase::JSONSchemaValidatorTestBase() {
+}
+
+void JSONSchemaValidatorTestBase::RunTests() {
+  TestComplex();
+  TestStringPattern();
+  TestEnum();
+  TestChoices();
+  TestExtends();
+  TestObject();
+  TestTypeReference();
+  TestArrayTuple();
+  TestArrayNonTuple();
+  TestString();
+  TestNumber();
+  TestTypeClassifier();
+  TestTypes();
+}
+
+void JSONSchemaValidatorTestBase::TestComplex() {
+  std::unique_ptr<base::DictionaryValue> schema(
+      LoadDictionary("complex_schema.json"));
+  std::unique_ptr<base::ListValue> instance(LoadList("complex_instance.json"));
+
+  ASSERT_TRUE(schema.get());
+  ASSERT_TRUE(instance.get());
+
+  ExpectValid(TEST_SOURCE, instance.get(), schema.get(), nullptr);
+  instance->Remove(instance->GetSize() - 1, nullptr);
+  ExpectValid(TEST_SOURCE, instance.get(), schema.get(), nullptr);
+  instance->Append(std::make_unique<base::DictionaryValue>());
+  ExpectNotValid(
+      TEST_SOURCE, instance.get(), schema.get(), nullptr, "1",
+      JSONSchemaValidator::FormatErrorMessage(
+          JSONSchemaValidator::kInvalidType, schema::kNumber, schema::kObject));
+  instance->Remove(instance->GetSize() - 1, nullptr);
+
+  base::DictionaryValue* item = nullptr;
+  ASSERT_TRUE(instance->GetDictionary(0, &item));
+  item->SetString("url", "xxxxxxxxxxx");
+
+  ExpectNotValid(TEST_SOURCE, instance.get(), schema.get(), nullptr, "0.url",
+                 JSONSchemaValidator::FormatErrorMessage(
+                     JSONSchemaValidator::kStringMaxLength, "10"));
+}
+
+void JSONSchemaValidatorTestBase::TestStringPattern() {
+  std::unique_ptr<base::DictionaryValue> schema(new base::DictionaryValue());
+  schema->SetString(schema::kType, schema::kString);
+  schema->SetString(schema::kPattern, "foo+");
+
+  ExpectValid(TEST_SOURCE,
+              std::unique_ptr<base::Value>(new base::Value("foo")).get(),
+              schema.get(), nullptr);
+  ExpectValid(TEST_SOURCE,
+              std::unique_ptr<base::Value>(new base::Value("foooooo")).get(),
+              schema.get(), nullptr);
+  ExpectNotValid(TEST_SOURCE,
+                 std::unique_ptr<base::Value>(new base::Value("bar")).get(),
+                 schema.get(), nullptr, std::string(),
+                 JSONSchemaValidator::FormatErrorMessage(
+                     JSONSchemaValidator::kStringPattern, "foo+"));
+}
+
+void JSONSchemaValidatorTestBase::TestEnum() {
+  std::unique_ptr<base::DictionaryValue> schema(
+      LoadDictionary("enum_schema.json"));
+
+  ExpectValid(TEST_SOURCE,
+              std::unique_ptr<base::Value>(new base::Value("foo")).get(),
+              schema.get(), nullptr);
+  ExpectValid(TEST_SOURCE,
+              std::unique_ptr<base::Value>(new base::Value(42)).get(),
+              schema.get(), nullptr);
+  ExpectValid(TEST_SOURCE,
+              std::unique_ptr<base::Value>(new base::Value(false)).get(),
+              schema.get(), nullptr);
+
+  ExpectNotValid(
+      TEST_SOURCE, std::unique_ptr<base::Value>(new base::Value("42")).get(),
+      schema.get(), nullptr, std::string(), JSONSchemaValidator::kInvalidEnum);
+  ExpectNotValid(TEST_SOURCE, std::make_unique<base::Value>().get(),
+                 schema.get(), nullptr, std::string(),
+                 JSONSchemaValidator::kInvalidEnum);
+}
+
+void JSONSchemaValidatorTestBase::TestChoices() {
+  std::unique_ptr<base::DictionaryValue> schema(
+      LoadDictionary("choices_schema.json"));
+
+  ExpectValid(TEST_SOURCE, std::make_unique<base::Value>().get(), schema.get(),
+              nullptr);
+  ExpectValid(TEST_SOURCE,
+              std::unique_ptr<base::Value>(new base::Value(42)).get(),
+              schema.get(), nullptr);
+
+  std::unique_ptr<base::DictionaryValue> instance(new base::DictionaryValue());
+  instance->SetString("foo", "bar");
+  ExpectValid(TEST_SOURCE, instance.get(), schema.get(), nullptr);
+
+  ExpectNotValid(TEST_SOURCE,
+                 std::unique_ptr<base::Value>(new base::Value("foo")).get(),
+                 schema.get(), nullptr, std::string(),
+                 JSONSchemaValidator::kInvalidChoice);
+  ExpectNotValid(TEST_SOURCE,
+                 std::unique_ptr<base::Value>(new base::ListValue()).get(),
+                 schema.get(), nullptr, std::string(),
+                 JSONSchemaValidator::kInvalidChoice);
+
+  instance->SetInteger("foo", 42);
+  ExpectNotValid(TEST_SOURCE, instance.get(), schema.get(), nullptr,
+                 std::string(), JSONSchemaValidator::kInvalidChoice);
+}
+
+void JSONSchemaValidatorTestBase::TestExtends() {
+  // TODO(aa): JS only
+}
+
+void JSONSchemaValidatorTestBase::TestObject() {
+  std::unique_ptr<base::DictionaryValue> schema(new base::DictionaryValue());
+  schema->SetString(schema::kType, schema::kObject);
+  schema->SetString("properties.foo.type", schema::kString);
+  schema->SetString("properties.bar.type", schema::kInteger);
+
+  std::unique_ptr<base::DictionaryValue> instance(new base::DictionaryValue());
+  instance->SetString("foo", "foo");
+  instance->SetInteger("bar", 42);
+
+  ExpectValid(TEST_SOURCE, instance.get(), schema.get(), nullptr);
+
+  instance->SetBoolean("extra", true);
+  ExpectNotValid(TEST_SOURCE, instance.get(), schema.get(), nullptr, "extra",
+                 JSONSchemaValidator::kUnexpectedProperty);
+  instance->Remove("extra", nullptr);
+
+  instance->Remove("bar", nullptr);
+  ExpectNotValid(TEST_SOURCE, instance.get(), schema.get(), nullptr, "bar",
+                 JSONSchemaValidator::kObjectPropertyIsRequired);
+
+  instance->SetString("bar", "42");
+  ExpectNotValid(TEST_SOURCE, instance.get(), schema.get(), nullptr, "bar",
+                 JSONSchemaValidator::FormatErrorMessage(
+                     JSONSchemaValidator::kInvalidType, schema::kInteger,
+                     schema::kString));
+  instance->SetInteger("bar", 42);
+
+  // Test "patternProperties".
+  instance->SetInteger("extra", 42);
+  ExpectNotValid(TEST_SOURCE, instance.get(), schema.get(), nullptr, "extra",
+                 JSONSchemaValidator::kUnexpectedProperty);
+  schema->SetString("patternProperties.extra+.type",
+                    schema::kInteger);
+  ExpectValid(TEST_SOURCE, instance.get(), schema.get(), nullptr);
+  instance->Remove("extra", nullptr);
+  instance->SetInteger("extraaa", 42);
+  ExpectValid(TEST_SOURCE, instance.get(), schema.get(), nullptr);
+  instance->Remove("extraaa", nullptr);
+  instance->SetInteger("extr", 42);
+  ExpectNotValid(TEST_SOURCE, instance.get(), schema.get(), nullptr, "extr",
+                 JSONSchemaValidator::kUnexpectedProperty);
+  instance->Remove("extr", nullptr);
+  schema->Remove(schema::kPatternProperties, nullptr);
+
+  // Test "patternProperties" and "properties" schemas are both checked if
+  // applicable.
+  schema->SetString("patternProperties.fo+.type", schema::kInteger);
+  ExpectNotValid(TEST_SOURCE, instance.get(), schema.get(), nullptr, "foo",
+                 JSONSchemaValidator::FormatErrorMessage(
+                     JSONSchemaValidator::kInvalidType, schema::kInteger,
+                     schema::kString));
+  instance->SetInteger("foo", 123);
+  ExpectNotValid(TEST_SOURCE, instance.get(), schema.get(), nullptr, "foo",
+                 JSONSchemaValidator::FormatErrorMessage(
+                     JSONSchemaValidator::kInvalidType, schema::kString,
+                     schema::kInteger));
+  instance->SetString("foo", "foo");
+  schema->Remove(schema::kPatternProperties, nullptr);
+
+  // Test additional properties.
+  base::DictionaryValue* additional_properties = schema->SetDictionary(
+      schema::kAdditionalProperties, std::make_unique<base::DictionaryValue>());
+  additional_properties->SetString(schema::kType, schema::kAny);
+
+  instance->SetBoolean("extra", true);
+  ExpectValid(TEST_SOURCE, instance.get(), schema.get(), nullptr);
+
+  instance->SetString("extra", "foo");
+  ExpectValid(TEST_SOURCE, instance.get(), schema.get(), nullptr);
+
+  additional_properties->SetString(schema::kType, schema::kBoolean);
+  instance->SetBoolean("extra", true);
+  ExpectValid(TEST_SOURCE, instance.get(), schema.get(), nullptr);
+
+  instance->SetString("extra", "foo");
+  ExpectNotValid(TEST_SOURCE, instance.get(), schema.get(), nullptr, "extra",
+                 JSONSchemaValidator::FormatErrorMessage(
+                     JSONSchemaValidator::kInvalidType, schema::kBoolean,
+                     schema::kString));
+  instance->Remove("extra", nullptr);
+
+  base::DictionaryValue* properties = nullptr;
+  base::DictionaryValue* bar_property = nullptr;
+  ASSERT_TRUE(schema->GetDictionary(schema::kProperties, &properties));
+  ASSERT_TRUE(properties->GetDictionary("bar", &bar_property));
+
+  bar_property->SetBoolean(schema::kOptional, true);
+  ExpectValid(TEST_SOURCE, instance.get(), schema.get(), nullptr);
+  instance->Remove("bar", nullptr);
+  ExpectValid(TEST_SOURCE, instance.get(), schema.get(), nullptr);
+  instance->Set("bar", std::make_unique<base::Value>());
+  ExpectNotValid(
+      TEST_SOURCE, instance.get(), schema.get(), nullptr, "bar",
+      JSONSchemaValidator::FormatErrorMessage(JSONSchemaValidator::kInvalidType,
+                                              schema::kInteger, schema::kNull));
+  instance->SetString("bar", "42");
+  ExpectNotValid(TEST_SOURCE, instance.get(), schema.get(), nullptr, "bar",
+                 JSONSchemaValidator::FormatErrorMessage(
+                     JSONSchemaValidator::kInvalidType, schema::kInteger,
+                     schema::kString));
+
+  // Verify that JSON parser handles dot in "patternProperties" well.
+  schema.reset(LoadDictionary("pattern_properties_dot.json"));
+  ASSERT_TRUE(schema->GetDictionary(schema::kPatternProperties, &properties));
+  ASSERT_TRUE(properties->HasKey("^.$"));
+
+  instance.reset(new base::DictionaryValue());
+  instance->SetString("a", "whatever");
+  ExpectValid(TEST_SOURCE, instance.get(), schema.get(), nullptr);
+  instance->SetString("foo", "bar");
+  ExpectNotValid(TEST_SOURCE, instance.get(), schema.get(), nullptr, "foo",
+                 JSONSchemaValidator::kUnexpectedProperty);
+}
+
+void JSONSchemaValidatorTestBase::TestTypeReference() {
+  std::unique_ptr<base::ListValue> types(LoadList("reference_types.json"));
+  ASSERT_TRUE(types.get());
+
+  std::unique_ptr<base::DictionaryValue> schema(new base::DictionaryValue());
+  schema->SetString(schema::kType, schema::kObject);
+  schema->SetString("properties.foo.type", schema::kString);
+  schema->SetString("properties.bar.$ref", "Max10Int");
+  schema->SetString("properties.baz.$ref", "MinLengthString");
+
+  std::unique_ptr<base::DictionaryValue> schema_inline(
+      new base::DictionaryValue());
+  schema_inline->SetString(schema::kType, schema::kObject);
+  schema_inline->SetString("properties.foo.type", schema::kString);
+  schema_inline->SetString("properties.bar.id", "NegativeInt");
+  schema_inline->SetString("properties.bar.type", schema::kInteger);
+  schema_inline->SetInteger("properties.bar.maximum", 0);
+  schema_inline->SetString("properties.baz.$ref", "NegativeInt");
+
+  std::unique_ptr<base::DictionaryValue> instance(new base::DictionaryValue());
+  instance->SetString("foo", "foo");
+  instance->SetInteger("bar", 4);
+  instance->SetString("baz", "ab");
+
+  std::unique_ptr<base::DictionaryValue> instance_inline(
+      new base::DictionaryValue());
+  instance_inline->SetString("foo", "foo");
+  instance_inline->SetInteger("bar", -4);
+  instance_inline->SetInteger("baz", -2);
+
+  ExpectValid(TEST_SOURCE, instance.get(), schema.get(), types.get());
+  ExpectValid(TEST_SOURCE, instance_inline.get(), schema_inline.get(), nullptr);
+
+  // Validation failure, but successful schema reference.
+  instance->SetString("baz", "a");
+  ExpectNotValid(TEST_SOURCE, instance.get(), schema.get(), types.get(),
+                 "baz", JSONSchemaValidator::FormatErrorMessage(
+                     JSONSchemaValidator::kStringMinLength, "2"));
+
+  instance_inline->SetInteger("bar", 20);
+  ExpectNotValid(TEST_SOURCE, instance_inline.get(), schema_inline.get(),
+                 nullptr, "bar",
+                 JSONSchemaValidator::FormatErrorMessage(
+                     JSONSchemaValidator::kNumberMaximum, "0"));
+
+  // Remove MinLengthString type.
+  types->Remove(types->GetSize() - 1, nullptr);
+  instance->SetString("baz", "ab");
+  ExpectNotValid(TEST_SOURCE, instance.get(), schema.get(), types.get(),
+                 "bar", JSONSchemaValidator::FormatErrorMessage(
+                     JSONSchemaValidator::kUnknownTypeReference,
+                     "Max10Int"));
+
+  // Remove internal type "NegativeInt".
+  schema_inline->Remove("properties.bar", nullptr);
+  instance_inline->Remove("bar", nullptr);
+  ExpectNotValid(
+      TEST_SOURCE, instance_inline.get(), schema_inline.get(), nullptr, "baz",
+      JSONSchemaValidator::FormatErrorMessage(
+          JSONSchemaValidator::kUnknownTypeReference, "NegativeInt"));
+}
+
+void JSONSchemaValidatorTestBase::TestArrayTuple() {
+  std::unique_ptr<base::DictionaryValue> schema(
+      LoadDictionary("array_tuple_schema.json"));
+  ASSERT_TRUE(schema.get());
+
+  std::unique_ptr<base::ListValue> instance(new base::ListValue());
+  instance->AppendString("42");
+  instance->AppendInteger(42);
+
+  ExpectValid(TEST_SOURCE, instance.get(), schema.get(), nullptr);
+
+  instance->AppendString("anything");
+  ExpectNotValid(TEST_SOURCE, instance.get(), schema.get(), nullptr,
+                 std::string(),
+                 JSONSchemaValidator::FormatErrorMessage(
+                     JSONSchemaValidator::kArrayMaxItems, "2"));
+
+  instance->Remove(1, nullptr);
+  instance->Remove(1, nullptr);
+  ExpectNotValid(TEST_SOURCE, instance.get(), schema.get(), nullptr, "1",
+                 JSONSchemaValidator::kArrayItemRequired);
+
+  instance->Set(0, std::make_unique<base::Value>(42));
+  instance->AppendInteger(42);
+  ExpectNotValid(TEST_SOURCE, instance.get(), schema.get(), nullptr, "0",
+                 JSONSchemaValidator::FormatErrorMessage(
+                     JSONSchemaValidator::kInvalidType, schema::kString,
+                     schema::kInteger));
+
+  base::DictionaryValue* additional_properties = schema->SetDictionary(
+      schema::kAdditionalProperties, std::make_unique<base::DictionaryValue>());
+  additional_properties->SetString(schema::kType, schema::kAny);
+  instance->Set(0, std::make_unique<base::Value>("42"));
+  instance->AppendString("anything");
+  ExpectValid(TEST_SOURCE, instance.get(), schema.get(), nullptr);
+  instance->Set(2, std::make_unique<base::ListValue>());
+  ExpectValid(TEST_SOURCE, instance.get(), schema.get(), nullptr);
+
+  additional_properties->SetString(schema::kType, schema::kBoolean);
+  ExpectNotValid(
+      TEST_SOURCE, instance.get(), schema.get(), nullptr, "2",
+      JSONSchemaValidator::FormatErrorMessage(
+          JSONSchemaValidator::kInvalidType, schema::kBoolean, schema::kArray));
+  instance->Set(2, std::make_unique<base::Value>(false));
+  ExpectValid(TEST_SOURCE, instance.get(), schema.get(), nullptr);
+
+  base::ListValue* items_schema = nullptr;
+  base::DictionaryValue* item0_schema = nullptr;
+  ASSERT_TRUE(schema->GetList(schema::kItems, &items_schema));
+  ASSERT_TRUE(items_schema->GetDictionary(0, &item0_schema));
+  item0_schema->SetBoolean(schema::kOptional, true);
+  instance->Remove(2, nullptr);
+  ExpectValid(TEST_SOURCE, instance.get(), schema.get(), nullptr);
+  // TODO(aa): I think this is inconsistent with the handling of NULL+optional
+  // for objects.
+  instance->Set(0, std::make_unique<base::Value>());
+  ExpectValid(TEST_SOURCE, instance.get(), schema.get(), nullptr);
+  instance->Set(0, std::make_unique<base::Value>(42));
+  ExpectNotValid(TEST_SOURCE, instance.get(), schema.get(), nullptr, "0",
+                 JSONSchemaValidator::FormatErrorMessage(
+                     JSONSchemaValidator::kInvalidType, schema::kString,
+                     schema::kInteger));
+}
+
+void JSONSchemaValidatorTestBase::TestArrayNonTuple() {
+  std::unique_ptr<base::DictionaryValue> schema(new base::DictionaryValue());
+  schema->SetString(schema::kType, schema::kArray);
+  schema->SetString("items.type", schema::kString);
+  schema->SetInteger(schema::kMinItems, 2);
+  schema->SetInteger(schema::kMaxItems, 3);
+
+  std::unique_ptr<base::ListValue> instance(new base::ListValue());
+  instance->AppendString("x");
+  instance->AppendString("x");
+
+  ExpectValid(TEST_SOURCE, instance.get(), schema.get(), nullptr);
+  instance->AppendString("x");
+  ExpectValid(TEST_SOURCE, instance.get(), schema.get(), nullptr);
+
+  instance->AppendString("x");
+  ExpectNotValid(TEST_SOURCE, instance.get(), schema.get(), nullptr,
+                 std::string(),
+                 JSONSchemaValidator::FormatErrorMessage(
+                     JSONSchemaValidator::kArrayMaxItems, "3"));
+  instance->Remove(1, nullptr);
+  instance->Remove(1, nullptr);
+  instance->Remove(1, nullptr);
+  ExpectNotValid(TEST_SOURCE, instance.get(), schema.get(), nullptr,
+                 std::string(),
+                 JSONSchemaValidator::FormatErrorMessage(
+                     JSONSchemaValidator::kArrayMinItems, "2"));
+
+  instance->Remove(1, nullptr);
+  instance->AppendInteger(42);
+  ExpectNotValid(TEST_SOURCE, instance.get(), schema.get(), nullptr, "1",
+                 JSONSchemaValidator::FormatErrorMessage(
+                     JSONSchemaValidator::kInvalidType, schema::kString,
+                     schema::kInteger));
+}
+
+void JSONSchemaValidatorTestBase::TestString() {
+  std::unique_ptr<base::DictionaryValue> schema(new base::DictionaryValue());
+  schema->SetString(schema::kType, schema::kString);
+  schema->SetInteger(schema::kMinLength, 1);
+  schema->SetInteger(schema::kMaxLength, 10);
+
+  ExpectValid(TEST_SOURCE,
+              std::unique_ptr<base::Value>(new base::Value("x")).get(),
+              schema.get(), nullptr);
+  ExpectValid(TEST_SOURCE,
+              std::unique_ptr<base::Value>(new base::Value("xxxxxxxxxx")).get(),
+              schema.get(), nullptr);
+
+  ExpectNotValid(
+      TEST_SOURCE,
+      std::unique_ptr<base::Value>(new base::Value(std::string())).get(),
+      schema.get(), nullptr, std::string(),
+      JSONSchemaValidator::FormatErrorMessage(
+          JSONSchemaValidator::kStringMinLength, "1"));
+  ExpectNotValid(
+      TEST_SOURCE,
+      std::unique_ptr<base::Value>(new base::Value("xxxxxxxxxxx")).get(),
+      schema.get(), nullptr, std::string(),
+      JSONSchemaValidator::FormatErrorMessage(
+          JSONSchemaValidator::kStringMaxLength, "10"));
+}
+
+void JSONSchemaValidatorTestBase::TestNumber() {
+  std::unique_ptr<base::DictionaryValue> schema(new base::DictionaryValue());
+  schema->SetString(schema::kType, schema::kNumber);
+  schema->SetInteger(schema::kMinimum, 1);
+  schema->SetInteger(schema::kMaximum, 100);
+  schema->SetInteger("maxDecimal", 2);
+
+  ExpectValid(TEST_SOURCE,
+              std::unique_ptr<base::Value>(new base::Value(1)).get(),
+              schema.get(), nullptr);
+  ExpectValid(TEST_SOURCE,
+              std::unique_ptr<base::Value>(new base::Value(50)).get(),
+              schema.get(), nullptr);
+  ExpectValid(TEST_SOURCE,
+              std::unique_ptr<base::Value>(new base::Value(100)).get(),
+              schema.get(), nullptr);
+  ExpectValid(TEST_SOURCE,
+              std::unique_ptr<base::Value>(new base::Value(88.88)).get(),
+              schema.get(), nullptr);
+
+  ExpectNotValid(TEST_SOURCE,
+                 std::unique_ptr<base::Value>(new base::Value(0.5)).get(),
+                 schema.get(), nullptr, std::string(),
+                 JSONSchemaValidator::FormatErrorMessage(
+                     JSONSchemaValidator::kNumberMinimum, "1"));
+  ExpectNotValid(TEST_SOURCE,
+                 std::unique_ptr<base::Value>(new base::Value(100.1)).get(),
+                 schema.get(), nullptr, std::string(),
+                 JSONSchemaValidator::FormatErrorMessage(
+                     JSONSchemaValidator::kNumberMaximum, "100"));
+}
+
+void JSONSchemaValidatorTestBase::TestTypeClassifier() {
+  EXPECT_EQ(std::string(schema::kBoolean),
+            JSONSchemaValidator::GetJSONSchemaType(
+                std::unique_ptr<base::Value>(new base::Value(true)).get()));
+  EXPECT_EQ(std::string(schema::kBoolean),
+            JSONSchemaValidator::GetJSONSchemaType(
+                std::unique_ptr<base::Value>(new base::Value(false)).get()));
+
+  // It doesn't matter whether the C++ type is 'integer' or 'real'. If the
+  // number is integral and within the representable range of integers in
+  // double, it's classified as 'integer'.
+  EXPECT_EQ(std::string(schema::kInteger),
+            JSONSchemaValidator::GetJSONSchemaType(
+                std::unique_ptr<base::Value>(new base::Value(42)).get()));
+  EXPECT_EQ(std::string(schema::kInteger),
+            JSONSchemaValidator::GetJSONSchemaType(
+                std::unique_ptr<base::Value>(new base::Value(0)).get()));
+  EXPECT_EQ(std::string(schema::kInteger),
+            JSONSchemaValidator::GetJSONSchemaType(
+                std::unique_ptr<base::Value>(new base::Value(42)).get()));
+  EXPECT_EQ(
+      std::string(schema::kInteger),
+      JSONSchemaValidator::GetJSONSchemaType(
+          std::unique_ptr<base::Value>(new base::Value(pow(2.0, DBL_MANT_DIG)))
+              .get()));
+  EXPECT_EQ(
+      std::string(schema::kInteger),
+      JSONSchemaValidator::GetJSONSchemaType(
+          std::unique_ptr<base::Value>(new base::Value(pow(-2.0, DBL_MANT_DIG)))
+              .get()));
+
+  // "number" is only used for non-integral numbers, or numbers beyond what
+  // double can accurately represent.
+  EXPECT_EQ(std::string(schema::kNumber),
+            JSONSchemaValidator::GetJSONSchemaType(
+                std::unique_ptr<base::Value>(new base::Value(88.8)).get()));
+  EXPECT_EQ(std::string(schema::kNumber),
+            JSONSchemaValidator::GetJSONSchemaType(
+                std::unique_ptr<base::Value>(
+                    new base::Value(pow(2.0, DBL_MANT_DIG) * 2))
+                    .get()));
+  EXPECT_EQ(std::string(schema::kNumber),
+            JSONSchemaValidator::GetJSONSchemaType(
+                std::unique_ptr<base::Value>(
+                    new base::Value(pow(-2.0, DBL_MANT_DIG) * 2))
+                    .get()));
+
+  EXPECT_EQ(std::string(schema::kString),
+            JSONSchemaValidator::GetJSONSchemaType(
+                std::unique_ptr<base::Value>(new base::Value("foo")).get()));
+  EXPECT_EQ(std::string(schema::kArray),
+            JSONSchemaValidator::GetJSONSchemaType(
+                std::unique_ptr<base::Value>(new base::ListValue()).get()));
+  EXPECT_EQ(
+      std::string(schema::kObject),
+      JSONSchemaValidator::GetJSONSchemaType(
+          std::unique_ptr<base::Value>(new base::DictionaryValue()).get()));
+  EXPECT_EQ(std::string(schema::kNull),
+            JSONSchemaValidator::GetJSONSchemaType(
+                std::make_unique<base::Value>().get()));
+}
+
+void JSONSchemaValidatorTestBase::TestTypes() {
+  std::unique_ptr<base::DictionaryValue> schema(new base::DictionaryValue());
+
+  // valid
+  schema->SetString(schema::kType, schema::kObject);
+  ExpectValid(TEST_SOURCE,
+              std::unique_ptr<base::Value>(new base::DictionaryValue()).get(),
+              schema.get(), nullptr);
+
+  schema->SetString(schema::kType, schema::kArray);
+  ExpectValid(TEST_SOURCE,
+              std::unique_ptr<base::Value>(new base::ListValue()).get(),
+              schema.get(), nullptr);
+
+  schema->SetString(schema::kType, schema::kString);
+  ExpectValid(TEST_SOURCE,
+              std::unique_ptr<base::Value>(new base::Value("foobar")).get(),
+              schema.get(), nullptr);
+
+  schema->SetString(schema::kType, schema::kNumber);
+  ExpectValid(TEST_SOURCE,
+              std::unique_ptr<base::Value>(new base::Value(88.8)).get(),
+              schema.get(), nullptr);
+  ExpectValid(TEST_SOURCE,
+              std::unique_ptr<base::Value>(new base::Value(42)).get(),
+              schema.get(), nullptr);
+  ExpectValid(TEST_SOURCE,
+              std::unique_ptr<base::Value>(new base::Value(42)).get(),
+              schema.get(), nullptr);
+  ExpectValid(TEST_SOURCE,
+              std::unique_ptr<base::Value>(new base::Value(0)).get(),
+              schema.get(), nullptr);
+
+  schema->SetString(schema::kType, schema::kInteger);
+  ExpectValid(TEST_SOURCE,
+              std::unique_ptr<base::Value>(new base::Value(42)).get(),
+              schema.get(), nullptr);
+  ExpectValid(TEST_SOURCE,
+              std::unique_ptr<base::Value>(new base::Value(42)).get(),
+              schema.get(), nullptr);
+  ExpectValid(TEST_SOURCE,
+              std::unique_ptr<base::Value>(new base::Value(0)).get(),
+              schema.get(), nullptr);
+  ExpectValid(
+      TEST_SOURCE,
+      std::unique_ptr<base::Value>(new base::Value(pow(2.0, DBL_MANT_DIG)))
+          .get(),
+      schema.get(), nullptr);
+  ExpectValid(
+      TEST_SOURCE,
+      std::unique_ptr<base::Value>(new base::Value(pow(-2.0, DBL_MANT_DIG)))
+          .get(),
+      schema.get(), nullptr);
+
+  schema->SetString(schema::kType, schema::kBoolean);
+  ExpectValid(TEST_SOURCE,
+              std::unique_ptr<base::Value>(new base::Value(false)).get(),
+              schema.get(), nullptr);
+  ExpectValid(TEST_SOURCE,
+              std::unique_ptr<base::Value>(new base::Value(true)).get(),
+              schema.get(), nullptr);
+
+  schema->SetString(schema::kType, schema::kNull);
+  ExpectValid(TEST_SOURCE, std::make_unique<base::Value>().get(), schema.get(),
+              nullptr);
+
+  // not valid
+  schema->SetString(schema::kType, schema::kObject);
+  ExpectNotValid(
+      TEST_SOURCE, std::unique_ptr<base::Value>(new base::ListValue()).get(),
+      schema.get(), nullptr, std::string(),
+      JSONSchemaValidator::FormatErrorMessage(JSONSchemaValidator::kInvalidType,
+                                              schema::kObject, schema::kArray));
+
+  schema->SetString(schema::kType, schema::kObject);
+  ExpectNotValid(
+      TEST_SOURCE, std::make_unique<base::Value>().get(), schema.get(), nullptr,
+      std::string(),
+      JSONSchemaValidator::FormatErrorMessage(JSONSchemaValidator::kInvalidType,
+                                              schema::kObject, schema::kNull));
+
+  schema->SetString(schema::kType, schema::kArray);
+  ExpectNotValid(
+      TEST_SOURCE, std::unique_ptr<base::Value>(new base::Value(42)).get(),
+      schema.get(), nullptr, std::string(),
+      JSONSchemaValidator::FormatErrorMessage(
+          JSONSchemaValidator::kInvalidType, schema::kArray, schema::kInteger));
+
+  schema->SetString(schema::kType, schema::kString);
+  ExpectNotValid(TEST_SOURCE,
+                 std::unique_ptr<base::Value>(new base::Value(42)).get(),
+                 schema.get(), nullptr, std::string(),
+                 JSONSchemaValidator::FormatErrorMessage(
+                     JSONSchemaValidator::kInvalidType, schema::kString,
+                     schema::kInteger));
+
+  schema->SetString(schema::kType, schema::kNumber);
+  ExpectNotValid(
+      TEST_SOURCE, std::unique_ptr<base::Value>(new base::Value("42")).get(),
+      schema.get(), nullptr, std::string(),
+      JSONSchemaValidator::FormatErrorMessage(
+          JSONSchemaValidator::kInvalidType, schema::kNumber, schema::kString));
+
+  schema->SetString(schema::kType, schema::kInteger);
+  ExpectNotValid(TEST_SOURCE,
+                 std::unique_ptr<base::Value>(new base::Value(88.8)).get(),
+                 schema.get(), nullptr, std::string(),
+                 JSONSchemaValidator::kInvalidTypeIntegerNumber);
+
+  schema->SetString(schema::kType, schema::kBoolean);
+  ExpectNotValid(TEST_SOURCE,
+                 std::unique_ptr<base::Value>(new base::Value(1)).get(),
+                 schema.get(), nullptr, std::string(),
+                 JSONSchemaValidator::FormatErrorMessage(
+                     JSONSchemaValidator::kInvalidType, schema::kBoolean,
+                     schema::kInteger));
+
+  schema->SetString(schema::kType, schema::kNull);
+  ExpectNotValid(
+      TEST_SOURCE, std::unique_ptr<base::Value>(new base::Value(false)).get(),
+      schema.get(), nullptr, std::string(),
+      JSONSchemaValidator::FormatErrorMessage(JSONSchemaValidator::kInvalidType,
+                                              schema::kNull, schema::kBoolean));
+}
diff --git a/components/json_schema/json_schema_validator_unittest_base.h b/components/json_schema/json_schema_validator_unittest_base.h
new file mode 100644
index 0000000..ff661ac
--- /dev/null
+++ b/components/json_schema/json_schema_validator_unittest_base.h
@@ -0,0 +1,56 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_JSON_SCHEMA_JSON_SCHEMA_VALIDATOR_UNITTEST_BASE_H_
+#define COMPONENTS_JSON_SCHEMA_JSON_SCHEMA_VALIDATOR_UNITTEST_BASE_H_
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+class DictionaryValue;
+class ListValue;
+class Value;
+}
+
+// Base class for unit tests for JSONSchemaValidator. There is currently only
+// one implementation, JSONSchemaValidatorCPPTest.
+//
+// TODO(aa): Refactor extensions/test/data/json_schema_test.js into
+// JSONSchemaValidatorJSTest that inherits from this.
+class JSONSchemaValidatorTestBase : public testing::Test {
+ public:
+  JSONSchemaValidatorTestBase();
+
+  void RunTests();
+
+ protected:
+  virtual void ExpectValid(const std::string& test_source,
+                           base::Value* instance,
+                           base::DictionaryValue* schema,
+                           base::ListValue* types) = 0;
+
+  virtual void ExpectNotValid(const std::string& test_source,
+                              base::Value* instance,
+                              base::DictionaryValue* schema,
+                              base::ListValue* types,
+                              const std::string& expected_error_path,
+                              const std::string& expected_error_message) = 0;
+
+ private:
+  void TestComplex();
+  void TestStringPattern();
+  void TestEnum();
+  void TestChoices();
+  void TestExtends();
+  void TestObject();
+  void TestTypeReference();
+  void TestArrayTuple();
+  void TestArrayNonTuple();
+  void TestString();
+  void TestNumber();
+  void TestTypeClassifier();
+  void TestTypes();
+};
+
+#endif  // COMPONENTS_JSON_SCHEMA_JSON_SCHEMA_VALIDATOR_UNITTEST_BASE_H_
diff --git a/components/policy/core/common/async_policy_loader.cc b/components/policy/core/common/async_policy_loader.cc
new file mode 100644
index 0000000..b0edee2
--- /dev/null
+++ b/components/policy/core/common/async_policy_loader.cc
@@ -0,0 +1,137 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/policy/core/common/async_policy_loader.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/sequenced_task_runner.h"
+#include "components/policy/core/common/policy_bundle.h"
+
+using base::Time;
+using base::TimeDelta;
+
+namespace policy {
+
+namespace {
+
+// Amount of time to wait for the files on disk to settle before trying to load
+// them. This alleviates the problem of reading partially written files and
+// makes it possible to batch quasi-simultaneous changes.
+constexpr TimeDelta kSettleInterval = TimeDelta::FromSeconds(5);
+
+// The time interval for rechecking policy. This is the fallback in case the
+// implementation never detects changes.
+constexpr TimeDelta kReloadInterval = TimeDelta::FromMinutes(15);
+
+}  // namespace
+
+AsyncPolicyLoader::AsyncPolicyLoader(
+    const scoped_refptr<base::SequencedTaskRunner>& task_runner)
+    : task_runner_(task_runner) {}
+
+AsyncPolicyLoader::~AsyncPolicyLoader() {}
+
+Time AsyncPolicyLoader::LastModificationTime() {
+  return Time();
+}
+
+void AsyncPolicyLoader::Reload(bool force) {
+  DCHECK(task_runner_->RunsTasksInCurrentSequence());
+
+  TimeDelta delay;
+  Time now = Time::Now();
+  // Check if there was a recent modification to the underlying files.
+  if (!force && !IsSafeToReload(now, &delay)) {
+    ScheduleNextReload(delay);
+    return;
+  }
+
+  std::unique_ptr<PolicyBundle> bundle(Load());
+
+  // Check if there was a modification while reading.
+  if (!force && !IsSafeToReload(now, &delay)) {
+    ScheduleNextReload(delay);
+    return;
+  }
+
+  // Filter out mismatching policies.
+  schema_map_->FilterBundle(bundle.get());
+
+  update_callback_.Run(std::move(bundle));
+  ScheduleNextReload(kReloadInterval);
+}
+
+std::unique_ptr<PolicyBundle> AsyncPolicyLoader::InitialLoad(
+    const scoped_refptr<SchemaMap>& schema_map) {
+  // This is the first load, early during startup. Use this to record the
+  // initial |last_modification_time_|, so that potential changes made before
+  // installing the watches can be detected.
+  last_modification_time_ = LastModificationTime();
+  schema_map_ = schema_map;
+  std::unique_ptr<PolicyBundle> bundle(Load());
+  // Filter out mismatching policies.
+  schema_map_->FilterBundle(bundle.get());
+  return bundle;
+}
+
+void AsyncPolicyLoader::Init(const UpdateCallback& update_callback) {
+  DCHECK(task_runner_->RunsTasksInCurrentSequence());
+  DCHECK(update_callback_.is_null());
+  DCHECK(!update_callback.is_null());
+  update_callback_ = update_callback;
+
+  InitOnBackgroundThread();
+
+  // There might have been changes to the underlying files since the initial
+  // load and before the watchers have been created.
+  if (LastModificationTime() != last_modification_time_)
+    Reload(false);
+
+  // Start periodic refreshes.
+  ScheduleNextReload(kReloadInterval);
+}
+
+void AsyncPolicyLoader::RefreshPolicies(scoped_refptr<SchemaMap> schema_map) {
+  DCHECK(task_runner_->RunsTasksInCurrentSequence());
+  schema_map_ = schema_map;
+  Reload(true);
+}
+
+void AsyncPolicyLoader::ScheduleNextReload(TimeDelta delay) {
+  DCHECK(task_runner_->RunsTasksInCurrentSequence());
+  weak_factory_.InvalidateWeakPtrs();
+  task_runner_->PostDelayedTask(FROM_HERE,
+                                base::Bind(&AsyncPolicyLoader::Reload,
+                                           weak_factory_.GetWeakPtr(),
+                                           false /* force */),
+                                delay);
+}
+
+bool AsyncPolicyLoader::IsSafeToReload(const Time& now, TimeDelta* delay) {
+  Time last_modification = LastModificationTime();
+  if (last_modification.is_null())
+    return true;
+
+  // If there was a change since the last recorded modification, wait some more.
+  if (last_modification != last_modification_time_) {
+    last_modification_time_ = last_modification;
+    last_modification_clock_ = now;
+    *delay = kSettleInterval;
+    return false;
+  }
+
+  // Check whether the settle interval has elapsed.
+  const TimeDelta age = now - last_modification_clock_;
+  if (age < kSettleInterval) {
+    *delay = kSettleInterval - age;
+    return false;
+  }
+
+  return true;
+}
+
+}  // namespace policy
diff --git a/components/policy/core/common/async_policy_loader.h b/components/policy/core/common/async_policy_loader.h
new file mode 100644
index 0000000..04ea60b
--- /dev/null
+++ b/components/policy/core/common/async_policy_loader.h
@@ -0,0 +1,126 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_POLICY_CORE_COMMON_ASYNC_POLICY_LOADER_H_
+#define COMPONENTS_POLICY_CORE_COMMON_ASYNC_POLICY_LOADER_H_
+
+#include <memory>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/time/time.h"
+#include "components/policy/core/common/schema_map.h"
+#include "components/policy/policy_export.h"
+
+namespace base {
+class SequencedTaskRunner;
+}
+
+namespace policy {
+
+class PolicyBundle;
+
+// Base implementation for platform-specific policy loaders. Together with the
+// AsyncPolicyProvider, this base implementation takes care of the initial load,
+// periodic reloads, watching file changes, refreshing policies and object
+// lifetime.
+//
+// All methods are invoked on the background |task_runner_|, including the
+// destructor. The only exceptions are the constructor (which may be called on
+// any thread), InitialLoad() which is called on the thread that owns the
+// provider and the calls of Load() and LastModificationTime() during the
+// initial load.
+// Also, during tests the destructor may be called on the main thread.
+class POLICY_EXPORT AsyncPolicyLoader {
+ public:
+  explicit AsyncPolicyLoader(
+      const scoped_refptr<base::SequencedTaskRunner>& task_runner);
+  virtual ~AsyncPolicyLoader();
+
+  // Gets a SequencedTaskRunner backed by the background thread.
+  base::SequencedTaskRunner* task_runner() const { return task_runner_.get(); }
+
+  // Returns the currently configured policies. Load() is always invoked on
+  // the background thread, except for the initial Load() at startup which is
+  // invoked from the thread that owns the provider.
+  virtual std::unique_ptr<PolicyBundle> Load() = 0;
+
+  // Allows implementations to finalize their initialization on the background
+  // thread (e.g. setup file watchers).
+  virtual void InitOnBackgroundThread() = 0;
+
+  // Implementations should return the time of the last modification detected,
+  // or base::Time() if it doesn't apply, which is the default.
+  virtual base::Time LastModificationTime();
+
+  // Used by the AsyncPolicyProvider to do the initial Load(). The first load
+  // is also used to initialize |last_modification_time_| and
+  // |schema_map_|.
+  std::unique_ptr<PolicyBundle> InitialLoad(
+      const scoped_refptr<SchemaMap>& schemas);
+
+  // Implementations should invoke Reload() when a change is detected. This
+  // must be invoked from the background thread and will trigger a Load(),
+  // and pass the returned bundle to the provider.
+  // The load is immediate when |force| is true. Otherwise, the loader
+  // reschedules the reload until the LastModificationTime() is a couple of
+  // seconds in the past. This mitigates the problem of reading files that are
+  // currently being written to, and whose contents are incomplete.
+  // A reload is posted periodically, if it hasn't been triggered recently. This
+  // makes sure the policies are reloaded if the update events aren't triggered.
+  void Reload(bool force);
+
+  const scoped_refptr<SchemaMap>& schema_map() const { return schema_map_; }
+
+ private:
+  // Allow AsyncPolicyProvider to call Init().
+  friend class AsyncPolicyProvider;
+
+  typedef base::Callback<void(std::unique_ptr<PolicyBundle>)> UpdateCallback;
+
+  // Used by the AsyncPolicyProvider to install the |update_callback_|.
+  // Invoked on the background thread.
+  void Init(const UpdateCallback& update_callback);
+
+  // Used by the AsyncPolicyProvider to reload with an updated SchemaMap.
+  void RefreshPolicies(scoped_refptr<SchemaMap> schema_map);
+
+  // Cancels any pending periodic reload and posts one |delay| time units from
+  // now.
+  void ScheduleNextReload(base::TimeDelta delay);
+
+  // Checks if the underlying files haven't changed recently, by checking the
+  // LastModificationTime(). |delay| is updated with a suggested time to wait
+  // before retrying when this returns false.
+  bool IsSafeToReload(const base::Time& now, base::TimeDelta* delay);
+
+  // Task runner for running background jobs.
+  const scoped_refptr<base::SequencedTaskRunner> task_runner_;
+
+  // Callback for updates, passed in Init().
+  UpdateCallback update_callback_;
+
+  // Records last known modification timestamp.
+  base::Time last_modification_time_;
+
+  // The wall clock time at which the last modification timestamp was
+  // recorded.  It's better to not assume the file notification time and the
+  // wall clock times come from the same source, just in case there is some
+  // non-local filesystem involved.
+  base::Time last_modification_clock_;
+
+  // The current policy schemas that this provider should load.
+  scoped_refptr<SchemaMap> schema_map_;
+
+  // Used to get WeakPtrs for the periodic reload task.
+  base::WeakPtrFactory<AsyncPolicyLoader> weak_factory_{this};
+
+  DISALLOW_COPY_AND_ASSIGN(AsyncPolicyLoader);
+};
+
+}  // namespace policy
+
+#endif  // COMPONENTS_POLICY_CORE_COMMON_ASYNC_POLICY_LOADER_H_
diff --git a/components/policy/core/common/async_policy_provider.cc b/components/policy/core/common/async_policy_provider.cc
new file mode 100644
index 0000000..9fe4153
--- /dev/null
+++ b/components/policy/core/common/async_policy_provider.cc
@@ -0,0 +1,131 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/policy/core/common/async_policy_provider.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/location.h"
+#include "base/sequenced_task_runner.h"
+#include "base/single_thread_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/policy/core/common/async_policy_loader.h"
+#include "components/policy/core/common/policy_bundle.h"
+#include "components/policy/core/common/schema_registry.h"
+
+namespace policy {
+
+AsyncPolicyProvider::AsyncPolicyProvider(
+    SchemaRegistry* registry,
+    std::unique_ptr<AsyncPolicyLoader> loader)
+    : loader_(std::move(loader)), weak_factory_(this) {
+  // Make an immediate synchronous load on startup.
+  OnLoaderReloaded(loader_->InitialLoad(registry->schema_map()));
+}
+
+AsyncPolicyProvider::~AsyncPolicyProvider() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
+
+void AsyncPolicyProvider::Init(SchemaRegistry* registry) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  ConfigurationPolicyProvider::Init(registry);
+
+  if (!loader_)
+    return;
+
+  AsyncPolicyLoader::UpdateCallback callback =
+      base::Bind(&AsyncPolicyProvider::LoaderUpdateCallback,
+                 base::ThreadTaskRunnerHandle::Get(),
+                 weak_factory_.GetWeakPtr());
+  bool post = loader_->task_runner()->PostTask(
+      FROM_HERE,
+      base::Bind(&AsyncPolicyLoader::Init,
+                 base::Unretained(loader_.get()),
+                 callback));
+  DCHECK(post) << "AsyncPolicyProvider::Init() called with threads not running";
+}
+
+void AsyncPolicyProvider::Shutdown() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  // Note on the lifetime of |loader_|:
+  // The |loader_| lives on the background thread, and is deleted from here.
+  // This means that posting tasks on the |loader_| to the background thread
+  // from the AsyncPolicyProvider is always safe, since a potential DeleteSoon()
+  // is only posted from here. The |loader_| posts back to the
+  // AsyncPolicyProvider through the |update_callback_|, which has a WeakPtr to
+  // |this|.
+  // If threads are spinning, delete the loader on the thread it lives on. If
+  // there are no threads, kill it immediately.
+  AsyncPolicyLoader* loader_to_delete = loader_.release();
+  if (!loader_to_delete->task_runner()->DeleteSoon(FROM_HERE, loader_to_delete))
+    delete loader_to_delete;
+  ConfigurationPolicyProvider::Shutdown();
+}
+
+void AsyncPolicyProvider::RefreshPolicies() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  // Subtle: RefreshPolicies() has a contract that requires the next policy
+  // update notification (triggered from UpdatePolicy()) to reflect any changes
+  // made before this call. So if a caller has modified the policy settings and
+  // invoked RefreshPolicies(), then by the next notification these policies
+  // should already be provided.
+  // However, it's also possible that an asynchronous Reload() is in progress
+  // and just posted OnLoaderReloaded(). Therefore a task is posted to the
+  // background thread before posting the next Reload, to prevent a potential
+  // concurrent Reload() from triggering a notification too early. If another
+  // refresh task has been posted, it is invalidated now.
+  if (!loader_)
+    return;
+  refresh_callback_.Reset(
+      base::Bind(&AsyncPolicyProvider::ReloadAfterRefreshSync,
+                 weak_factory_.GetWeakPtr()));
+  loader_->task_runner()->PostTaskAndReply(FROM_HERE, base::DoNothing(),
+                                           refresh_callback_.callback());
+}
+
+void AsyncPolicyProvider::ReloadAfterRefreshSync() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  // This task can only enter if it was posted from RefreshPolicies(), and it
+  // hasn't been cancelled meanwhile by another call to RefreshPolicies().
+  DCHECK(!refresh_callback_.IsCancelled());
+  // There can't be another refresh callback pending now, since its creation
+  // in RefreshPolicies() would have cancelled the current execution. So it's
+  // safe to cancel the |refresh_callback_| now, so that OnLoaderReloaded()
+  // sees that there is no refresh pending.
+  refresh_callback_.Cancel();
+
+  if (!loader_)
+    return;
+
+  loader_->task_runner()->PostTask(
+      FROM_HERE,
+      base::Bind(&AsyncPolicyLoader::RefreshPolicies,
+                 base::Unretained(loader_.get()),
+                 schema_map()));
+}
+
+void AsyncPolicyProvider::OnLoaderReloaded(
+    std::unique_ptr<PolicyBundle> bundle) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  // Only propagate policy updates if there are no pending refreshes, and if
+  // Shutdown() hasn't been called yet.
+  if (refresh_callback_.IsCancelled() && loader_)
+    UpdatePolicy(std::move(bundle));
+}
+
+// static
+void AsyncPolicyProvider::LoaderUpdateCallback(
+    scoped_refptr<base::SingleThreadTaskRunner> runner,
+    base::WeakPtr<AsyncPolicyProvider> weak_this,
+    std::unique_ptr<PolicyBundle> bundle) {
+  runner->PostTask(FROM_HERE,
+                   base::BindOnce(&AsyncPolicyProvider::OnLoaderReloaded,
+                                  weak_this, std::move(bundle)));
+}
+
+}  // namespace policy
diff --git a/components/policy/core/common/async_policy_provider.h b/components/policy/core/common/async_policy_provider.h
new file mode 100644
index 0000000..3ef4c4b
--- /dev/null
+++ b/components/policy/core/common/async_policy_provider.h
@@ -0,0 +1,80 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_POLICY_CORE_COMMON_ASYNC_POLICY_PROVIDER_H_
+#define COMPONENTS_POLICY_CORE_COMMON_ASYNC_POLICY_PROVIDER_H_
+
+#include <memory>
+
+#include "base/cancelable_callback.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/sequence_checker.h"
+#include "components/policy/core/common/configuration_policy_provider.h"
+#include "components/policy/policy_export.h"
+
+namespace base {
+class SingleThreadTaskRunner;
+}
+
+namespace policy {
+
+class AsyncPolicyLoader;
+class PolicyBundle;
+class SchemaRegistry;
+
+// A policy provider that loads its policies asynchronously on a background
+// thread. Platform-specific providers are created by passing an implementation
+// of AsyncPolicyLoader to a new AsyncPolicyProvider.
+class POLICY_EXPORT AsyncPolicyProvider : public ConfigurationPolicyProvider {
+ public:
+  // The AsyncPolicyProvider does a synchronous load in its constructor, and
+  // therefore it needs the |registry| at construction time. The same |registry|
+  // should be passed later to Init().
+  AsyncPolicyProvider(SchemaRegistry* registry,
+                      std::unique_ptr<AsyncPolicyLoader> loader);
+  ~AsyncPolicyProvider() override;
+
+  // ConfigurationPolicyProvider implementation.
+  void Init(SchemaRegistry* registry) override;
+  void Shutdown() override;
+  void RefreshPolicies() override;
+
+ private:
+  // Helper for RefreshPolicies().
+  void ReloadAfterRefreshSync();
+
+  // Invoked with the latest bundle loaded by the |loader_|.
+  void OnLoaderReloaded(std::unique_ptr<PolicyBundle> bundle);
+
+  // Callback passed to the loader that it uses to pass back the current policy
+  // bundle to the provider. This is invoked on the background thread and
+  // forwards to OnLoaderReloaded() on the runner that owns the provider,
+  // if |weak_this| is still valid.
+  static void LoaderUpdateCallback(
+      scoped_refptr<base::SingleThreadTaskRunner> runner,
+      base::WeakPtr<AsyncPolicyProvider> weak_this,
+      std::unique_ptr<PolicyBundle> bundle);
+
+  // The |loader_| that does the platform-specific policy loading. It lives
+  // on the background thread but is owned by |this|.
+  std::unique_ptr<AsyncPolicyLoader> loader_;
+
+  // Callback used to synchronize RefreshPolicies() calls with the background
+  // thread. See the implementation for the details.
+  base::CancelableClosure refresh_callback_;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+
+  // Used to get a WeakPtr to |this| for the update callback given to the
+  // loader.
+  base::WeakPtrFactory<AsyncPolicyProvider> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(AsyncPolicyProvider);
+};
+
+}  // namespace policy
+
+#endif  // COMPONENTS_POLICY_CORE_COMMON_ASYNC_POLICY_PROVIDER_H_
diff --git a/components/policy/core/common/async_policy_provider_unittest.cc b/components/policy/core/common/async_policy_provider_unittest.cc
new file mode 100644
index 0000000..61286c6
--- /dev/null
+++ b/components/policy/core/common/async_policy_provider_unittest.cc
@@ -0,0 +1,227 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/policy/core/common/async_policy_provider.h"
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/sequenced_task_runner.h"
+#include "base/values.h"
+#include "components/policy/core/common/async_policy_loader.h"
+#include "components/policy/core/common/external_data_fetcher.h"
+#include "components/policy/core/common/mock_configuration_policy_provider.h"
+#include "components/policy/core/common/policy_types.h"
+#include "components/policy/core/common/schema_registry.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::Mock;
+using testing::Return;
+using testing::Sequence;
+
+namespace policy {
+
+namespace {
+
+// Helper to write a policy in |bundle| with less code.
+void SetPolicy(PolicyBundle* bundle,
+               const std::string& name,
+               const std::string& value) {
+  bundle->Get(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()))
+      .Set(name, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+           POLICY_SOURCE_PLATFORM, std::make_unique<base::Value>(value),
+           nullptr);
+}
+
+class MockPolicyLoader : public AsyncPolicyLoader {
+ public:
+  explicit MockPolicyLoader(
+      scoped_refptr<base::SequencedTaskRunner> task_runner);
+  ~MockPolicyLoader() override;
+
+  // Load() returns a std::unique_ptr<PolicyBundle> but it can't be mocked
+  // because std::unique_ptr is moveable but not copyable. This override
+  // forwards the call to MockLoad() which returns a PolicyBundle*, and returns
+  // a copy wrapped in a std::unique_ptr.
+  std::unique_ptr<PolicyBundle> Load() override;
+
+  MOCK_METHOD0(MockLoad, const PolicyBundle*());
+  MOCK_METHOD0(InitOnBackgroundThread, void());
+  MOCK_METHOD0(LastModificationTime, base::Time());
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockPolicyLoader);
+};
+
+MockPolicyLoader::MockPolicyLoader(
+    scoped_refptr<base::SequencedTaskRunner> task_runner)
+    : AsyncPolicyLoader(task_runner) {}
+
+MockPolicyLoader::~MockPolicyLoader() {}
+
+std::unique_ptr<PolicyBundle> MockPolicyLoader::Load() {
+  std::unique_ptr<PolicyBundle> bundle;
+  const PolicyBundle* loaded = MockLoad();
+  if (loaded) {
+    bundle.reset(new PolicyBundle());
+    bundle->CopyFrom(*loaded);
+  }
+  return bundle;
+}
+
+}  // namespace
+
+class AsyncPolicyProviderTest : public testing::Test {
+ protected:
+  AsyncPolicyProviderTest();
+  ~AsyncPolicyProviderTest() override;
+
+  void SetUp() override;
+  void TearDown() override;
+
+  base::MessageLoop loop_;
+  SchemaRegistry schema_registry_;
+  PolicyBundle initial_bundle_;
+  MockPolicyLoader* loader_;
+  std::unique_ptr<AsyncPolicyProvider> provider_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(AsyncPolicyProviderTest);
+};
+
+AsyncPolicyProviderTest::AsyncPolicyProviderTest() {}
+
+AsyncPolicyProviderTest::~AsyncPolicyProviderTest() {}
+
+void AsyncPolicyProviderTest::SetUp() {
+  SetPolicy(&initial_bundle_, "policy", "initial");
+  loader_ = new MockPolicyLoader(loop_.task_runner());
+  EXPECT_CALL(*loader_, LastModificationTime())
+      .WillRepeatedly(Return(base::Time()));
+  EXPECT_CALL(*loader_, InitOnBackgroundThread()).Times(1);
+  EXPECT_CALL(*loader_, MockLoad()).WillOnce(Return(&initial_bundle_));
+
+  provider_.reset(new AsyncPolicyProvider(
+      &schema_registry_, std::unique_ptr<AsyncPolicyLoader>(loader_)));
+  provider_->Init(&schema_registry_);
+  // Verify that the initial load is done synchronously:
+  EXPECT_TRUE(provider_->policies().Equals(initial_bundle_));
+
+  base::RunLoop().RunUntilIdle();
+  Mock::VerifyAndClearExpectations(loader_);
+
+  EXPECT_CALL(*loader_, LastModificationTime())
+      .WillRepeatedly(Return(base::Time()));
+}
+
+void AsyncPolicyProviderTest::TearDown() {
+  if (provider_) {
+    provider_->Shutdown();
+    provider_.reset();
+  }
+  base::RunLoop().RunUntilIdle();
+}
+
+TEST_F(AsyncPolicyProviderTest, RefreshPolicies) {
+  PolicyBundle refreshed_bundle;
+  SetPolicy(&refreshed_bundle, "policy", "refreshed");
+  EXPECT_CALL(*loader_, MockLoad()).WillOnce(Return(&refreshed_bundle));
+
+  MockConfigurationPolicyObserver observer;
+  provider_->AddObserver(&observer);
+  EXPECT_CALL(observer, OnUpdatePolicy(provider_.get())).Times(1);
+  provider_->RefreshPolicies();
+  base::RunLoop().RunUntilIdle();
+  // The refreshed policies are now provided.
+  EXPECT_TRUE(provider_->policies().Equals(refreshed_bundle));
+  provider_->RemoveObserver(&observer);
+}
+
+TEST_F(AsyncPolicyProviderTest, RefreshPoliciesTwice) {
+  PolicyBundle refreshed_bundle;
+  SetPolicy(&refreshed_bundle, "policy", "refreshed");
+  EXPECT_CALL(*loader_, MockLoad()).WillRepeatedly(Return(&refreshed_bundle));
+
+  MockConfigurationPolicyObserver observer;
+  provider_->AddObserver(&observer);
+  EXPECT_CALL(observer, OnUpdatePolicy(provider_.get())).Times(0);
+  provider_->RefreshPolicies();
+  // Doesn't refresh before going through the background thread.
+  Mock::VerifyAndClearExpectations(&observer);
+
+  // Doesn't refresh if another RefreshPolicies request is made.
+  EXPECT_CALL(observer, OnUpdatePolicy(provider_.get())).Times(0);
+  provider_->RefreshPolicies();
+  Mock::VerifyAndClearExpectations(&observer);
+
+  EXPECT_CALL(observer, OnUpdatePolicy(provider_.get())).Times(1);
+  base::RunLoop().RunUntilIdle();
+  // The refreshed policies are now provided.
+  EXPECT_TRUE(provider_->policies().Equals(refreshed_bundle));
+  Mock::VerifyAndClearExpectations(&observer);
+  provider_->RemoveObserver(&observer);
+}
+
+TEST_F(AsyncPolicyProviderTest, RefreshPoliciesDuringReload) {
+  PolicyBundle reloaded_bundle;
+  SetPolicy(&reloaded_bundle, "policy", "reloaded");
+  PolicyBundle refreshed_bundle;
+  SetPolicy(&refreshed_bundle, "policy", "refreshed");
+
+  Sequence load_sequence;
+  // Reload.
+  EXPECT_CALL(*loader_, MockLoad()).InSequence(load_sequence)
+                                   .WillOnce(Return(&reloaded_bundle));
+  // RefreshPolicies.
+  EXPECT_CALL(*loader_, MockLoad()).InSequence(load_sequence)
+                                   .WillOnce(Return(&refreshed_bundle));
+
+  MockConfigurationPolicyObserver observer;
+  provider_->AddObserver(&observer);
+  EXPECT_CALL(observer, OnUpdatePolicy(provider_.get())).Times(0);
+
+  // A Reload is triggered before RefreshPolicies, and it shouldn't trigger
+  // notifications.
+  loader_->Reload(true);
+  Mock::VerifyAndClearExpectations(&observer);
+
+  // Doesn't refresh before going through the background thread.
+  EXPECT_CALL(observer, OnUpdatePolicy(provider_.get())).Times(0);
+  provider_->RefreshPolicies();
+  Mock::VerifyAndClearExpectations(&observer);
+
+  EXPECT_CALL(observer, OnUpdatePolicy(provider_.get())).Times(1);
+  base::RunLoop().RunUntilIdle();
+  // The refreshed policies are now provided, and the |reloaded_bundle| was
+  // dropped.
+  EXPECT_TRUE(provider_->policies().Equals(refreshed_bundle));
+  Mock::VerifyAndClearExpectations(&observer);
+  provider_->RemoveObserver(&observer);
+}
+
+TEST_F(AsyncPolicyProviderTest, Shutdown) {
+  EXPECT_CALL(*loader_, MockLoad()).WillRepeatedly(Return(&initial_bundle_));
+
+  MockConfigurationPolicyObserver observer;
+  provider_->AddObserver(&observer);
+
+  // Though there is a pending Reload, the provider and the loader can be
+  // deleted at any time.
+  EXPECT_CALL(observer, OnUpdatePolicy(provider_.get())).Times(0);
+  loader_->Reload(true);
+  Mock::VerifyAndClearExpectations(&observer);
+
+  EXPECT_CALL(observer, OnUpdatePolicy(provider_.get())).Times(0);
+  provider_->Shutdown();
+  base::RunLoop().RunUntilIdle();
+  Mock::VerifyAndClearExpectations(&observer);
+
+  provider_->RemoveObserver(&observer);
+  provider_.reset();
+}
+
+}  // namespace policy
diff --git a/components/policy/core/common/config_dir_policy_loader.cc b/components/policy/core/common/config_dir_policy_loader.cc
new file mode 100644
index 0000000..8edf586
--- /dev/null
+++ b/components/policy/core/common/config_dir_policy_loader.cc
@@ -0,0 +1,240 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/policy/core/common/config_dir_policy_loader.h"
+
+#include <stddef.h>
+
+#include <algorithm>
+#include <set>
+#include <string>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/files/file_enumerator.h"
+#include "base/files/file_util.h"
+#include "base/json/json_file_value_serializer.h"
+#include "base/json/json_reader.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/stl_util.h"
+#include "components/policy/core/common/policy_bundle.h"
+#include "components/policy/core/common/policy_load_status.h"
+#include "components/policy/core/common/policy_types.h"
+
+namespace policy {
+
+namespace {
+
+// Subdirectories that contain the mandatory and recommended policies.
+constexpr base::FilePath::CharType kMandatoryConfigDir[] =
+    FILE_PATH_LITERAL("managed");
+constexpr base::FilePath::CharType kRecommendedConfigDir[] =
+    FILE_PATH_LITERAL("recommended");
+
+PolicyLoadStatus JsonErrorToPolicyLoadStatus(int status) {
+  switch (status) {
+    case JSONFileValueDeserializer::JSON_ACCESS_DENIED:
+    case JSONFileValueDeserializer::JSON_CANNOT_READ_FILE:
+    case JSONFileValueDeserializer::JSON_FILE_LOCKED:
+      return POLICY_LOAD_STATUS_READ_ERROR;
+    case JSONFileValueDeserializer::JSON_NO_SUCH_FILE:
+      return POLICY_LOAD_STATUS_MISSING;
+    case base::JSONReader::JSON_INVALID_ESCAPE:
+    case base::JSONReader::JSON_SYNTAX_ERROR:
+    case base::JSONReader::JSON_UNEXPECTED_TOKEN:
+    case base::JSONReader::JSON_TRAILING_COMMA:
+    case base::JSONReader::JSON_TOO_MUCH_NESTING:
+    case base::JSONReader::JSON_UNEXPECTED_DATA_AFTER_ROOT:
+    case base::JSONReader::JSON_UNSUPPORTED_ENCODING:
+    case base::JSONReader::JSON_UNQUOTED_DICTIONARY_KEY:
+      return POLICY_LOAD_STATUS_PARSE_ERROR;
+    case base::JSONReader::JSON_NO_ERROR:
+      NOTREACHED();
+      return POLICY_LOAD_STATUS_STARTED;
+  }
+  NOTREACHED() << "Invalid status " << status;
+  return POLICY_LOAD_STATUS_PARSE_ERROR;
+}
+
+}  // namespace
+
+ConfigDirPolicyLoader::ConfigDirPolicyLoader(
+    scoped_refptr<base::SequencedTaskRunner> task_runner,
+    const base::FilePath& config_dir,
+    PolicyScope scope)
+    : AsyncPolicyLoader(task_runner),
+      task_runner_(task_runner),
+      config_dir_(config_dir),
+      scope_(scope) {}
+
+ConfigDirPolicyLoader::~ConfigDirPolicyLoader() {}
+
+void ConfigDirPolicyLoader::InitOnBackgroundThread() {
+  DCHECK(task_runner_->RunsTasksInCurrentSequence());
+  base::FilePathWatcher::Callback callback =
+      base::Bind(&ConfigDirPolicyLoader::OnFileUpdated, base::Unretained(this));
+  mandatory_watcher_.Watch(config_dir_.Append(kMandatoryConfigDir), false,
+                           callback);
+  recommended_watcher_.Watch(config_dir_.Append(kRecommendedConfigDir), false,
+                             callback);
+}
+
+std::unique_ptr<PolicyBundle> ConfigDirPolicyLoader::Load() {
+  std::unique_ptr<PolicyBundle> bundle(new PolicyBundle());
+  LoadFromPath(config_dir_.Append(kMandatoryConfigDir),
+               POLICY_LEVEL_MANDATORY,
+               bundle.get());
+  LoadFromPath(config_dir_.Append(kRecommendedConfigDir),
+               POLICY_LEVEL_RECOMMENDED,
+               bundle.get());
+  return bundle;
+}
+
+base::Time ConfigDirPolicyLoader::LastModificationTime() {
+  static constexpr const base::FilePath::CharType* kConfigDirSuffixes[] = {
+      kMandatoryConfigDir, kRecommendedConfigDir,
+  };
+
+  base::Time last_modification = base::Time();
+  base::File::Info info;
+
+  for (size_t i = 0; i < arraysize(kConfigDirSuffixes); ++i) {
+    base::FilePath path(config_dir_.Append(kConfigDirSuffixes[i]));
+
+    // Skip if the file doesn't exist, or it isn't a directory.
+    if (!base::GetFileInfo(path, &info) || !info.is_directory)
+      continue;
+
+    // Enumerate the files and find the most recent modification timestamp.
+    base::FileEnumerator file_enumerator(path, false,
+                                         base::FileEnumerator::FILES);
+    for (base::FilePath config_file = file_enumerator.Next();
+         !config_file.empty();
+         config_file = file_enumerator.Next()) {
+      if (base::GetFileInfo(config_file, &info) && !info.is_directory)
+        last_modification = std::max(last_modification, info.last_modified);
+    }
+  }
+
+  return last_modification;
+}
+
+void ConfigDirPolicyLoader::LoadFromPath(const base::FilePath& path,
+                                         PolicyLevel level,
+                                         PolicyBundle* bundle) {
+  // Enumerate the files and sort them lexicographically.
+  std::set<base::FilePath> files;
+  base::FileEnumerator file_enumerator(path, false,
+                                       base::FileEnumerator::FILES);
+  for (base::FilePath config_file_path = file_enumerator.Next();
+       !config_file_path.empty(); config_file_path = file_enumerator.Next())
+    files.insert(config_file_path);
+
+  PolicyLoadStatusUmaReporter status;
+  if (files.empty()) {
+    status.Add(POLICY_LOAD_STATUS_NO_POLICY);
+    return;
+  }
+
+  // Start with an empty dictionary and merge the files' contents.
+  // The files are processed in reverse order because |MergeFrom| gives priority
+  // to existing keys, but the ConfigDirPolicyProvider gives priority to the
+  // last file in lexicographic order.
+  for (std::set<base::FilePath>::reverse_iterator config_file_iter =
+           files.rbegin(); config_file_iter != files.rend();
+       ++config_file_iter) {
+    JSONFileValueDeserializer deserializer(*config_file_iter,
+                                           base::JSON_ALLOW_TRAILING_COMMAS);
+    int error_code = 0;
+    std::string error_msg;
+    std::unique_ptr<base::Value> value =
+        deserializer.Deserialize(&error_code, &error_msg);
+    if (!value) {
+      LOG(WARNING) << "Failed to read configuration file "
+                   << config_file_iter->value() << ": " << error_msg;
+      status.Add(JsonErrorToPolicyLoadStatus(error_code));
+      continue;
+    }
+    base::DictionaryValue* dictionary_value = nullptr;
+    if (!value->GetAsDictionary(&dictionary_value)) {
+      LOG(WARNING) << "Expected JSON dictionary in configuration file "
+                   << config_file_iter->value();
+      status.Add(POLICY_LOAD_STATUS_PARSE_ERROR);
+      continue;
+    }
+
+    // Detach the "3rdparty" node.
+    std::unique_ptr<base::Value> third_party;
+    if (dictionary_value->Remove("3rdparty", &third_party))
+      Merge3rdPartyPolicy(third_party.get(), level, bundle);
+
+    // Add chrome policy.
+    PolicyMap policy_map;
+    policy_map.LoadFrom(dictionary_value, level, scope_,
+                        POLICY_SOURCE_PLATFORM);
+    bundle->Get(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()))
+        .MergeFrom(policy_map);
+  }
+}
+
+void ConfigDirPolicyLoader::Merge3rdPartyPolicy(
+    const base::Value* policies,
+    PolicyLevel level,
+    PolicyBundle* bundle) {
+  // The first-level entries in |policies| are PolicyDomains. The second-level
+  // entries are component IDs, and the third-level entries are the policies
+  // for that domain/component namespace.
+
+  const base::DictionaryValue* domains_dictionary;
+  if (!policies->GetAsDictionary(&domains_dictionary)) {
+    LOG(WARNING) << "3rdparty value is not a dictionary!";
+    return;
+  }
+
+  // Helper to lookup a domain given its string name.
+  std::map<std::string, PolicyDomain> supported_domains;
+  supported_domains["extensions"] = POLICY_DOMAIN_EXTENSIONS;
+
+  for (base::DictionaryValue::Iterator domains_it(*domains_dictionary);
+       !domains_it.IsAtEnd(); domains_it.Advance()) {
+    if (!base::ContainsKey(supported_domains, domains_it.key())) {
+      LOG(WARNING) << "Unsupported 3rd party policy domain: "
+                   << domains_it.key();
+      continue;
+    }
+
+    const base::DictionaryValue* components_dictionary;
+    if (!domains_it.value().GetAsDictionary(&components_dictionary)) {
+      LOG(WARNING) << "3rdparty/" << domains_it.key()
+                   << " value is not a dictionary!";
+      continue;
+    }
+
+    PolicyDomain domain = supported_domains[domains_it.key()];
+    for (base::DictionaryValue::Iterator components_it(*components_dictionary);
+         !components_it.IsAtEnd(); components_it.Advance()) {
+      const base::DictionaryValue* policy_dictionary;
+      if (!components_it.value().GetAsDictionary(&policy_dictionary)) {
+        LOG(WARNING) << "3rdparty/" << domains_it.key() << "/"
+                     << components_it.key() << " value is not a dictionary!";
+        continue;
+      }
+
+      PolicyMap policy;
+      policy.LoadFrom(policy_dictionary, level, scope_, POLICY_SOURCE_PLATFORM);
+      bundle->Get(PolicyNamespace(domain, components_it.key()))
+          .MergeFrom(policy);
+    }
+  }
+}
+
+void ConfigDirPolicyLoader::OnFileUpdated(const base::FilePath& path,
+                                          bool error) {
+  DCHECK(task_runner_->RunsTasksInCurrentSequence());
+  if (!error)
+    Reload(false);
+}
+
+}  // namespace policy
diff --git a/components/policy/core/common/config_dir_policy_loader.h b/components/policy/core/common/config_dir_policy_loader.h
new file mode 100644
index 0000000..8956353
--- /dev/null
+++ b/components/policy/core/common/config_dir_policy_loader.h
@@ -0,0 +1,72 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_POLICY_CORE_COMMON_CONFIG_DIR_POLICY_LOADER_H_
+#define COMPONENTS_POLICY_CORE_COMMON_CONFIG_DIR_POLICY_LOADER_H_
+
+#include "base/files/file_path.h"
+#include "base/files/file_path_watcher.h"
+#include "base/macros.h"
+#include "base/sequenced_task_runner.h"
+#include "components/policy/core/common/async_policy_loader.h"
+#include "components/policy/core/common/policy_types.h"
+#include "components/policy/policy_export.h"
+
+namespace base {
+class Value;
+}
+
+namespace policy {
+
+// A policy loader implementation backed by a set of files in a given
+// directory. The files should contain JSON-formatted policy settings. They are
+// merged together and the result is returned in a PolicyBundle.
+// The files are consulted in lexicographic file name order, so the
+// last value read takes precedence in case of policy key collisions.
+class POLICY_EXPORT ConfigDirPolicyLoader : public AsyncPolicyLoader {
+ public:
+  ConfigDirPolicyLoader(scoped_refptr<base::SequencedTaskRunner> task_runner,
+                        const base::FilePath& config_dir,
+                        PolicyScope scope);
+  ~ConfigDirPolicyLoader() override;
+
+  // AsyncPolicyLoader implementation.
+  void InitOnBackgroundThread() override;
+  std::unique_ptr<PolicyBundle> Load() override;
+  base::Time LastModificationTime() override;
+
+ private:
+  // Loads the policy files at |path| into the |bundle|, with the given |level|.
+  void LoadFromPath(const base::FilePath& path,
+                    PolicyLevel level,
+                    PolicyBundle* bundle);
+
+  // Merges the 3rd party |policies| into the |bundle|, with the given |level|.
+  void Merge3rdPartyPolicy(const base::Value* policies,
+                           PolicyLevel level,
+                           PolicyBundle* bundle);
+
+  // Callback for the FilePathWatchers.
+  void OnFileUpdated(const base::FilePath& path, bool error);
+
+  // Task runner for running background jobs.
+  const scoped_refptr<base::SequencedTaskRunner> task_runner_;
+
+  // The directory containing the policy files.
+  const base::FilePath config_dir_;
+
+  // Policies loaded by this provider will have this scope.
+  const PolicyScope scope_;
+
+  // Watchers for events on the mandatory and recommended subdirectories of
+  // |config_dir_|.
+  base::FilePathWatcher mandatory_watcher_;
+  base::FilePathWatcher recommended_watcher_;
+
+  DISALLOW_COPY_AND_ASSIGN(ConfigDirPolicyLoader);
+};
+
+}  // namespace policy
+
+#endif  // COMPONENTS_POLICY_CORE_COMMON_CONFIG_DIR_POLICY_LOADER_H_
diff --git a/components/policy/core/common/config_dir_policy_loader_unittest.cc b/components/policy/core/common/config_dir_policy_loader_unittest.cc
new file mode 100644
index 0000000..b17add7
--- /dev/null
+++ b/components/policy/core/common/config_dir_policy_loader_unittest.cc
@@ -0,0 +1,244 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/policy/core/common/config_dir_policy_loader.h"
+#include <memory>
+
+#include <utility>
+
+#include "base/compiler_specific.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/json/json_string_value_serializer.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/sequenced_task_runner.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/values.h"
+#include "components/policy/core/common/async_policy_provider.h"
+#include "components/policy/core/common/configuration_policy_provider_test.h"
+#include "components/policy/core/common/policy_bundle.h"
+#include "components/policy/core/common/policy_map.h"
+#include "components/policy/core/common/policy_types.h"
+
+namespace policy {
+
+namespace {
+
+// Subdirectory of the config dir that contains mandatory policies.
+const base::FilePath::CharType kMandatoryPath[] = FILE_PATH_LITERAL("managed");
+
+class TestHarness : public PolicyProviderTestHarness {
+ public:
+  TestHarness();
+  ~TestHarness() override;
+
+  void SetUp() override;
+
+  ConfigurationPolicyProvider* CreateProvider(
+      SchemaRegistry* registry,
+      scoped_refptr<base::SequencedTaskRunner> task_runner) override;
+
+  void InstallEmptyPolicy() override;
+  void InstallStringPolicy(const std::string& policy_name,
+                           const std::string& policy_value) override;
+  void InstallIntegerPolicy(const std::string& policy_name,
+                            int policy_value) override;
+  void InstallBooleanPolicy(const std::string& policy_name,
+                            bool policy_value) override;
+  void InstallStringListPolicy(const std::string& policy_name,
+                               const base::ListValue* policy_value) override;
+  void InstallDictionaryPolicy(
+      const std::string& policy_name,
+      const base::DictionaryValue* policy_value) override;
+  void Install3rdPartyPolicy(const base::DictionaryValue* policies) override;
+
+  const base::FilePath& test_dir() { return test_dir_.GetPath(); }
+
+  // JSON-encode a dictionary and write it to a file.
+  void WriteConfigFile(const base::DictionaryValue& dict,
+                       const std::string& file_name);
+
+  // Returns a unique name for a policy file. Each subsequent call returns a new
+  // name that comes lexicographically after the previous one.
+  std::string NextConfigFileName();
+
+  static PolicyProviderTestHarness* Create();
+
+ private:
+  base::ScopedTempDir test_dir_;
+  int next_policy_file_index_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestHarness);
+};
+
+TestHarness::TestHarness()
+    : PolicyProviderTestHarness(POLICY_LEVEL_MANDATORY,
+                                POLICY_SCOPE_MACHINE,
+                                POLICY_SOURCE_PLATFORM),
+      next_policy_file_index_(100) {}
+
+TestHarness::~TestHarness() {}
+
+void TestHarness::SetUp() {
+  ASSERT_TRUE(test_dir_.CreateUniqueTempDir());
+}
+
+ConfigurationPolicyProvider* TestHarness::CreateProvider(
+    SchemaRegistry* registry,
+    scoped_refptr<base::SequencedTaskRunner> task_runner) {
+  std::unique_ptr<AsyncPolicyLoader> loader(
+      new ConfigDirPolicyLoader(task_runner, test_dir(), POLICY_SCOPE_MACHINE));
+  return new AsyncPolicyProvider(registry, std::move(loader));
+}
+
+void TestHarness::InstallEmptyPolicy() {
+  base::DictionaryValue dict;
+  WriteConfigFile(dict, NextConfigFileName());
+}
+
+void TestHarness::InstallStringPolicy(const std::string& policy_name,
+                                      const std::string& policy_value) {
+  base::DictionaryValue dict;
+  dict.SetString(policy_name, policy_value);
+  WriteConfigFile(dict, NextConfigFileName());
+}
+
+void TestHarness::InstallIntegerPolicy(const std::string& policy_name,
+                                       int policy_value) {
+  base::DictionaryValue dict;
+  dict.SetInteger(policy_name, policy_value);
+  WriteConfigFile(dict, NextConfigFileName());
+}
+
+void TestHarness::InstallBooleanPolicy(const std::string& policy_name,
+                                       bool policy_value) {
+  base::DictionaryValue dict;
+  dict.SetBoolean(policy_name, policy_value);
+  WriteConfigFile(dict, NextConfigFileName());
+}
+
+void TestHarness::InstallStringListPolicy(const std::string& policy_name,
+                                          const base::ListValue* policy_value) {
+  base::DictionaryValue dict;
+  dict.Set(policy_name, std::make_unique<base::Value>(policy_value->Clone()));
+  WriteConfigFile(dict, NextConfigFileName());
+}
+
+void TestHarness::InstallDictionaryPolicy(
+    const std::string& policy_name,
+    const base::DictionaryValue* policy_value) {
+  base::DictionaryValue dict;
+  dict.Set(policy_name, std::make_unique<base::Value>(policy_value->Clone()));
+  WriteConfigFile(dict, NextConfigFileName());
+}
+
+void TestHarness::Install3rdPartyPolicy(const base::DictionaryValue* policies) {
+  base::DictionaryValue dict;
+  dict.SetKey("3rdparty", policies->Clone());
+  WriteConfigFile(dict, NextConfigFileName());
+}
+
+void TestHarness::WriteConfigFile(const base::DictionaryValue& dict,
+                                  const std::string& file_name) {
+  std::string data;
+  JSONStringValueSerializer serializer(&data);
+  serializer.Serialize(dict);
+  const base::FilePath mandatory_dir(test_dir().Append(kMandatoryPath));
+  ASSERT_TRUE(base::CreateDirectory(mandatory_dir));
+  const base::FilePath file_path(mandatory_dir.AppendASCII(file_name));
+  ASSERT_EQ((int) data.size(),
+            base::WriteFile(file_path, data.c_str(), data.size()));
+}
+
+std::string TestHarness::NextConfigFileName() {
+  EXPECT_LE(next_policy_file_index_, 999);
+  return std::string("policy") + base::IntToString(next_policy_file_index_++);
+}
+
+// static
+PolicyProviderTestHarness* TestHarness::Create() {
+  return new TestHarness();
+}
+
+}  // namespace
+
+// Instantiate abstract test case for basic policy reading tests.
+INSTANTIATE_TEST_CASE_P(
+    ConfigDirPolicyLoaderTest,
+    ConfigurationPolicyProviderTest,
+    testing::Values(TestHarness::Create));
+
+// Instantiate abstract test case for 3rd party policy reading tests.
+INSTANTIATE_TEST_CASE_P(
+    ConfigDir3rdPartyPolicyLoaderTest,
+    Configuration3rdPartyPolicyProviderTest,
+    testing::Values(TestHarness::Create));
+
+// Some tests that exercise special functionality in ConfigDirPolicyLoader.
+class ConfigDirPolicyLoaderTest : public PolicyTestBase {
+ protected:
+  void SetUp() override {
+    PolicyTestBase::SetUp();
+    harness_.SetUp();
+  }
+
+  TestHarness harness_;
+};
+
+// The preferences dictionary is expected to be empty when there are no files to
+// load.
+TEST_F(ConfigDirPolicyLoaderTest, ReadPrefsEmpty) {
+  ConfigDirPolicyLoader loader(
+      scoped_task_environment_.GetMainThreadTaskRunner(), harness_.test_dir(),
+      POLICY_SCOPE_MACHINE);
+  std::unique_ptr<PolicyBundle> bundle(loader.Load());
+  ASSERT_TRUE(bundle.get());
+  const PolicyBundle kEmptyBundle;
+  EXPECT_TRUE(bundle->Equals(kEmptyBundle));
+}
+
+// Reading from a non-existent directory should result in an empty preferences
+// dictionary.
+TEST_F(ConfigDirPolicyLoaderTest, ReadPrefsNonExistentDirectory) {
+  base::FilePath non_existent_dir(
+      harness_.test_dir().Append(FILE_PATH_LITERAL("not_there")));
+  ConfigDirPolicyLoader loader(
+      scoped_task_environment_.GetMainThreadTaskRunner(), non_existent_dir,
+      POLICY_SCOPE_MACHINE);
+  std::unique_ptr<PolicyBundle> bundle(loader.Load());
+  ASSERT_TRUE(bundle.get());
+  const PolicyBundle kEmptyBundle;
+  EXPECT_TRUE(bundle->Equals(kEmptyBundle));
+}
+
+// Test merging values from different files.
+TEST_F(ConfigDirPolicyLoaderTest, ReadPrefsMergePrefs) {
+  // Write a bunch of data files in order to increase the chance to detect the
+  // provider not respecting lexicographic ordering when reading them. Since the
+  // filesystem may return files in arbitrary order, there is no way to be sure,
+  // but this is better than nothing.
+  base::DictionaryValue test_dict_bar;
+  test_dict_bar.SetString("HomepageLocation", "http://bar.com");
+  for (unsigned int i = 1; i <= 4; ++i)
+    harness_.WriteConfigFile(test_dict_bar, base::UintToString(i));
+  base::DictionaryValue test_dict_foo;
+  test_dict_foo.SetString("HomepageLocation", "http://foo.com");
+  harness_.WriteConfigFile(test_dict_foo, "9");
+  for (unsigned int i = 5; i <= 8; ++i)
+    harness_.WriteConfigFile(test_dict_bar, base::UintToString(i));
+
+  ConfigDirPolicyLoader loader(
+      scoped_task_environment_.GetMainThreadTaskRunner(), harness_.test_dir(),
+      POLICY_SCOPE_USER);
+  std::unique_ptr<PolicyBundle> bundle(loader.Load());
+  ASSERT_TRUE(bundle.get());
+  PolicyBundle expected_bundle;
+  expected_bundle.Get(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()))
+      .LoadFrom(&test_dict_foo, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+                POLICY_SOURCE_PLATFORM);
+  EXPECT_TRUE(bundle->Equals(expected_bundle));
+}
+
+}  // namespace policy
diff --git a/components/policy/core/common/configuration_policy_provider.cc b/components/policy/core/common/configuration_policy_provider.cc
new file mode 100644
index 0000000..a886bd4
--- /dev/null
+++ b/components/policy/core/common/configuration_policy_provider.cc
@@ -0,0 +1,75 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/policy/core/common/configuration_policy_provider.h"
+
+#include "base/callback.h"
+#include "components/policy/core/common/external_data_fetcher.h"
+#include "components/policy/core/common/policy_map.h"
+
+namespace policy {
+
+ConfigurationPolicyProvider::Observer::~Observer() {}
+
+ConfigurationPolicyProvider::ConfigurationPolicyProvider()
+    : initialized_(false), schema_registry_(nullptr) {}
+
+ConfigurationPolicyProvider::~ConfigurationPolicyProvider() {
+  DCHECK(!initialized_);
+}
+
+void ConfigurationPolicyProvider::Init(SchemaRegistry* registry) {
+  schema_registry_ = registry;
+  schema_registry_->AddObserver(this);
+  initialized_ = true;
+}
+
+void ConfigurationPolicyProvider::Shutdown() {
+  initialized_ = false;
+  if (schema_registry_) {
+    // Unit tests don't initialize the BrowserPolicyConnector but call
+    // shutdown; handle that.
+    schema_registry_->RemoveObserver(this);
+    schema_registry_ = nullptr;
+  }
+}
+
+bool ConfigurationPolicyProvider::IsInitializationComplete(
+    PolicyDomain domain) const {
+  return true;
+}
+
+void ConfigurationPolicyProvider::UpdatePolicy(
+    std::unique_ptr<PolicyBundle> bundle) {
+  if (bundle)
+    policy_bundle_.Swap(bundle.get());
+  else
+    policy_bundle_.Clear();
+  for (auto& observer : observer_list_)
+    observer.OnUpdatePolicy(this);
+}
+
+SchemaRegistry* ConfigurationPolicyProvider::schema_registry() const {
+  return schema_registry_;
+}
+
+const scoped_refptr<SchemaMap>&
+ConfigurationPolicyProvider::schema_map() const {
+  return schema_registry_->schema_map();
+}
+
+void ConfigurationPolicyProvider::AddObserver(Observer* observer) {
+  observer_list_.AddObserver(observer);
+}
+
+void ConfigurationPolicyProvider::RemoveObserver(Observer* observer) {
+  observer_list_.RemoveObserver(observer);
+}
+
+void ConfigurationPolicyProvider::OnSchemaRegistryUpdated(
+    bool has_new_schemas) {}
+
+void ConfigurationPolicyProvider::OnSchemaRegistryReady() {}
+
+}  // namespace policy
diff --git a/components/policy/core/common/configuration_policy_provider.h b/components/policy/core/common/configuration_policy_provider.h
new file mode 100644
index 0000000..a813c0c
--- /dev/null
+++ b/components/policy/core/common/configuration_policy_provider.h
@@ -0,0 +1,105 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_POLICY_CORE_COMMON_CONFIGURATION_POLICY_PROVIDER_H_
+#define COMPONENTS_POLICY_CORE_COMMON_CONFIGURATION_POLICY_PROVIDER_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/observer_list.h"
+#include "components/policy/core/common/policy_bundle.h"
+#include "components/policy/core/common/policy_namespace.h"
+#include "components/policy/core/common/schema_registry.h"
+#include "components/policy/policy_export.h"
+
+namespace policy {
+
+// A mostly-abstract super class for platform-specific policy providers.
+// Platform-specific policy providers (Windows Group Policy, gconf,
+// etc.) should implement a subclass of this class.
+class POLICY_EXPORT ConfigurationPolicyProvider
+    : public SchemaRegistry::Observer {
+ public:
+  class POLICY_EXPORT Observer {
+   public:
+    virtual ~Observer();
+    virtual void OnUpdatePolicy(ConfigurationPolicyProvider* provider) = 0;
+  };
+
+  ConfigurationPolicyProvider();
+
+  // Policy providers can be deleted quite late during shutdown of the browser,
+  // and it's not guaranteed that the message loops will still be running when
+  // this is invoked. Override Shutdown() instead for cleanup code that needs
+  // to post to the FILE thread, for example.
+  ~ConfigurationPolicyProvider() override;
+
+  // Invoked as soon as the main message loops are spinning. Policy providers
+  // are created early during startup to provide the initial policies; the
+  // Init() call allows them to perform initialization tasks that require
+  // running message loops.
+  // The policy provider will load policy for the components registered in
+  // the |schema_registry| whose domain is supported by this provider.
+  virtual void Init(SchemaRegistry* registry);
+
+  // Must be invoked before deleting the provider. Implementations can override
+  // this method to do appropriate cleanup while threads are still running, and
+  // must also invoke ConfigurationPolicyProvider::Shutdown().
+  // The provider should keep providing the current policies after Shutdown()
+  // is invoked, it only has to stop updating.
+  virtual void Shutdown();
+
+  // Returns the current PolicyBundle.
+  const PolicyBundle& policies() const { return policy_bundle_; }
+
+  // Check whether this provider has completed initialization for the given
+  // policy |domain|. This is used to detect whether initialization is done in
+  // case implementations need to do asynchronous operations for initialization.
+  virtual bool IsInitializationComplete(PolicyDomain domain) const;
+
+  // Asks the provider to refresh its policies. All the updates caused by this
+  // call will be visible on the next call of OnUpdatePolicy on the observers,
+  // which are guaranteed to happen even if the refresh fails.
+  // It is possible that Shutdown() is called first though, and
+  // OnUpdatePolicy won't be called if that happens.
+  virtual void RefreshPolicies() = 0;
+
+  // Observers must detach themselves before the provider is deleted.
+  virtual void AddObserver(Observer* observer);
+  virtual void RemoveObserver(Observer* observer);
+
+  // SchemaRegistry::Observer:
+  void OnSchemaRegistryUpdated(bool has_new_schemas) override;
+  void OnSchemaRegistryReady() override;
+
+ protected:
+  // Subclasses must invoke this to update the policies currently served by
+  // this provider. UpdatePolicy() takes ownership of |policies|.
+  // The observers are notified after the policies are updated.
+  void UpdatePolicy(std::unique_ptr<PolicyBundle> bundle);
+
+  SchemaRegistry* schema_registry() const;
+
+  const scoped_refptr<SchemaMap>& schema_map() const;
+
+ private:
+  // The policies currently configured at this provider.
+  PolicyBundle policy_bundle_;
+
+  // Used to validate proper Init() and Shutdown() nesting. This flag is set by
+  // Init() and cleared by Shutdown() and needs to be false in the destructor.
+  bool initialized_;
+
+  SchemaRegistry* schema_registry_;
+
+  base::ObserverList<Observer, true> observer_list_;
+
+  DISALLOW_COPY_AND_ASSIGN(ConfigurationPolicyProvider);
+};
+
+}  // namespace policy
+
+#endif  // COMPONENTS_POLICY_CORE_COMMON_CONFIGURATION_POLICY_PROVIDER_H_
diff --git a/components/policy/core/common/configuration_policy_provider_test.cc b/components/policy/core/common/configuration_policy_provider_test.cc
new file mode 100644
index 0000000..4f9beea
--- /dev/null
+++ b/components/policy/core/common/configuration_policy_provider_test.cc
@@ -0,0 +1,420 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/policy/core/common/configuration_policy_provider_test.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/task_scheduler/post_task.h"
+#include "base/task_scheduler/task_traits.h"
+#include "base/values.h"
+#include "components/policy/core/common/configuration_policy_provider.h"
+#include "components/policy/core/common/external_data_fetcher.h"
+#include "components/policy/core/common/mock_configuration_policy_provider.h"
+#include "components/policy/core/common/policy_bundle.h"
+#include "components/policy/core/common/policy_map.h"
+#include "components/policy/core/common/policy_namespace.h"
+#include "components/policy/core/common/policy_types.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+using ::testing::Mock;
+using ::testing::_;
+
+namespace policy {
+
+const char kTestChromeSchema[] =
+    "{"
+    "  \"type\": \"object\","
+    "  \"properties\": {"
+    "    \"StringPolicy\": { \"type\": \"string\" },"
+    "    \"BooleanPolicy\": { \"type\": \"boolean\" },"
+    "    \"IntegerPolicy\": { \"type\": \"integer\" },"
+    "    \"StringListPolicy\": {"
+    "      \"type\": \"array\","
+    "      \"items\": { \"type\": \"string\" }"
+    "    },"
+    "    \"DictionaryPolicy\": {"
+    "      \"type\": \"object\","
+    "      \"properties\": {"
+    "        \"bool\": { \"type\": \"boolean\" },"
+    "        \"double\": { \"type\": \"number\" },"
+    "        \"int\": { \"type\": \"integer\" },"
+    "        \"string\": { \"type\": \"string\" },"
+    "        \"array\": {"
+    "          \"type\": \"array\","
+    "          \"items\": { \"type\": \"string\" }"
+    "        },"
+    "        \"dictionary\": {"
+    "          \"type\": \"object\","
+    "          \"properties\": {"
+    "            \"sub\": { \"type\": \"string\" },"
+    "            \"sublist\": {"
+    "              \"type\": \"array\","
+    "              \"items\": {"
+    "                \"type\": \"object\","
+    "                \"properties\": {"
+    "                  \"aaa\": { \"type\": \"integer\" },"
+    "                  \"bbb\": { \"type\": \"integer\" },"
+    "                  \"ccc\": { \"type\": \"string\" },"
+    "                  \"ddd\": { \"type\": \"string\" }"
+    "                }"
+    "              }"
+    "            }"
+    "          }"
+    "        },"
+    "        \"list\": {"
+    "          \"type\": \"array\","
+    "          \"items\": {"
+    "            \"type\": \"object\","
+    "            \"properties\": {"
+    "              \"subdictindex\": { \"type\": \"integer\" },"
+    "              \"subdict\": {"
+    "                \"type\": \"object\","
+    "                \"properties\": {"
+    "                  \"bool\": { \"type\": \"boolean\" },"
+    "                  \"double\": { \"type\": \"number\" },"
+    "                  \"int\": { \"type\": \"integer\" },"
+    "                  \"string\": { \"type\": \"string\" }"
+    "                }"
+    "              }"
+    "            }"
+    "          }"
+    "        },"
+    "        \"dict\": {"
+    "          \"type\": \"object\","
+    "          \"properties\": {"
+    "            \"bool\": { \"type\": \"boolean\" },"
+    "            \"double\": { \"type\": \"number\" },"
+    "            \"int\": { \"type\": \"integer\" },"
+    "            \"string\": { \"type\": \"string\" },"
+    "            \"list\": {"
+    "              \"type\": \"array\","
+    "              \"items\": {"
+    "                \"type\": \"object\","
+    "                \"properties\": {"
+    "                  \"subdictindex\": { \"type\": \"integer\" },"
+    "                  \"subdict\": {"
+    "                    \"type\": \"object\","
+    "                    \"properties\": {"
+    "                      \"bool\": { \"type\": \"boolean\" },"
+    "                      \"double\": { \"type\": \"number\" },"
+    "                      \"int\": { \"type\": \"integer\" },"
+    "                      \"string\": { \"type\": \"string\" }"
+    "                    }"
+    "                  }"
+    "                }"
+    "              }"
+    "            }"
+    "          }"
+    "        }"
+    "      }"
+    "    }"
+    "  }"
+    "}";
+
+namespace test_keys {
+
+const char kKeyString[] = "StringPolicy";
+const char kKeyBoolean[] = "BooleanPolicy";
+const char kKeyInteger[] = "IntegerPolicy";
+const char kKeyStringList[] = "StringListPolicy";
+const char kKeyDictionary[] = "DictionaryPolicy";
+
+}  // namespace test_keys
+
+PolicyTestBase::PolicyTestBase() {}
+
+PolicyTestBase::~PolicyTestBase() {}
+
+void PolicyTestBase::SetUp() {
+  const PolicyNamespace ns(POLICY_DOMAIN_CHROME, "");
+  ASSERT_TRUE(RegisterSchema(ns, kTestChromeSchema));
+}
+
+void PolicyTestBase::TearDown() {
+  scoped_task_environment_.RunUntilIdle();
+}
+
+bool PolicyTestBase::RegisterSchema(const PolicyNamespace& ns,
+                                    const std::string& schema_string) {
+  std::string error;
+  Schema schema = Schema::Parse(schema_string, &error);
+  if (schema.valid()) {
+    schema_registry_.RegisterComponent(ns, schema);
+    return true;
+  }
+  ADD_FAILURE() << error;
+  return false;
+}
+
+PolicyProviderTestHarness::PolicyProviderTestHarness(PolicyLevel level,
+                                                     PolicyScope scope,
+                                                     PolicySource source)
+    : level_(level), scope_(scope), source_(source) {}
+
+PolicyProviderTestHarness::~PolicyProviderTestHarness() {}
+
+PolicyLevel PolicyProviderTestHarness::policy_level() const {
+  return level_;
+}
+
+PolicyScope PolicyProviderTestHarness::policy_scope() const {
+  return scope_;
+}
+
+PolicySource PolicyProviderTestHarness::policy_source() const {
+  return source_;
+}
+
+void PolicyProviderTestHarness::Install3rdPartyPolicy(
+    const base::DictionaryValue* policies) {
+  FAIL();
+}
+
+ConfigurationPolicyProviderTest::ConfigurationPolicyProviderTest() {}
+
+ConfigurationPolicyProviderTest::~ConfigurationPolicyProviderTest() {}
+
+void ConfigurationPolicyProviderTest::SetUp() {
+  PolicyTestBase::SetUp();
+
+  test_harness_.reset((*GetParam())());
+  ASSERT_NO_FATAL_FAILURE(test_harness_->SetUp());
+
+  const PolicyNamespace chrome_ns(POLICY_DOMAIN_CHROME, "");
+  Schema chrome_schema = *schema_registry_.schema_map()->GetSchema(chrome_ns);
+  Schema extension_schema =
+      chrome_schema.GetKnownProperty(test_keys::kKeyDictionary);
+  ASSERT_TRUE(extension_schema.valid());
+  schema_registry_.RegisterComponent(
+      PolicyNamespace(POLICY_DOMAIN_EXTENSIONS,
+                      "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
+      extension_schema);
+  schema_registry_.RegisterComponent(
+      PolicyNamespace(POLICY_DOMAIN_EXTENSIONS,
+                      "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"),
+      extension_schema);
+  schema_registry_.RegisterComponent(
+      PolicyNamespace(POLICY_DOMAIN_EXTENSIONS,
+                      "cccccccccccccccccccccccccccccccc"),
+      extension_schema);
+
+  provider_.reset(test_harness_->CreateProvider(
+      &schema_registry_,
+      base::CreateSequencedTaskRunnerWithTraits({base::MayBlock()})));
+  provider_->Init(&schema_registry_);
+  // Some providers do a reload on init. Make sure any notifications generated
+  // are fired now.
+  scoped_task_environment_.RunUntilIdle();
+
+  const PolicyBundle kEmptyBundle;
+  EXPECT_TRUE(provider_->policies().Equals(kEmptyBundle));
+}
+
+void ConfigurationPolicyProviderTest::TearDown() {
+  // Give providers the chance to clean up after themselves on the file thread.
+  provider_->Shutdown();
+  provider_.reset();
+
+  PolicyTestBase::TearDown();
+}
+
+void ConfigurationPolicyProviderTest::CheckValue(
+    const char* policy_name,
+    const base::Value& expected_value,
+    base::Closure install_value) {
+  // Install the value, reload policy and check the provider for the value.
+  install_value.Run();
+  provider_->RefreshPolicies();
+  scoped_task_environment_.RunUntilIdle();
+  PolicyBundle expected_bundle;
+  expected_bundle.Get(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()))
+      .Set(policy_name, test_harness_->policy_level(),
+           test_harness_->policy_scope(), test_harness_->policy_source(),
+           expected_value.CreateDeepCopy(), nullptr);
+  EXPECT_TRUE(provider_->policies().Equals(expected_bundle));
+  // TODO(joaodasilva): set the policy in the POLICY_DOMAIN_EXTENSIONS too,
+  // and extend the |expected_bundle|, once all providers are ready.
+}
+
+TEST_P(ConfigurationPolicyProviderTest, Empty) {
+  provider_->RefreshPolicies();
+  scoped_task_environment_.RunUntilIdle();
+  const PolicyBundle kEmptyBundle;
+  EXPECT_TRUE(provider_->policies().Equals(kEmptyBundle));
+}
+
+TEST_P(ConfigurationPolicyProviderTest, StringValue) {
+  const char kTestString[] = "string_value";
+  base::Value expected_value(kTestString);
+  CheckValue(test_keys::kKeyString,
+             expected_value,
+             base::Bind(&PolicyProviderTestHarness::InstallStringPolicy,
+                        base::Unretained(test_harness_.get()),
+                        test_keys::kKeyString,
+                        kTestString));
+}
+
+TEST_P(ConfigurationPolicyProviderTest, BooleanValue) {
+  base::Value expected_value(true);
+  CheckValue(test_keys::kKeyBoolean,
+             expected_value,
+             base::Bind(&PolicyProviderTestHarness::InstallBooleanPolicy,
+                        base::Unretained(test_harness_.get()),
+                        test_keys::kKeyBoolean,
+                        true));
+}
+
+TEST_P(ConfigurationPolicyProviderTest, IntegerValue) {
+  base::Value expected_value(42);
+  CheckValue(test_keys::kKeyInteger,
+             expected_value,
+             base::Bind(&PolicyProviderTestHarness::InstallIntegerPolicy,
+                        base::Unretained(test_harness_.get()),
+                        test_keys::kKeyInteger,
+                        42));
+}
+
+TEST_P(ConfigurationPolicyProviderTest, StringListValue) {
+  base::ListValue expected_value;
+  expected_value.AppendString("first");
+  expected_value.AppendString("second");
+  CheckValue(test_keys::kKeyStringList,
+             expected_value,
+             base::Bind(&PolicyProviderTestHarness::InstallStringListPolicy,
+                        base::Unretained(test_harness_.get()),
+                        test_keys::kKeyStringList,
+                        &expected_value));
+}
+
+TEST_P(ConfigurationPolicyProviderTest, DictionaryValue) {
+  base::DictionaryValue expected_value;
+  expected_value.SetBoolean("bool", true);
+  expected_value.SetDouble("double", 123.456);
+  expected_value.SetInteger("int", 123);
+  expected_value.SetString("string", "omg");
+
+  auto list = std::make_unique<base::ListValue>();
+  list->AppendString("first");
+  list->AppendString("second");
+  expected_value.Set("array", std::move(list));
+
+  auto dict = std::make_unique<base::DictionaryValue>();
+  dict->SetString("sub", "value");
+  list = std::make_unique<base::ListValue>();
+  auto sub = std::make_unique<base::DictionaryValue>();
+  sub->SetInteger("aaa", 111);
+  sub->SetInteger("bbb", 222);
+  list->Append(std::move(sub));
+  sub = std::make_unique<base::DictionaryValue>();
+  sub->SetString("ccc", "333");
+  sub->SetString("ddd", "444");
+  list->Append(std::move(sub));
+  dict->Set("sublist", std::move(list));
+  expected_value.Set("dictionary", std::move(dict));
+
+  CheckValue(test_keys::kKeyDictionary,
+             expected_value,
+             base::Bind(&PolicyProviderTestHarness::InstallDictionaryPolicy,
+                        base::Unretained(test_harness_.get()),
+                        test_keys::kKeyDictionary,
+                        &expected_value));
+}
+
+TEST_P(ConfigurationPolicyProviderTest, RefreshPolicies) {
+  PolicyBundle bundle;
+  EXPECT_TRUE(provider_->policies().Equals(bundle));
+
+  // OnUpdatePolicy is called even when there are no changes.
+  MockConfigurationPolicyObserver observer;
+  provider_->AddObserver(&observer);
+  EXPECT_CALL(observer, OnUpdatePolicy(provider_.get())).Times(1);
+  provider_->RefreshPolicies();
+  scoped_task_environment_.RunUntilIdle();
+  Mock::VerifyAndClearExpectations(&observer);
+
+  EXPECT_TRUE(provider_->policies().Equals(bundle));
+
+  // OnUpdatePolicy is called when there are changes.
+  test_harness_->InstallStringPolicy(test_keys::kKeyString, "value");
+  EXPECT_CALL(observer, OnUpdatePolicy(provider_.get())).Times(1);
+  provider_->RefreshPolicies();
+  scoped_task_environment_.RunUntilIdle();
+  Mock::VerifyAndClearExpectations(&observer);
+
+  bundle.Get(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()))
+      .Set(test_keys::kKeyString, test_harness_->policy_level(),
+           test_harness_->policy_scope(), test_harness_->policy_source(),
+           std::make_unique<base::Value>("value"), nullptr);
+  EXPECT_TRUE(provider_->policies().Equals(bundle));
+  provider_->RemoveObserver(&observer);
+}
+
+Configuration3rdPartyPolicyProviderTest::
+    Configuration3rdPartyPolicyProviderTest() {}
+
+Configuration3rdPartyPolicyProviderTest::
+    ~Configuration3rdPartyPolicyProviderTest() {}
+
+TEST_P(Configuration3rdPartyPolicyProviderTest, Load3rdParty) {
+  base::DictionaryValue policy_dict;
+  policy_dict.SetBoolean("bool", true);
+  policy_dict.SetDouble("double", 123.456);
+  policy_dict.SetInteger("int", 789);
+  policy_dict.SetString("string", "string value");
+
+  auto list = std::make_unique<base::ListValue>();
+  for (int i = 0; i < 2; ++i) {
+    auto dict = std::make_unique<base::DictionaryValue>();
+    dict->SetInteger("subdictindex", i);
+    dict->SetKey("subdict", policy_dict.Clone());
+    list->Append(std::move(dict));
+  }
+  policy_dict.Set("list", std::move(list));
+  policy_dict.SetKey("dict", policy_dict.Clone());
+
+  // Install these policies as a Chrome policy.
+  test_harness_->InstallDictionaryPolicy(test_keys::kKeyDictionary,
+                                         &policy_dict);
+  // Install them as 3rd party policies too.
+  base::DictionaryValue policy_3rdparty;
+  policy_3rdparty.SetPath({"extensions", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},
+                          policy_dict.Clone());
+  policy_3rdparty.SetPath({"extensions", "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"},
+                          policy_dict.Clone());
+  // Install invalid 3rd party policies that shouldn't be loaded. These also
+  // help detecting memory leaks in the code paths that detect invalid input.
+  policy_3rdparty.SetPath({"invalid-domain", "component"}, policy_dict.Clone());
+  policy_3rdparty.SetString("extensions.cccccccccccccccccccccccccccccccc",
+                            "invalid-value");
+  test_harness_->Install3rdPartyPolicy(&policy_3rdparty);
+
+  provider_->RefreshPolicies();
+  scoped_task_environment_.RunUntilIdle();
+
+  PolicyMap expected_policy;
+  expected_policy.Set(test_keys::kKeyDictionary, test_harness_->policy_level(),
+                      test_harness_->policy_scope(),
+                      test_harness_->policy_source(),
+                      policy_dict.CreateDeepCopy(), nullptr);
+  PolicyBundle expected_bundle;
+  expected_bundle.Get(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()))
+      .CopyFrom(expected_policy);
+  expected_policy.Clear();
+  expected_policy.LoadFrom(&policy_dict,
+                           test_harness_->policy_level(),
+                           test_harness_->policy_scope(),
+                           test_harness_->policy_source());
+  expected_bundle.Get(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS,
+                                      "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"))
+      .CopyFrom(expected_policy);
+  expected_bundle.Get(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS,
+                                      "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"))
+      .CopyFrom(expected_policy);
+  EXPECT_TRUE(provider_->policies().Equals(expected_bundle));
+}
+
+}  // namespace policy
diff --git a/components/policy/core/common/configuration_policy_provider_test.h b/components/policy/core/common/configuration_policy_provider_test.h
new file mode 100644
index 0000000..0d314b3
--- /dev/null
+++ b/components/policy/core/common/configuration_policy_provider_test.h
@@ -0,0 +1,158 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_POLICY_CORE_COMMON_CONFIGURATION_POLICY_PROVIDER_TEST_H_
+#define COMPONENTS_POLICY_CORE_COMMON_CONFIGURATION_POLICY_PROVIDER_TEST_H_
+
+#include <memory>
+#include <string>
+
+#include "base/callback_forward.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/test/scoped_task_environment.h"
+#include "build/build_config.h"
+#include "components/policy/core/common/policy_types.h"
+#include "components/policy/core/common/schema.h"
+#include "components/policy/core/common/schema_registry.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+class DictionaryValue;
+class ListValue;
+class SequencedTaskRunner;
+class Value;
+}
+
+namespace policy {
+
+class ConfigurationPolicyProvider;
+
+namespace test_keys {
+
+extern const char kKeyString[];
+extern const char kKeyBoolean[];
+extern const char kKeyInteger[];
+extern const char kKeyStringList[];
+extern const char kKeyDictionary[];
+
+}  // namespace test_keys
+
+class PolicyTestBase : public testing::Test {
+ public:
+  PolicyTestBase();
+  ~PolicyTestBase() override;
+
+  // testing::Test:
+  void SetUp() override;
+  void TearDown() override;
+
+ protected:
+  bool RegisterSchema(const PolicyNamespace& ns,
+                      const std::string& schema);
+
+  // Needs to be the first member
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+  SchemaRegistry schema_registry_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(PolicyTestBase);
+};
+
+// An interface for creating a test policy provider and creating a policy
+// provider instance for testing. Used as the parameter to the abstract
+// ConfigurationPolicyProviderTest below.
+class PolicyProviderTestHarness {
+ public:
+  // |level|, |scope| and |source| are the level, scope and source of the
+  // policies returned by the providers from CreateProvider().
+  PolicyProviderTestHarness(PolicyLevel level,
+                            PolicyScope scope,
+                            PolicySource source);
+  virtual ~PolicyProviderTestHarness();
+
+  // Actions to run at gtest SetUp() time.
+  virtual void SetUp() = 0;
+
+  // Create a new policy provider.
+  virtual ConfigurationPolicyProvider* CreateProvider(
+      SchemaRegistry* registry,
+      scoped_refptr<base::SequencedTaskRunner> task_runner) = 0;
+
+  // Returns the policy level, scope and source set by the policy provider.
+  PolicyLevel policy_level() const;
+  PolicyScope policy_scope() const;
+  PolicySource policy_source() const;
+
+  // Helpers to configure the environment the policy provider reads from.
+  virtual void InstallEmptyPolicy() = 0;
+  virtual void InstallStringPolicy(const std::string& policy_name,
+                                   const std::string& policy_value) = 0;
+  virtual void InstallIntegerPolicy(const std::string& policy_name,
+                                    int policy_value) = 0;
+  virtual void InstallBooleanPolicy(const std::string& policy_name,
+                                    bool policy_value) = 0;
+  virtual void InstallStringListPolicy(const std::string& policy_name,
+                                       const base::ListValue* policy_value) = 0;
+  virtual void InstallDictionaryPolicy(
+      const std::string& policy_name,
+      const base::DictionaryValue* policy_value) = 0;
+
+  // Not every provider supports installing 3rd party policy. Those who do
+  // should override this method; the default just makes the test fail.
+  virtual void Install3rdPartyPolicy(const base::DictionaryValue* policies);
+
+ private:
+  PolicyLevel level_;
+  PolicyScope scope_;
+  PolicySource source_;
+
+  DISALLOW_COPY_AND_ASSIGN(PolicyProviderTestHarness);
+};
+
+// A factory method for creating a test harness.
+typedef PolicyProviderTestHarness* (*CreatePolicyProviderTestHarness)();
+
+// Abstract policy provider test. This is meant to be instantiated for each
+// policy provider implementation, passing in a suitable harness factory
+// function as the test parameter.
+class ConfigurationPolicyProviderTest
+    : public PolicyTestBase,
+      public testing::WithParamInterface<CreatePolicyProviderTestHarness> {
+ protected:
+  ConfigurationPolicyProviderTest();
+  virtual ~ConfigurationPolicyProviderTest();
+
+  void SetUp() override;
+  void TearDown() override;
+
+  // Installs a valid policy and checks whether the provider returns the
+  // |expected_value|.
+  void CheckValue(const char* policy_name,
+                  const base::Value& expected_value,
+                  base::Closure install_value);
+
+  std::unique_ptr<PolicyProviderTestHarness> test_harness_;
+  std::unique_ptr<ConfigurationPolicyProvider> provider_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ConfigurationPolicyProviderTest);
+};
+
+// An extension of ConfigurationPolicyProviderTest that also tests loading of
+// 3rd party policy. Policy provider implementations that support loading of
+// 3rd party policy should also instantiate these tests.
+class Configuration3rdPartyPolicyProviderTest
+    : public ConfigurationPolicyProviderTest {
+ protected:
+  Configuration3rdPartyPolicyProviderTest();
+  virtual ~Configuration3rdPartyPolicyProviderTest();
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(Configuration3rdPartyPolicyProviderTest);
+};
+
+}  // namespace policy
+
+#endif  // COMPONENTS_POLICY_CORE_COMMON_CONFIGURATION_POLICY_PROVIDER_TEST_H_
diff --git a/components/policy/core/common/external_data_fetcher.cc b/components/policy/core/common/external_data_fetcher.cc
new file mode 100644
index 0000000..1bce30a
--- /dev/null
+++ b/components/policy/core/common/external_data_fetcher.cc
@@ -0,0 +1,45 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/policy/core/common/external_data_fetcher.h"
+
+#include "base/callback.h"
+#include "components/policy/core/common/external_data_manager.h"
+
+namespace policy {
+
+ExternalDataFetcher::ExternalDataFetcher(
+    base::WeakPtr<ExternalDataManager> manager,
+    const std::string& policy)
+    : manager_(manager),
+      policy_(policy) {
+}
+
+ExternalDataFetcher::ExternalDataFetcher(const ExternalDataFetcher& other)
+    : manager_(other.manager_),
+      policy_(other.policy_) {
+}
+
+ExternalDataFetcher::~ExternalDataFetcher() {
+}
+
+// static
+bool ExternalDataFetcher::Equals(const ExternalDataFetcher* first,
+                                 const ExternalDataFetcher* second) {
+  if (!first && !second)
+    return true;
+  if (!first || !second)
+    return false;
+  return first->manager_.get() == second->manager_.get() &&
+         first->policy_ == second->policy_;
+}
+
+void ExternalDataFetcher::Fetch(const FetchCallback& callback) const {
+  if (manager_)
+    manager_->Fetch(policy_, callback);
+  else
+    callback.Run(std::unique_ptr<std::string>());
+}
+
+}  // namespace policy
diff --git a/components/policy/core/common/external_data_fetcher.h b/components/policy/core/common/external_data_fetcher.h
new file mode 100644
index 0000000..0c014e7
--- /dev/null
+++ b/components/policy/core/common/external_data_fetcher.h
@@ -0,0 +1,53 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_POLICY_CORE_COMMON_EXTERNAL_DATA_FETCHER_H_
+#define COMPONENTS_POLICY_CORE_COMMON_EXTERNAL_DATA_FETCHER_H_
+
+#include <memory>
+#include <string>
+
+#include "base/callback_forward.h"
+#include "base/memory/weak_ptr.h"
+#include "components/policy/policy_export.h"
+
+namespace policy {
+
+class ExternalDataManager;
+
+// A helper that encapsulates the parameters required to retrieve the external
+// data for a policy.
+class POLICY_EXPORT ExternalDataFetcher {
+ public:
+  typedef base::Callback<void(std::unique_ptr<std::string>)> FetchCallback;
+
+  // This instance's Fetch() method will instruct the |manager| to retrieve the
+  // external data referenced by the given |policy|.
+  ExternalDataFetcher(base::WeakPtr<ExternalDataManager> manager,
+                      const std::string& policy);
+  ExternalDataFetcher(const ExternalDataFetcher& other);
+
+  ~ExternalDataFetcher();
+
+  static bool Equals(const ExternalDataFetcher* first,
+                     const ExternalDataFetcher* second);
+
+  // Retrieves the external data referenced by |policy_| and invokes |callback|
+  // with the result. If |policy_| does not reference any external data, the
+  // |callback| is invoked with a NULL pointer. Otherwise, the |callback| is
+  // invoked with the referenced data once it has been successfully retrieved.
+  // If retrieval is temporarily impossible (e.g. no network connectivity), the
+  // |callback| will be invoked when the temporary hindrance is resolved. If
+  // retrieval is permanently impossible (e.g. |policy_| references data that
+  // does not exist on the server), the |callback| will never be invoked.
+  void Fetch(const FetchCallback& callback) const;
+
+ private:
+  base::WeakPtr<ExternalDataManager> manager_;
+  const std::string policy_;
+};
+
+}  // namespace policy
+
+#endif  // COMPONENTS_POLICY_CORE_COMMON_EXTERNAL_DATA_FETCHER_H_
diff --git a/components/policy/core/common/external_data_manager.h b/components/policy/core/common/external_data_manager.h
new file mode 100644
index 0000000..406cf59
--- /dev/null
+++ b/components/policy/core/common/external_data_manager.h
@@ -0,0 +1,36 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_POLICY_CORE_COMMON_EXTERNAL_DATA_MANAGER_H_
+#define COMPONENTS_POLICY_CORE_COMMON_EXTERNAL_DATA_MANAGER_H_
+
+#include <string>
+
+#include "components/policy/core/common/external_data_fetcher.h"
+#include "components/policy/policy_export.h"
+
+namespace policy {
+
+// Downloads, verifies, caches and retrieves external data referenced by
+// policies.
+// An implementation of this abstract interface should be provided for each
+// policy source (cloud policy, platform policy) that supports external data
+// references.
+class POLICY_EXPORT ExternalDataManager {
+ public:
+  // Retrieves the external data referenced by |policy| and invokes |callback|
+  // with the result. If |policy| does not reference any external data, the
+  // |callback| is invoked with a NULL pointer. Otherwise, the |callback| is
+  // invoked with the referenced data once it has been successfully retrieved.
+  // If retrieval is temporarily impossible (e.g. no network connectivity), the
+  // |callback| will be invoked when the temporary hindrance is resolved. If
+  // retrieval is permanently impossible (e.g. |policy| references data that
+  // does not exist on the server), the |callback| will never be invoked.
+  virtual void Fetch(const std::string& policy,
+                     const ExternalDataFetcher::FetchCallback& callback) = 0;
+};
+
+}  // namespace policy
+
+#endif  // COMPONENTS_POLICY_CORE_COMMON_EXTERNAL_DATA_MANAGER_H_
diff --git a/components/policy/core/common/fake_async_policy_loader.cc b/components/policy/core/common/fake_async_policy_loader.cc
new file mode 100644
index 0000000..efa9f7b
--- /dev/null
+++ b/components/policy/core/common/fake_async_policy_loader.cc
@@ -0,0 +1,37 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/policy/core/common/fake_async_policy_loader.h"
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/sequenced_task_runner.h"
+
+namespace policy {
+
+FakeAsyncPolicyLoader::FakeAsyncPolicyLoader(
+    const scoped_refptr<base::SequencedTaskRunner>& task_runner)
+    : AsyncPolicyLoader(task_runner) {
+}
+
+std::unique_ptr<PolicyBundle> FakeAsyncPolicyLoader::Load() {
+  std::unique_ptr<PolicyBundle> result(new PolicyBundle());
+  result->CopyFrom(policy_bundle_);
+  return result;
+}
+
+void FakeAsyncPolicyLoader::InitOnBackgroundThread() {
+  // Nothing to do.
+}
+
+void FakeAsyncPolicyLoader::SetPolicies(const PolicyBundle& policy_bundle) {
+  policy_bundle_.CopyFrom(policy_bundle);
+}
+
+void FakeAsyncPolicyLoader::PostReloadOnBackgroundThread(bool force) {
+  task_runner()->PostTask(FROM_HERE, base::Bind(&AsyncPolicyLoader::Reload,
+                                                base::Unretained(this), force));
+}
+
+}  // namespace policy
diff --git a/components/policy/core/common/fake_async_policy_loader.h b/components/policy/core/common/fake_async_policy_loader.h
new file mode 100644
index 0000000..e158374
--- /dev/null
+++ b/components/policy/core/common/fake_async_policy_loader.h
@@ -0,0 +1,54 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_POLICY_CORE_COMMON_FAKE_ASYNC_POLICY_LOADER_H_
+#define COMPONENTS_POLICY_CORE_COMMON_FAKE_ASYNC_POLICY_LOADER_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "components/policy/core/common/async_policy_loader.h"
+#include "components/policy/core/common/policy_bundle.h"
+
+namespace base {
+class SequencedTaskRunner;
+}  // namespace base
+
+namespace policy {
+
+// Fake AsyncPolicyLoader for testing with test-controlled policies.
+//
+// Typical test code would populate the policy contents via calls to
+// ClearPolicies and AddPolicies and then notify the rest of the policy
+// subsystem of the changes by calling PostReloadOnBackgroundThread.
+class FakeAsyncPolicyLoader : public AsyncPolicyLoader {
+ public:
+  explicit FakeAsyncPolicyLoader(
+      const scoped_refptr<base::SequencedTaskRunner>& task_runner);
+
+  // Implementation of virtual methods from AsyncPolicyLoader base class.
+  std::unique_ptr<PolicyBundle> Load() override;
+  void InitOnBackgroundThread() override;
+
+  // Provides content for the simulated / faked policies.
+  void SetPolicies(const PolicyBundle& policy_bundle);
+
+  // Notifies the rest of the policy subsystem that policy contents have
+  // changed.  This simulates / fakes a notification that normally would be
+  // triggered by a FilePathWatcher or (registry)ObjectWatcher in a real loader.
+  //
+  // See AsyncPolicyLoader::Reload method for description of the |force|
+  // parameter.
+  void PostReloadOnBackgroundThread(bool force);
+
+ private:
+  PolicyBundle policy_bundle_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeAsyncPolicyLoader);
+};
+
+}  // namespace policy
+
+#endif  // COMPONENTS_POLICY_CORE_COMMON_FAKE_ASYNC_POLICY_LOADER_H_
diff --git a/components/policy/core/common/generate_policy_source_unittest.cc b/components/policy/core/common/generate_policy_source_unittest.cc
new file mode 100644
index 0000000..78d5cad
--- /dev/null
+++ b/components/policy/core/common/generate_policy_source_unittest.cc
@@ -0,0 +1,227 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <cstring>
+#include <memory>
+#include <string>
+
+#include "base/values.h"
+#include "build/build_config.h"
+#include "components/policy/core/common/policy_details.h"
+#include "components/policy/core/common/schema.h"
+#include "components/policy/policy_constants.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// This unittest tests the code generated by
+// chrome/tools/build/generate_policy_source.py.
+
+namespace policy {
+
+namespace {
+
+#if defined(OS_CHROMEOS)
+// Checks if two schemas are the same or not. Note that this function doesn't
+// consider restrictions on integers and strings nor pattern properties.
+bool IsSameSchema(Schema a, Schema b) {
+  if (a.valid() != b.valid())
+    return false;
+  if (!a.valid())
+    return true;
+  if (a.type() != b.type())
+    return false;
+  if (a.type() == base::Value::Type::LIST)
+    return IsSameSchema(a.GetItems(), b.GetItems());
+  if (a.type() != base::Value::Type::DICTIONARY)
+    return true;
+  Schema::Iterator a_it = a.GetPropertiesIterator();
+  Schema::Iterator b_it = b.GetPropertiesIterator();
+  while (!a_it.IsAtEnd()) {
+    if (b_it.IsAtEnd())
+      return false;
+    if (strcmp(a_it.key(), b_it.key()) != 0)
+      return false;
+    if (!IsSameSchema(a_it.schema(), b_it.schema()))
+      return false;
+    a_it.Advance();
+    b_it.Advance();
+  }
+  if (!b_it.IsAtEnd())
+    return false;
+  return IsSameSchema(a.GetAdditionalProperties(), b.GetAdditionalProperties());
+}
+#endif
+
+}  // namespace
+
+TEST(GeneratePolicySource, ChromeSchemaData) {
+  Schema schema = Schema::Wrap(GetChromeSchemaData());
+  ASSERT_TRUE(schema.valid());
+  EXPECT_EQ(base::Value::Type::DICTIONARY, schema.type());
+
+  Schema subschema = schema.GetAdditionalProperties();
+  EXPECT_FALSE(subschema.valid());
+
+  subschema = schema.GetProperty("no such policy exists");
+  EXPECT_FALSE(subschema.valid());
+
+  subschema = schema.GetProperty(key::kSearchSuggestEnabled);
+  ASSERT_TRUE(subschema.valid());
+  EXPECT_EQ(base::Value::Type::BOOLEAN, subschema.type());
+
+  subschema = schema.GetProperty(key::kDefaultCookiesSetting);
+  ASSERT_TRUE(subschema.valid());
+  EXPECT_EQ(base::Value::Type::INTEGER, subschema.type());
+
+  subschema = schema.GetProperty(key::kProxyMode);
+  ASSERT_TRUE(subschema.valid());
+  EXPECT_EQ(base::Value::Type::STRING, subschema.type());
+
+  subschema = schema.GetProperty(key::kURLBlacklist);
+  ASSERT_TRUE(subschema.valid());
+  EXPECT_EQ(base::Value::Type::LIST, subschema.type());
+  ASSERT_TRUE(subschema.GetItems().valid());
+  EXPECT_EQ(base::Value::Type::STRING, subschema.GetItems().type());
+
+#if !defined(OS_ANDROID) && !defined(OS_IOS)
+  subschema = schema.GetProperty(key::kExtensionSettings);
+  ASSERT_TRUE(subschema.valid());
+  ASSERT_EQ(base::Value::Type::DICTIONARY, subschema.type());
+  EXPECT_FALSE(subschema.GetAdditionalProperties().valid());
+  EXPECT_FALSE(subschema.GetProperty("no such extension id exists").valid());
+  EXPECT_TRUE(subschema.GetPatternProperties("*").empty());
+  EXPECT_TRUE(subschema.GetPatternProperties("no such extension id").empty());
+  EXPECT_TRUE(subschema.GetPatternProperties("^[a-p]{32}$").empty());
+  EXPECT_TRUE(subschema.GetPatternProperties(
+                  "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").empty());
+  EXPECT_TRUE(subschema.GetPatternProperties(
+                  "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").empty());
+  SchemaList schema_list =
+      subschema.GetPatternProperties("abcdefghijklmnopabcdefghijklmnop");
+  ASSERT_EQ(1u, schema_list.size());
+  subschema = schema_list[0];
+  ASSERT_TRUE(subschema.valid());
+  ASSERT_EQ(base::Value::Type::DICTIONARY, subschema.type());
+  subschema = subschema.GetProperty("installation_mode");
+  ASSERT_TRUE(subschema.valid());
+  ASSERT_EQ(base::Value::Type::STRING, subschema.type());
+
+  subschema = schema.GetProperty(key::kExtensionSettings).GetProperty("*");
+  ASSERT_TRUE(subschema.valid());
+  ASSERT_EQ(base::Value::Type::DICTIONARY, subschema.type());
+  subschema = subschema.GetProperty("installation_mode");
+  ASSERT_TRUE(subschema.valid());
+  ASSERT_EQ(base::Value::Type::STRING, subschema.type());
+#endif
+
+  subschema = schema.GetProperty(key::kProxySettings);
+  ASSERT_TRUE(subschema.valid());
+  EXPECT_EQ(base::Value::Type::DICTIONARY, subschema.type());
+  EXPECT_FALSE(subschema.GetAdditionalProperties().valid());
+  EXPECT_FALSE(subschema.GetProperty("no such proxy key exists").valid());
+  ASSERT_TRUE(subschema.GetProperty(key::kProxyMode).valid());
+  ASSERT_TRUE(subschema.GetProperty(key::kProxyServer).valid());
+  ASSERT_TRUE(subschema.GetProperty(key::kProxyServerMode).valid());
+  ASSERT_TRUE(subschema.GetProperty(key::kProxyPacUrl).valid());
+  ASSERT_TRUE(subschema.GetProperty(key::kProxyBypassList).valid());
+
+  // Verify that all the Chrome policies are there.
+  for (Schema::Iterator it = schema.GetPropertiesIterator();
+       !it.IsAtEnd(); it.Advance()) {
+    EXPECT_TRUE(it.key());
+    EXPECT_FALSE(std::string(it.key()).empty());
+    EXPECT_TRUE(GetChromePolicyDetails(it.key()));
+  }
+
+  // The properties are iterated in order.
+  const char* kExpectedProperties[] = {
+      key::kProxyBypassList, key::kProxyMode,       key::kProxyPacUrl,
+      key::kProxyServer,     key::kProxyServerMode, nullptr,
+  };
+  const char** next = kExpectedProperties;
+  for (Schema::Iterator it(subschema.GetPropertiesIterator());
+       !it.IsAtEnd(); it.Advance(), ++next) {
+    ASSERT_TRUE(*next != nullptr);
+    EXPECT_STREQ(*next, it.key());
+    ASSERT_TRUE(it.schema().valid());
+    EXPECT_EQ(base::Value::Type::STRING, it.schema().type());
+  }
+  EXPECT_TRUE(*next == nullptr);
+
+#if defined(OS_CHROMEOS)
+  subschema = schema.GetKnownProperty(key::kPowerManagementIdleSettings);
+  ASSERT_TRUE(subschema.valid());
+
+  EXPECT_TRUE(IsSameSchema(subschema.GetKnownProperty("AC"),
+                           subschema.GetKnownProperty("Battery")));
+
+  subschema = schema.GetKnownProperty(key::kDeviceLoginScreenPowerManagement);
+  ASSERT_TRUE(subschema.valid());
+
+  EXPECT_TRUE(IsSameSchema(subschema.GetKnownProperty("AC"),
+                           subschema.GetKnownProperty("Battery")));
+#endif
+}
+
+TEST(GeneratePolicySource, PolicyDetails) {
+  EXPECT_FALSE(GetChromePolicyDetails(""));
+  EXPECT_FALSE(GetChromePolicyDetails("no such policy"));
+  EXPECT_FALSE(GetChromePolicyDetails("SearchSuggestEnable"));
+  EXPECT_FALSE(GetChromePolicyDetails("searchSuggestEnabled"));
+  EXPECT_FALSE(GetChromePolicyDetails("SSearchSuggestEnabled"));
+
+  const PolicyDetails* details =
+      GetChromePolicyDetails(key::kSearchSuggestEnabled);
+  ASSERT_TRUE(details);
+  EXPECT_FALSE(details->is_deprecated);
+  EXPECT_FALSE(details->is_device_policy);
+  EXPECT_EQ(6, details->id);
+  EXPECT_EQ(0u, details->max_external_data_size);
+
+#if !defined(OS_IOS)
+  details = GetChromePolicyDetails(key::kJavascriptEnabled);
+  ASSERT_TRUE(details);
+  EXPECT_TRUE(details->is_deprecated);
+  EXPECT_FALSE(details->is_device_policy);
+  EXPECT_EQ(9, details->id);
+  EXPECT_EQ(0u, details->max_external_data_size);
+#endif
+
+#if defined(OS_CHROMEOS)
+  details = GetChromePolicyDetails(key::kDevicePolicyRefreshRate);
+  ASSERT_TRUE(details);
+  EXPECT_FALSE(details->is_deprecated);
+  EXPECT_TRUE(details->is_device_policy);
+  EXPECT_EQ(90, details->id);
+  EXPECT_EQ(0u, details->max_external_data_size);
+#endif
+
+  // TODO(bartfab): add a test that verifies a max_external_data_size larger
+  // than 0, once a type 'external' policy is added.
+}
+
+#if defined(OS_CHROMEOS)
+TEST(GeneratePolicySource, SetEnterpriseDefaults) {
+  PolicyMap policy_map;
+
+  // If policy not configured yet, set the enterprise default.
+  SetEnterpriseUsersDefaults(&policy_map);
+
+  const base::Value* multiprof_behavior =
+      policy_map.GetValue(key::kChromeOsMultiProfileUserBehavior);
+  base::Value expected("primary-only");
+  EXPECT_TRUE(expected.Equals(multiprof_behavior));
+
+  // If policy already configured, it's not changed to enterprise defaults.
+  policy_map.Set(key::kChromeOsMultiProfileUserBehavior, POLICY_LEVEL_MANDATORY,
+                 POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD,
+                 std::make_unique<base::Value>("test_value"), nullptr);
+  SetEnterpriseUsersDefaults(&policy_map);
+  multiprof_behavior =
+      policy_map.GetValue(key::kChromeOsMultiProfileUserBehavior);
+  expected = base::Value("test_value");
+  EXPECT_TRUE(expected.Equals(multiprof_behavior));
+}
+#endif
+
+}  // namespace policy
diff --git a/components/policy/core/common/mock_configuration_policy_provider.cc b/components/policy/core/common/mock_configuration_policy_provider.cc
new file mode 100644
index 0000000..ae8458a
--- /dev/null
+++ b/components/policy/core/common/mock_configuration_policy_provider.cc
@@ -0,0 +1,48 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/policy/core/common/mock_configuration_policy_provider.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "base/message_loop/message_loop_current.h"
+#include "base/run_loop.h"
+#include "components/policy/core/common/policy_bundle.h"
+
+using testing::Invoke;
+
+namespace policy {
+
+MockConfigurationPolicyProvider::MockConfigurationPolicyProvider() {}
+
+MockConfigurationPolicyProvider::~MockConfigurationPolicyProvider() {}
+
+void MockConfigurationPolicyProvider::UpdateChromePolicy(
+    const PolicyMap& policy) {
+  std::unique_ptr<PolicyBundle> bundle(new PolicyBundle());
+  bundle->Get(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()))
+      .CopyFrom(policy);
+  UpdatePolicy(std::move(bundle));
+  if (base::MessageLoopCurrent::IsSet())
+    base::RunLoop().RunUntilIdle();
+}
+
+void MockConfigurationPolicyProvider::SetAutoRefresh() {
+  EXPECT_CALL(*this, RefreshPolicies()).WillRepeatedly(
+      Invoke(this, &MockConfigurationPolicyProvider::RefreshWithSamePolicies));
+}
+
+void MockConfigurationPolicyProvider::RefreshWithSamePolicies() {
+  std::unique_ptr<PolicyBundle> bundle(new PolicyBundle);
+  bundle->CopyFrom(policies());
+  UpdatePolicy(std::move(bundle));
+}
+
+MockConfigurationPolicyObserver::MockConfigurationPolicyObserver() {}
+
+MockConfigurationPolicyObserver::~MockConfigurationPolicyObserver() {}
+
+}  // namespace policy
diff --git a/components/policy/core/common/mock_configuration_policy_provider.h b/components/policy/core/common/mock_configuration_policy_provider.h
new file mode 100644
index 0000000..84f664c
--- /dev/null
+++ b/components/policy/core/common/mock_configuration_policy_provider.h
@@ -0,0 +1,66 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_POLICY_CORE_COMMON_MOCK_CONFIGURATION_POLICY_PROVIDER_H_
+#define COMPONENTS_POLICY_CORE_COMMON_MOCK_CONFIGURATION_POLICY_PROVIDER_H_
+
+#include "base/macros.h"
+#include "components/policy/core/common/configuration_policy_provider.h"
+#include "components/policy/core/common/policy_map.h"
+#include "components/policy/core/common/schema_registry.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace policy {
+
+// Mock ConfigurationPolicyProvider implementation that supplies canned
+// values for polices.
+// TODO(joaodasilva, mnissler): introduce an implementation that non-policy
+// code can use that doesn't require the usual boilerplate.
+// http://crbug.com/242087
+class MockConfigurationPolicyProvider : public ConfigurationPolicyProvider {
+ public:
+  MockConfigurationPolicyProvider();
+  ~MockConfigurationPolicyProvider() override;
+
+  MOCK_CONST_METHOD1(IsInitializationComplete, bool(PolicyDomain domain));
+  MOCK_METHOD0(RefreshPolicies, void());
+
+  // Make public for tests.
+  using ConfigurationPolicyProvider::UpdatePolicy;
+
+  // Utility method that invokes UpdatePolicy() with a PolicyBundle that maps
+  // the Chrome namespace to a copy of |policy|.
+  void UpdateChromePolicy(const PolicyMap& policy);
+
+  // Convenience method so that tests don't need to create a registry to create
+  // this mock.
+  using ConfigurationPolicyProvider::Init;
+  void Init() {
+    ConfigurationPolicyProvider::Init(&registry_);
+  }
+
+  // Convenience method that installs an expectation on RefreshPolicies that
+  // just notifies the observers and serves the same policies.
+  void SetAutoRefresh();
+
+ private:
+  void RefreshWithSamePolicies();
+
+  SchemaRegistry registry_;
+
+  DISALLOW_COPY_AND_ASSIGN(MockConfigurationPolicyProvider);
+};
+
+class MockConfigurationPolicyObserver
+    : public ConfigurationPolicyProvider::Observer {
+ public:
+  MockConfigurationPolicyObserver();
+  ~MockConfigurationPolicyObserver() override;
+
+  MOCK_METHOD1(OnUpdatePolicy, void(ConfigurationPolicyProvider*));
+};
+
+}  // namespace policy
+
+#endif  // COMPONENTS_POLICY_CORE_COMMON_MOCK_CONFIGURATION_POLICY_PROVIDER_H_
diff --git a/components/policy/core/common/mock_policy_service.cc b/components/policy/core/common/mock_policy_service.cc
new file mode 100644
index 0000000..6e3e0c1
--- /dev/null
+++ b/components/policy/core/common/mock_policy_service.cc
@@ -0,0 +1,21 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/policy/core/common/mock_policy_service.h"
+
+namespace policy {
+
+MockPolicyServiceObserver::MockPolicyServiceObserver() {
+}
+
+MockPolicyServiceObserver::~MockPolicyServiceObserver() {
+}
+
+MockPolicyService::MockPolicyService() {
+}
+
+MockPolicyService::~MockPolicyService() {
+}
+
+}  // namespace policy
diff --git a/components/policy/core/common/mock_policy_service.h b/components/policy/core/common/mock_policy_service.h
new file mode 100644
index 0000000..3352860
--- /dev/null
+++ b/components/policy/core/common/mock_policy_service.h
@@ -0,0 +1,39 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_POLICY_CORE_COMMON_MOCK_POLICY_SERVICE_H_
+#define COMPONENTS_POLICY_CORE_COMMON_MOCK_POLICY_SERVICE_H_
+
+#include "components/policy/core/common/policy_service.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace policy {
+
+class MockPolicyServiceObserver : public PolicyService::Observer {
+ public:
+  MockPolicyServiceObserver();
+  ~MockPolicyServiceObserver() override;
+
+  MOCK_METHOD3(OnPolicyUpdated, void(const PolicyNamespace&,
+                                     const PolicyMap& previous,
+                                     const PolicyMap& current));
+  MOCK_METHOD1(OnPolicyServiceInitialized, void(PolicyDomain));
+};
+
+class MockPolicyService : public PolicyService {
+ public:
+  MockPolicyService();
+  ~MockPolicyService() override;
+
+  MOCK_METHOD2(AddObserver, void(PolicyDomain, Observer*));
+  MOCK_METHOD2(RemoveObserver, void(PolicyDomain, Observer*));
+
+  MOCK_CONST_METHOD1(GetPolicies, const PolicyMap&(const PolicyNamespace&));
+  MOCK_CONST_METHOD1(IsInitializationComplete, bool(PolicyDomain domain));
+  MOCK_METHOD1(RefreshPolicies, void(const base::Closure&));
+};
+
+}  // namespace policy
+
+#endif  // COMPONENTS_POLICY_CORE_COMMON_MOCK_POLICY_SERVICE_H_
diff --git a/components/policy/core/common/plist_writer.cc b/components/policy/core/common/plist_writer.cc
new file mode 100644
index 0000000..68c1db5
--- /dev/null
+++ b/components/policy/core/common/plist_writer.cc
@@ -0,0 +1,91 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/policy/core/common/plist_writer.h"
+
+#include "base/strings/string_number_conversions.h"
+#include "build/build_config.h"
+#include "third_party/libxml/chromium/libxml_utils.h"
+
+namespace policy {
+
+namespace {
+
+// Called recursively to build the Plist xml. When completed,
+// |plist_writer| will contain the Plist. Return true on success and false on
+// failure.
+bool BuildPlistString(const base::Value& node, XmlWriter& plist_writer) {
+  switch (node.type()) {
+    case base::Value::Type::BOOLEAN: {
+      bool value = node.GetBool();
+      plist_writer.StartElement(value ? "true" : "false");
+      plist_writer.EndElement();
+      return true;
+    }
+
+    case base::Value::Type::INTEGER: {
+      int value = node.GetInt();
+      plist_writer.WriteElement("integer", base::IntToString(value));
+      return true;
+    }
+
+    case base::Value::Type::STRING: {
+      std::string value = node.GetString();
+      plist_writer.WriteElement("string", value);
+      return true;
+    }
+
+    case base::Value::Type::LIST: {
+      plist_writer.StartElement("array");
+
+      for (const auto& value : node.GetList()) {
+        if (!BuildPlistString(value, plist_writer))
+          return false;
+      }
+
+      plist_writer.EndElement();
+      return true;
+    }
+
+    case base::Value::Type::DICTIONARY: {
+      plist_writer.StartElement("dict");
+
+      const base::DictionaryValue* dict = nullptr;
+      bool result = node.GetAsDictionary(&dict);
+      DCHECK(result);
+      for (base::DictionaryValue::Iterator itr(*dict); !itr.IsAtEnd();
+           itr.Advance()) {
+        plist_writer.WriteElement("key", itr.key());
+
+        if (!BuildPlistString(itr.value(), plist_writer))
+          result = false;
+      }
+
+      plist_writer.EndElement();
+      return result;
+    }
+
+    default:
+      NOTREACHED();
+      return false;
+  }
+}
+
+}  // namespace
+
+bool PlistWrite(const base::Value& node, std::string* plist) {
+  // Where we write Plist data as we generate it.
+  XmlWriter plist_writer;
+  plist_writer.StartWriting();
+  plist_writer.StartIndenting();
+  plist_writer.StartElement("plist");
+  bool result = BuildPlistString(node, plist_writer);
+  plist_writer.EndElement();
+  plist_writer.StopWriting();
+
+  *plist = plist_writer.GetWrittenString();
+  return result;
+}
+
+}  // namespace policy
diff --git a/components/policy/core/common/plist_writer.h b/components/policy/core/common/plist_writer.h
new file mode 100644
index 0000000..0ad8223
--- /dev/null
+++ b/components/policy/core/common/plist_writer.h
@@ -0,0 +1,25 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_POLICY_CORE_COMMON_PLIST_WRITER_H_
+#define COMPONENTS_POLICY_CORE_COMMON_PLIST_WRITER_H_
+
+#include <stddef.h>
+#include <string>
+#include "base/values.h"
+#include "components/policy/policy_export.h"
+
+namespace policy {
+
+// Given a root node, generates a Plist string and puts it into |plist|.
+// The output string is overwritten and not appended.
+// Return true on success and false on failure.
+// TODO(rodmartin): Should we generate plist if it would be invalid plist
+// (e.g., |node| is not a DictionaryValue/ListValue or if there are inf/-inf
+// float values)?
+POLICY_EXPORT bool PlistWrite(const base::Value& node, std::string* plist);
+
+}  // namespace policy
+
+#endif  // COMPONENTS_POLICY_CORE_COMMON_Plist_WRITER_H_
\ No newline at end of file
diff --git a/components/policy/core/common/plist_writer_unittest.cc b/components/policy/core/common/plist_writer_unittest.cc
new file mode 100644
index 0000000..0d89d0a
--- /dev/null
+++ b/components/policy/core/common/plist_writer_unittest.cc
@@ -0,0 +1,135 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/policy/core/common/plist_writer.h"
+
+#include "base/memory/ptr_util.h"
+#include "base/strings/strcat.h"
+#include "base/values.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace policy {
+
+namespace {
+
+const char kPlistHeaderXML[] = "<?xml version=\"1.0\"?>";
+const char kPlistHeaderVersion[] = "<plist>";
+const char kPlistFooter[] = "</plist>\n";
+
+#define PLIST_NEWLINE "\n"
+
+}  // namespace
+
+class PlistWriterTest : public testing::Test {
+ public:
+  PlistWriterTest();
+  ~PlistWriterTest() override;
+
+  void SetUp() override;
+
+  std::string header_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(PlistWriterTest);
+};
+
+PlistWriterTest::PlistWriterTest() {}
+
+PlistWriterTest::~PlistWriterTest() {}
+
+void PlistWriterTest::SetUp() {
+  header_ = base::StrCat(
+      {kPlistHeaderXML, PLIST_NEWLINE, kPlistHeaderVersion, PLIST_NEWLINE});
+}
+
+TEST_F(PlistWriterTest, BasicTypes) {
+  std::string output_plist;
+
+  // Test empty dict.
+  EXPECT_TRUE(PlistWrite(base::DictionaryValue(), &output_plist));
+  EXPECT_EQ(base::StrCat({header_, " <dict/>", PLIST_NEWLINE, kPlistFooter}),
+            output_plist);
+
+  // Test empty list.
+  EXPECT_TRUE(PlistWrite(base::ListValue(), &output_plist));
+  EXPECT_EQ(base::StrCat({header_, " <array/>", PLIST_NEWLINE, kPlistFooter}),
+            output_plist);
+
+  // Test integer values.
+  EXPECT_TRUE(PlistWrite(base::Value(42), &output_plist));
+  EXPECT_EQ(base::StrCat({header_, " <integer>42</integer>", PLIST_NEWLINE,
+                          kPlistFooter}),
+            output_plist);
+
+  // Test boolean values.
+  EXPECT_TRUE(PlistWrite(base::Value(true), &output_plist));
+  EXPECT_EQ(base::StrCat({header_, " <true/>", PLIST_NEWLINE, kPlistFooter}),
+            output_plist);
+
+  // Test string values.
+  EXPECT_TRUE(PlistWrite(base::Value("foo"), &output_plist));
+  EXPECT_EQ(base::StrCat({header_, " <string>foo</string>", PLIST_NEWLINE,
+                          kPlistFooter}),
+            output_plist);
+}
+
+TEST_F(PlistWriterTest, NestedTypes) {
+  std::string output_plist;
+
+  // Writer unittests like empty list/dict nesting,
+  // list list nesting, etc.
+  base::DictionaryValue root_dict;
+  std::unique_ptr<base::ListValue> list(new base::ListValue());
+  std::unique_ptr<base::DictionaryValue> inner_dict(
+      new base::DictionaryValue());
+  inner_dict->SetInteger("inner int", 10);
+  list->Append(std::move(inner_dict));
+  list->Append(std::make_unique<base::ListValue>());
+  list->AppendBoolean(false);
+  root_dict.Set("list", std::move(list));
+
+  EXPECT_TRUE(PlistWrite(root_dict, &output_plist));
+  EXPECT_EQ(base::StrCat({header_,       " <dict>",
+                          PLIST_NEWLINE, "  <key>list</key>",
+                          PLIST_NEWLINE, "  <array>",
+                          PLIST_NEWLINE, "   <dict>",
+                          PLIST_NEWLINE, "    <key>inner int</key>",
+                          PLIST_NEWLINE, "    <integer>10</integer>",
+                          PLIST_NEWLINE, "   </dict>",
+                          PLIST_NEWLINE, "   <array/>",
+                          PLIST_NEWLINE, "   <false/>",
+                          PLIST_NEWLINE, "  </array>",
+                          PLIST_NEWLINE, " </dict>",
+                          PLIST_NEWLINE, kPlistFooter}),
+            output_plist);
+}
+
+TEST_F(PlistWriterTest, KeysWithPeriods) {
+  std::string output_plist;
+
+  base::DictionaryValue period_dict;
+  period_dict.SetKey("a.b", base::Value(3));
+  period_dict.SetKey("c", base::Value(2));
+  std::unique_ptr<base::DictionaryValue> period_dict2(
+      new base::DictionaryValue());
+  period_dict2->SetKey("g.h.i.j", base::Value(1));
+  period_dict.SetWithoutPathExpansion("d.e.f", std::move(period_dict2));
+  EXPECT_TRUE(PlistWrite(period_dict, &output_plist));
+  EXPECT_EQ(base::StrCat({header_,       " <dict>",
+                          PLIST_NEWLINE, "  <key>a.b</key>",
+                          PLIST_NEWLINE, "  <integer>3</integer>",
+                          PLIST_NEWLINE, "  <key>c</key>",
+                          PLIST_NEWLINE, "  <integer>2</integer>",
+                          PLIST_NEWLINE, "  <key>d.e.f</key>",
+                          PLIST_NEWLINE, "  <dict>",
+                          PLIST_NEWLINE, "   <key>g.h.i.j</key>",
+                          PLIST_NEWLINE, "   <integer>1</integer>",
+                          PLIST_NEWLINE, "  </dict>",
+                          PLIST_NEWLINE, " </dict>",
+                          PLIST_NEWLINE, kPlistFooter}),
+            output_plist);
+}
+
+}  // namespace policy
diff --git a/components/policy/core/common/policy_bundle.cc b/components/policy/core/common/policy_bundle.cc
new file mode 100644
index 0000000..3d9d158
--- /dev/null
+++ b/components/policy/core/common/policy_bundle.cc
@@ -0,0 +1,104 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/policy/core/common/policy_bundle.h"
+
+#include "base/logging.h"
+
+namespace policy {
+
+PolicyBundle::PolicyBundle() {}
+
+PolicyBundle::~PolicyBundle() {
+  Clear();
+}
+
+PolicyMap& PolicyBundle::Get(const PolicyNamespace& ns) {
+  DCHECK(ns.domain != POLICY_DOMAIN_CHROME || ns.component_id.empty());
+  std::unique_ptr<PolicyMap>& policy = policy_bundle_[ns];
+  if (!policy)
+    policy = std::make_unique<PolicyMap>();
+  return *policy;
+}
+
+const PolicyMap& PolicyBundle::Get(const PolicyNamespace& ns) const {
+  DCHECK(ns.domain != POLICY_DOMAIN_CHROME || ns.component_id.empty());
+  const_iterator it = policy_bundle_.find(ns);
+  return it == end() ? kEmpty_ : *it->second;
+}
+
+void PolicyBundle::Swap(PolicyBundle* other) {
+  policy_bundle_.swap(other->policy_bundle_);
+}
+
+void PolicyBundle::CopyFrom(const PolicyBundle& other) {
+  Clear();
+  for (auto it = other.begin(); it != other.end(); ++it) {
+    policy_bundle_[it->first] = it->second->DeepCopy();
+  }
+}
+
+void PolicyBundle::MergeFrom(const PolicyBundle& other) {
+  // Iterate over both |this| and |other| in order; skip what's extra in |this|,
+  // add what's missing, and merge the namespaces in common.
+  MapType::iterator it_this = policy_bundle_.begin();
+  MapType::iterator end_this = policy_bundle_.end();
+  const_iterator it_other = other.begin();
+  const_iterator end_other = other.end();
+
+  while (it_this != end_this && it_other != end_other) {
+    if (it_this->first == it_other->first) {
+      // Same namespace: merge existing PolicyMaps.
+      it_this->second->MergeFrom(*it_other->second);
+      ++it_this;
+      ++it_other;
+    } else if (it_this->first < it_other->first) {
+      // |this| has a PolicyMap that |other| doesn't; skip it.
+      ++it_this;
+    } else if (it_other->first < it_this->first) {
+      // |other| has a PolicyMap that |this| doesn't; copy it.
+      policy_bundle_[it_other->first] = it_other->second->DeepCopy();
+      ++it_other;
+    } else {
+      NOTREACHED();
+    }
+  }
+
+  // Add extra PolicyMaps at the end.
+  while (it_other != end_other) {
+    policy_bundle_[it_other->first] = it_other->second->DeepCopy();
+    ++it_other;
+  }
+}
+
+bool PolicyBundle::Equals(const PolicyBundle& other) const {
+  // Equals() has the peculiarity that an entry with an empty PolicyMap equals
+  // an non-existent entry. This handles usage of non-const Get() that doesn't
+  // insert any policies.
+  const_iterator it_this = begin();
+  const_iterator it_other = other.begin();
+
+  while (true) {
+    // Skip empty PolicyMaps.
+    while (it_this != end() && it_this->second->empty())
+      ++it_this;
+    while (it_other != other.end() && it_other->second->empty())
+      ++it_other;
+    if (it_this == end() || it_other == other.end())
+      break;
+    if (it_this->first != it_other->first ||
+        !it_this->second->Equals(*it_other->second)) {
+      return false;
+    }
+    ++it_this;
+    ++it_other;
+  }
+  return it_this == end() && it_other == other.end();
+}
+
+void PolicyBundle::Clear() {
+  policy_bundle_.clear();
+}
+
+}  // namespace policy
diff --git a/components/policy/core/common/policy_bundle.h b/components/policy/core/common/policy_bundle.h
new file mode 100644
index 0000000..8a39532
--- /dev/null
+++ b/components/policy/core/common/policy_bundle.h
@@ -0,0 +1,74 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_POLICY_CORE_COMMON_POLICY_BUNDLE_H_
+#define COMPONENTS_POLICY_CORE_COMMON_POLICY_BUNDLE_H_
+
+#include <map>
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+#include "components/policy/core/common/policy_map.h"
+#include "components/policy/core/common/policy_namespace.h"
+#include "components/policy/policy_export.h"
+
+namespace policy {
+
+// Maps policy namespaces to PolicyMaps.
+class POLICY_EXPORT PolicyBundle {
+ public:
+  using MapType = std::map<PolicyNamespace, std::unique_ptr<PolicyMap>>;
+  using iterator = MapType::iterator;
+  using const_iterator = MapType::const_iterator;
+
+  PolicyBundle();
+  virtual ~PolicyBundle();
+
+  // Returns the PolicyMap for namespace |ns|. Creates a new map if necessary.
+  PolicyMap& Get(const PolicyNamespace& ns);
+  const PolicyMap& Get(const PolicyNamespace& ns) const;
+
+  // Swaps the internal representation of |this| with |other|.
+  void Swap(PolicyBundle* other);
+
+  // |this| becomes a copy of |other|. Any existing PolicyMaps are dropped.
+  void CopyFrom(const PolicyBundle& other);
+
+  // Merges the PolicyMaps of |this| with those of |other| for each namespace
+  // in common. Also adds copies of the (namespace, PolicyMap) pairs in |other|
+  // that don't have an entry in |this|.
+  // Each policy in each PolicyMap is replaced only if the policy from |other|
+  // has a higher priority.
+  // See PolicyMap::MergeFrom for details on merging individual PolicyMaps.
+  void MergeFrom(const PolicyBundle& other);
+
+  // Returns true if |other| has the same keys and value as |this|.
+  bool Equals(const PolicyBundle& other) const;
+
+  // Returns iterators to the beginning and end of the underlying container.
+  iterator begin() { return policy_bundle_.begin(); }
+  iterator end() { return policy_bundle_.end(); }
+
+  // These can be used to iterate over and read the PolicyMaps, but not to
+  // modify them.
+  const_iterator begin() const { return policy_bundle_.begin(); }
+  const_iterator end() const { return policy_bundle_.end(); }
+
+  // Erases all the existing pairs.
+  void Clear();
+
+ private:
+  MapType policy_bundle_;
+
+  // An empty PolicyMap that is returned by const Get() for namespaces that
+  // do not exist in |policy_bundle_|.
+  const PolicyMap kEmpty_;
+
+  DISALLOW_COPY_AND_ASSIGN(PolicyBundle);
+};
+
+}  // namespace policy
+
+#endif  // COMPONENTS_POLICY_CORE_COMMON_POLICY_BUNDLE_H_
diff --git a/components/policy/core/common/policy_bundle_unittest.cc b/components/policy/core/common/policy_bundle_unittest.cc
new file mode 100644
index 0000000..7ac3bd1
--- /dev/null
+++ b/components/policy/core/common/policy_bundle_unittest.cc
@@ -0,0 +1,263 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/policy/core/common/policy_bundle.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/callback.h"
+#include "base/values.h"
+#include "components/policy/core/common/external_data_fetcher.h"
+#include "components/policy/core/common/policy_map.h"
+#include "components/policy/core/common/policy_types.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace policy {
+
+namespace {
+
+const char kPolicyClashing0[] = "policy-clashing-0";
+const char kPolicyClashing1[] = "policy-clashing-1";
+const char kPolicy0[] = "policy-0";
+const char kPolicy1[] = "policy-1";
+const char kPolicy2[] = "policy-2";
+const char kExtension0[] = "extension-0";
+const char kExtension1[] = "extension-1";
+const char kExtension2[] = "extension-2";
+const char kExtension3[] = "extension-3";
+
+// Adds test policies to |policy|.
+void AddTestPolicies(PolicyMap* policy) {
+  policy->Set("mandatory-user", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+              POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(123), nullptr);
+  policy->Set("mandatory-machine", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE,
+              POLICY_SOURCE_CLOUD, std::make_unique<base::Value>("omg"),
+              nullptr);
+  policy->Set("recommended-user", POLICY_LEVEL_RECOMMENDED, POLICY_SCOPE_USER,
+              POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(true),
+              nullptr);
+  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
+  dict->SetBoolean("false", false);
+  dict->SetInteger("int", 456);
+  dict->SetString("str", "bbq");
+  policy->Set("recommended-machine", POLICY_LEVEL_RECOMMENDED,
+              POLICY_SCOPE_MACHINE, POLICY_SOURCE_CLOUD, std::move(dict),
+              nullptr);
+}
+
+// Adds test policies to |policy| based on the parameters:
+// - kPolicyClashing0 mapped to |value|, user mandatory
+// - kPolicyClashing1 mapped to |value|, with |level| and |scope|
+// - |name| mapped to |value|, user mandatory
+void AddTestPoliciesWithParams(PolicyMap *policy,
+                               const char* name,
+                               int value,
+                               PolicyLevel level,
+                               PolicyScope scope) {
+  policy->Set(kPolicyClashing0, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+              POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(value),
+              nullptr);
+  policy->Set(kPolicyClashing1, level, scope, POLICY_SOURCE_CLOUD,
+              std::make_unique<base::Value>(value), nullptr);
+  policy->Set(name, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+              POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(value),
+              nullptr);
+}
+
+// Returns true if |bundle| is empty.
+bool IsEmpty(const PolicyBundle& bundle) {
+  return bundle.begin() == bundle.end();
+}
+
+}  // namespace
+
+TEST(PolicyBundleTest, Get) {
+  PolicyBundle bundle;
+  EXPECT_TRUE(IsEmpty(bundle));
+
+  AddTestPolicies(&bundle.Get(
+      PolicyNamespace(POLICY_DOMAIN_CHROME, std::string())));
+  EXPECT_FALSE(IsEmpty(bundle));
+
+  PolicyMap policy;
+  AddTestPolicies(&policy);
+  EXPECT_TRUE(bundle.Get(PolicyNamespace(POLICY_DOMAIN_CHROME,
+                                         std::string())).Equals(policy));
+
+  PolicyBundle::const_iterator it = bundle.begin();
+  ASSERT_TRUE(it != bundle.end());
+  EXPECT_EQ(POLICY_DOMAIN_CHROME, it->first.domain);
+  EXPECT_EQ("", it->first.component_id);
+  ASSERT_TRUE(it->second);
+  EXPECT_TRUE(it->second->Equals(policy));
+  ++it;
+  EXPECT_TRUE(it == bundle.end());
+  EXPECT_TRUE(bundle.Get(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS,
+                                         kExtension0)).empty());
+
+  EXPECT_FALSE(IsEmpty(bundle));
+  bundle.Clear();
+  EXPECT_TRUE(IsEmpty(bundle));
+}
+
+TEST(PolicyBundleTest, SwapAndCopy) {
+  PolicyBundle bundle0;
+  PolicyBundle bundle1;
+
+  AddTestPolicies(&bundle0.Get(
+      PolicyNamespace(POLICY_DOMAIN_CHROME, std::string())));
+  AddTestPolicies(&bundle0.Get(
+      PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, kExtension0)));
+  EXPECT_FALSE(IsEmpty(bundle0));
+  EXPECT_TRUE(IsEmpty(bundle1));
+
+  PolicyMap policy;
+  AddTestPolicies(&policy);
+  EXPECT_TRUE(bundle0.Get(PolicyNamespace(POLICY_DOMAIN_CHROME,
+                                          std::string())).Equals(policy));
+  EXPECT_TRUE(bundle0.Get(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS,
+                                          kExtension0)).Equals(policy));
+
+  bundle0.Swap(&bundle1);
+  EXPECT_TRUE(IsEmpty(bundle0));
+  EXPECT_FALSE(IsEmpty(bundle1));
+
+  EXPECT_TRUE(bundle1.Get(PolicyNamespace(POLICY_DOMAIN_CHROME,
+                                          std::string())).Equals(policy));
+  EXPECT_TRUE(bundle1.Get(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS,
+                                          kExtension0)).Equals(policy));
+
+  bundle0.CopyFrom(bundle1);
+  EXPECT_FALSE(IsEmpty(bundle0));
+  EXPECT_FALSE(IsEmpty(bundle1));
+  EXPECT_TRUE(bundle0.Get(PolicyNamespace(POLICY_DOMAIN_CHROME,
+                                          std::string())).Equals(policy));
+  EXPECT_TRUE(bundle0.Get(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS,
+                                          kExtension0)).Equals(policy));
+}
+
+TEST(PolicyBundleTest, MergeFrom) {
+  // Each bundleN has kExtensionN. Each bundle also has policy for
+  // chrome and kExtension3.
+  // |bundle0| has the highest priority, |bundle2| the lowest.
+  PolicyBundle bundle0;
+  PolicyBundle bundle1;
+  PolicyBundle bundle2;
+
+  PolicyMap policy0;
+  AddTestPoliciesWithParams(
+      &policy0, kPolicy0, 0u, POLICY_LEVEL_RECOMMENDED, POLICY_SCOPE_USER);
+  bundle0.Get(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()))
+      .CopyFrom(policy0);
+  bundle0.Get(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, kExtension0))
+      .CopyFrom(policy0);
+  bundle0.Get(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, kExtension3))
+      .CopyFrom(policy0);
+
+  PolicyMap policy1;
+  AddTestPoliciesWithParams(
+      &policy1, kPolicy1, 1u, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE);
+  bundle1.Get(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()))
+      .CopyFrom(policy1);
+  bundle1.Get(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, kExtension1))
+      .CopyFrom(policy1);
+  bundle1.Get(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, kExtension3))
+      .CopyFrom(policy1);
+
+  PolicyMap policy2;
+  AddTestPoliciesWithParams(
+      &policy2, kPolicy2, 2u, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER);
+  bundle2.Get(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()))
+      .CopyFrom(policy2);
+  bundle2.Get(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, kExtension2))
+      .CopyFrom(policy2);
+  bundle2.Get(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, kExtension3))
+      .CopyFrom(policy2);
+
+  // Merge in order of decreasing priority.
+  PolicyBundle merged;
+  merged.MergeFrom(bundle0);
+  merged.MergeFrom(bundle1);
+  merged.MergeFrom(bundle2);
+  PolicyBundle empty_bundle;
+  merged.MergeFrom(empty_bundle);
+
+  // chrome and kExtension3 policies are merged:
+  // - kPolicyClashing0 comes from bundle0, which has the highest priority;
+  // - kPolicyClashing1 comes from bundle1, which has the highest level/scope
+  //   combination;
+  // - kPolicyN are merged from each bundle.
+  PolicyMap expected;
+  expected.Set(kPolicyClashing0, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+               POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(0), nullptr);
+  expected.Set(kPolicyClashing1, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE,
+               POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(1), nullptr);
+  expected.Set(kPolicy0, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+               POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(0), nullptr);
+  expected.Set(kPolicy1, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+               POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(1), nullptr);
+  expected.Set(kPolicy2, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+               POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(2), nullptr);
+  EXPECT_TRUE(merged.Get(PolicyNamespace(POLICY_DOMAIN_CHROME,
+                                         std::string())).Equals(expected));
+  EXPECT_TRUE(merged.Get(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS,
+                                         kExtension3)).Equals(expected));
+  // extension0 comes only from bundle0.
+  EXPECT_TRUE(merged.Get(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS,
+                                         kExtension0)).Equals(policy0));
+  // extension1 comes only from bundle1.
+  EXPECT_TRUE(merged.Get(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS,
+                                         kExtension1)).Equals(policy1));
+  // extension2 comes only from bundle2.
+  EXPECT_TRUE(merged.Get(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS,
+                                         kExtension2)).Equals(policy2));
+}
+
+TEST(PolicyBundleTest, Equals) {
+  PolicyBundle bundle;
+  AddTestPolicies(&bundle.Get(
+      PolicyNamespace(POLICY_DOMAIN_CHROME, std::string())));
+  AddTestPolicies(&bundle.Get(
+      PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, kExtension0)));
+
+  PolicyBundle other;
+  EXPECT_FALSE(bundle.Equals(other));
+  other.CopyFrom(bundle);
+  EXPECT_TRUE(bundle.Equals(other));
+
+  AddTestPolicies(&bundle.Get(
+      PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, kExtension1)));
+  EXPECT_FALSE(bundle.Equals(other));
+  other.CopyFrom(bundle);
+  EXPECT_TRUE(bundle.Equals(other));
+  AddTestPolicies(&other.Get(
+      PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, kExtension2)));
+  EXPECT_FALSE(bundle.Equals(other));
+
+  other.CopyFrom(bundle);
+  bundle.Get(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()))
+      .Set(kPolicy0, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+           POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(123), nullptr);
+  EXPECT_FALSE(bundle.Equals(other));
+  other.CopyFrom(bundle);
+  EXPECT_TRUE(bundle.Equals(other));
+  bundle.Get(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()))
+      .Set(kPolicy0, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE,
+           POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(123), nullptr);
+  EXPECT_FALSE(bundle.Equals(other));
+
+  // Test non-const Get().
+  bundle.Clear();
+  other.Clear();
+  PolicyMap& policy_map =
+      bundle.Get(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()));
+  EXPECT_TRUE(bundle.Equals(other));
+  policy_map.Set(kPolicy0, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+                 POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(123),
+                 nullptr);
+  EXPECT_FALSE(bundle.Equals(other));
+}
+
+}  // namespace policy
diff --git a/components/policy/core/common/policy_details.h b/components/policy/core/common/policy_details.h
new file mode 100644
index 0000000..66e26d0
--- /dev/null
+++ b/components/policy/core/common/policy_details.h
@@ -0,0 +1,48 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_POLICY_CORE_COMMON_POLICY_DETAILS_H_
+#define COMPONENTS_POLICY_CORE_COMMON_POLICY_DETAILS_H_
+
+#include <stddef.h>
+
+#include <string>
+
+#include "base/callback_forward.h"
+#include "components/policy/policy_export.h"
+#include "components/policy/risk_tag.h"
+
+namespace policy {
+
+// Contains read-only metadata about a Chrome policy.
+struct POLICY_EXPORT PolicyDetails {
+  // True if this policy has been deprecated.
+  bool is_deprecated;
+
+  // True if this policy is a Chrome OS device policy.
+  bool is_device_policy;
+
+  // The id of the protobuf field that contains this policy,
+  // in the cloud policy protobuf.
+  int id;
+
+  // If this policy references external data then this is the maximum size
+  // allowed for that data.
+  // Otherwise this field is 0 and doesn't have any meaning.
+  size_t max_external_data_size;
+
+  // Contains tags that describe impact on a user's privacy or security.
+  RiskTag risk_tags[kMaxRiskTagCount];
+};
+
+// A typedef for functions that match the signature of
+// GetChromePolicyDetails(). This can be used to inject that
+// function into objects, so that it can be easily mocked for
+// tests.
+typedef base::Callback<const PolicyDetails*(const std::string&)>
+    GetChromePolicyDetailsCallback;
+
+}  // namespace policy
+
+#endif  // COMPONENTS_POLICY_CORE_COMMON_POLICY_DETAILS_H_
diff --git a/components/policy/core/common/policy_map.cc b/components/policy/core/common/policy_map.cc
new file mode 100644
index 0000000..50750bb
--- /dev/null
+++ b/components/policy/core/common/policy_map.cc
@@ -0,0 +1,234 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/policy/core/common/policy_map.h"
+
+#include <algorithm>
+
+#include "base/callback.h"
+#include "base/stl_util.h"
+
+namespace policy {
+
+PolicyMap::Entry::Entry() = default;
+
+PolicyMap::Entry::~Entry() = default;
+
+PolicyMap::Entry::Entry(Entry&&) noexcept = default;
+PolicyMap::Entry& PolicyMap::Entry::operator=(Entry&&) noexcept = default;
+
+PolicyMap::Entry PolicyMap::Entry::DeepCopy() const {
+  Entry copy;
+  copy.level = level;
+  copy.scope = scope;
+  copy.source = source;
+  if (value)
+    copy.value = value->CreateDeepCopy();
+  copy.error = error;
+  if (external_data_fetcher) {
+    copy.external_data_fetcher.reset(
+        new ExternalDataFetcher(*external_data_fetcher));
+  }
+  return copy;
+}
+
+bool PolicyMap::Entry::has_higher_priority_than(
+    const PolicyMap::Entry& other) const {
+  if (level != other.level)
+    return level > other.level;
+
+  if (scope != other.scope)
+    return scope > other.scope;
+
+  return source > other.source;
+}
+
+bool PolicyMap::Entry::Equals(const PolicyMap::Entry& other) const {
+  return level == other.level && scope == other.scope &&
+         source == other.source &&  // Necessary for PolicyUIHandler observers.
+                                    // They have to update when sources change.
+         error == other.error &&
+         ((!value && !other.value) ||
+          (value && other.value && *value == *other.value)) &&
+         ExternalDataFetcher::Equals(external_data_fetcher.get(),
+                                     other.external_data_fetcher.get());
+}
+
+PolicyMap::PolicyMap() {}
+
+PolicyMap::~PolicyMap() {
+  Clear();
+}
+
+const PolicyMap::Entry* PolicyMap::Get(const std::string& policy) const {
+  PolicyMapType::const_iterator entry = map_.find(policy);
+  return entry == map_.end() ? nullptr : &entry->second;
+}
+
+PolicyMap::Entry* PolicyMap::GetMutable(const std::string& policy) {
+  PolicyMapType::iterator entry = map_.find(policy);
+  return entry == map_.end() ? nullptr : &entry->second;
+}
+
+const base::Value* PolicyMap::GetValue(const std::string& policy) const {
+  PolicyMapType::const_iterator entry = map_.find(policy);
+  return entry == map_.end() ? nullptr : entry->second.value.get();
+}
+
+base::Value* PolicyMap::GetMutableValue(const std::string& policy) {
+  PolicyMapType::iterator entry = map_.find(policy);
+  return entry == map_.end() ? nullptr : entry->second.value.get();
+}
+
+void PolicyMap::Set(
+    const std::string& policy,
+    PolicyLevel level,
+    PolicyScope scope,
+    PolicySource source,
+    std::unique_ptr<base::Value> value,
+    std::unique_ptr<ExternalDataFetcher> external_data_fetcher) {
+  Entry entry;
+  entry.level = level;
+  entry.scope = scope;
+  entry.source = source;
+  entry.value = std::move(value);
+  entry.external_data_fetcher = std::move(external_data_fetcher);
+  Set(policy, std::move(entry));
+}
+
+void PolicyMap::Set(const std::string& policy, Entry entry) {
+  map_[policy] = std::move(entry);
+}
+
+void PolicyMap::SetError(const std::string& policy, const std::string& error) {
+  map_[policy].error = error;
+}
+
+void PolicyMap::SetSourceForAll(PolicySource source) {
+  for (auto& it : map_) {
+    it.second.source = source;
+  }
+}
+
+void PolicyMap::Erase(const std::string& policy) {
+  map_.erase(policy);
+}
+
+void PolicyMap::EraseMatching(
+    const base::Callback<bool(const const_iterator)>& filter) {
+  FilterErase(filter, true);
+}
+
+void PolicyMap::EraseNonmatching(
+    const base::Callback<bool(const const_iterator)>& filter) {
+  FilterErase(filter, false);
+}
+
+void PolicyMap::Swap(PolicyMap* other) {
+  map_.swap(other->map_);
+}
+
+void PolicyMap::CopyFrom(const PolicyMap& other) {
+  Clear();
+  for (const auto& it : other)
+    Set(it.first, it.second.DeepCopy());
+}
+
+std::unique_ptr<PolicyMap> PolicyMap::DeepCopy() const {
+  std::unique_ptr<PolicyMap> copy(new PolicyMap());
+  copy->CopyFrom(*this);
+  return copy;
+}
+
+void PolicyMap::MergeFrom(const PolicyMap& other) {
+  for (const auto& it : other) {
+    const Entry* entry = Get(it.first);
+    if (!entry || it.second.has_higher_priority_than(*entry))
+      Set(it.first, it.second.DeepCopy());
+  }
+}
+
+void PolicyMap::LoadFrom(const base::DictionaryValue* policies,
+                         PolicyLevel level,
+                         PolicyScope scope,
+                         PolicySource source) {
+  for (base::DictionaryValue::Iterator it(*policies); !it.IsAtEnd();
+       it.Advance()) {
+    Set(it.key(), level, scope, source, it.value().CreateDeepCopy(), nullptr);
+  }
+}
+
+void PolicyMap::GetDifferingKeys(const PolicyMap& other,
+                                 std::set<std::string>* differing_keys) const {
+  // Walk over the maps in lockstep, adding everything that is different.
+  const_iterator iter_this(begin());
+  const_iterator iter_other(other.begin());
+  while (iter_this != end() && iter_other != other.end()) {
+    const int diff = iter_this->first.compare(iter_other->first);
+    if (diff == 0) {
+      if (!iter_this->second.Equals(iter_other->second))
+        differing_keys->insert(iter_this->first);
+      ++iter_this;
+      ++iter_other;
+    } else if (diff < 0) {
+      differing_keys->insert(iter_this->first);
+      ++iter_this;
+    } else {
+      differing_keys->insert(iter_other->first);
+      ++iter_other;
+    }
+  }
+
+  // Add the remaining entries.
+  for (; iter_this != end(); ++iter_this)
+    differing_keys->insert(iter_this->first);
+  for (; iter_other != other.end(); ++iter_other)
+    differing_keys->insert(iter_other->first);
+}
+
+bool PolicyMap::Equals(const PolicyMap& other) const {
+  return other.size() == size() &&
+         std::equal(begin(), end(), other.begin(), MapEntryEquals);
+}
+
+bool PolicyMap::empty() const {
+  return map_.empty();
+}
+
+size_t PolicyMap::size() const {
+  return map_.size();
+}
+
+PolicyMap::const_iterator PolicyMap::begin() const {
+  return map_.begin();
+}
+
+PolicyMap::const_iterator PolicyMap::end() const {
+  return map_.end();
+}
+
+void PolicyMap::Clear() {
+  map_.clear();
+}
+
+// static
+bool PolicyMap::MapEntryEquals(const PolicyMap::PolicyMapType::value_type& a,
+                               const PolicyMap::PolicyMapType::value_type& b) {
+  return a.first == b.first && a.second.Equals(b.second);
+}
+
+void PolicyMap::FilterErase(
+    const base::Callback<bool(const const_iterator)>& filter,
+    bool deletion_value) {
+  PolicyMapType::iterator iter(map_.begin());
+  while (iter != map_.end()) {
+    if (filter.Run(iter) == deletion_value) {
+      map_.erase(iter++);
+    } else {
+      ++iter;
+    }
+  }
+}
+
+}  // namespace policy
diff --git a/components/policy/core/common/policy_map.h b/components/policy/core/common/policy_map.h
new file mode 100644
index 0000000..0f0ef09
--- /dev/null
+++ b/components/policy/core/common/policy_map.h
@@ -0,0 +1,157 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_POLICY_CORE_COMMON_POLICY_MAP_H_
+#define COMPONENTS_POLICY_CORE_COMMON_POLICY_MAP_H_
+
+#include <stddef.h>
+
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/values.h"
+#include "components/policy/core/common/external_data_fetcher.h"
+#include "components/policy/core/common/policy_types.h"
+#include "components/policy/policy_export.h"
+
+namespace policy {
+
+// A mapping of policy names to policy values for a given policy namespace.
+class POLICY_EXPORT PolicyMap {
+ public:
+  // Each policy maps to an Entry which keeps the policy value as well as other
+  // relevant data about the policy.
+  struct POLICY_EXPORT Entry {
+    PolicyLevel level = POLICY_LEVEL_RECOMMENDED;
+    PolicyScope scope = POLICY_SCOPE_USER;
+    std::unique_ptr<base::Value> value;
+    std::string error;
+    std::unique_ptr<ExternalDataFetcher> external_data_fetcher;
+
+    // For debugging and displaying only. Set by provider delivering the policy.
+    PolicySource source = POLICY_SOURCE_ENTERPRISE_DEFAULT;
+
+    Entry();
+    ~Entry();
+
+    Entry(Entry&&) noexcept;
+    Entry& operator=(Entry&&) noexcept;
+
+    // Returns a copy of |this|.
+    Entry DeepCopy() const;
+
+    // Returns true if |this| has higher priority than |other|. The priority of
+    // the fields are |level| > |scope| > |source|.
+    bool has_higher_priority_than(const Entry& other) const;
+
+    // Returns true if |this| equals |other|.
+    bool Equals(const Entry& other) const;
+  };
+
+  typedef std::map<std::string, Entry> PolicyMapType;
+  typedef PolicyMapType::const_iterator const_iterator;
+
+  PolicyMap();
+  virtual ~PolicyMap();
+
+  // Returns a weak reference to the entry currently stored for key |policy|,
+  // or NULL if not found. Ownership is retained by the PolicyMap.
+  const Entry* Get(const std::string& policy) const;
+  Entry* GetMutable(const std::string& policy);
+
+  // Returns a weak reference to the value currently stored for key
+  // |policy|, or NULL if not found. Ownership is retained by the PolicyMap.
+  // This is equivalent to Get(policy)->value, when it doesn't return NULL.
+  const base::Value* GetValue(const std::string& policy) const;
+  base::Value* GetMutableValue(const std::string& policy);
+
+  // Overwrites any existing information stored in the map for the key |policy|.
+  // Resets the error for that policy to the empty string.
+  void Set(const std::string& policy,
+           PolicyLevel level,
+           PolicyScope scope,
+           PolicySource source,
+           std::unique_ptr<base::Value> value,
+           std::unique_ptr<ExternalDataFetcher> external_data_fetcher);
+
+  void Set(const std::string& policy, Entry entry);
+
+  // Adds an |error| to the map for the key |policy| that should be shown to the
+  // user alongside the value in the policy UI. This is equivalent to calling
+  // |GetMutableValue(policy)->error = error|, so should only be called for
+  // policies that are already stored in this map.
+  void SetError(const std::string& policy, const std::string& error);
+
+  // For all policies, overwrite the PolicySource with |source|.
+  void SetSourceForAll(PolicySource source);
+
+  // Erase the given |policy|, if it exists in this map.
+  void Erase(const std::string& policy);
+
+  // Erase all entries for which |filter| returns true.
+  void EraseMatching(const base::Callback<bool(const const_iterator)>& filter);
+
+  // Erase all entries for which |filter| returns false.
+  void EraseNonmatching(
+      const base::Callback<bool(const const_iterator)>& filter);
+
+  // Swaps the internal representation of |this| with |other|.
+  void Swap(PolicyMap* other);
+
+  // |this| becomes a copy of |other|. Any existing policies are dropped.
+  void CopyFrom(const PolicyMap& other);
+
+  // Returns a copy of |this|.
+  std::unique_ptr<PolicyMap> DeepCopy() const;
+
+  // Merges policies from |other| into |this|. Existing policies are only
+  // overridden by those in |other| if they have a higher priority, as defined
+  // by Entry::has_higher_priority_than(). If a policy is contained in both
+  // maps with the same priority, the current value in |this| is preserved.
+  void MergeFrom(const PolicyMap& other);
+
+  // Loads the values in |policies| into this PolicyMap. All policies loaded
+  // will have |level|, |scope| and |source| in their entries. Existing entries
+  // are replaced.
+  void LoadFrom(const base::DictionaryValue* policies,
+                PolicyLevel level,
+                PolicyScope scope,
+                PolicySource source);
+
+  // Compares this value map against |other| and stores all key names that have
+  // different values or reference different external data in |differing_keys|.
+  // This includes keys that are present only in one of the maps.
+  // |differing_keys| is not cleared before the keys are added.
+  void GetDifferingKeys(const PolicyMap& other,
+                        std::set<std::string>* differing_keys) const;
+
+  bool Equals(const PolicyMap& other) const;
+  bool empty() const;
+  size_t size() const;
+
+  const_iterator begin() const;
+  const_iterator end() const;
+  void Clear();
+
+ private:
+  // Helper function for Equals().
+  static bool MapEntryEquals(const PolicyMapType::value_type& a,
+                             const PolicyMapType::value_type& b);
+
+  // Erase all entries for which |filter| returns |deletion_value|.
+  void FilterErase(const base::Callback<bool(const const_iterator)>& filter,
+                   bool deletion_value);
+
+  PolicyMapType map_;
+
+  DISALLOW_COPY_AND_ASSIGN(PolicyMap);
+};
+
+}  // namespace policy
+
+#endif  // COMPONENTS_POLICY_CORE_COMMON_POLICY_MAP_H_
diff --git a/components/policy/core/common/policy_map_unittest.cc b/components/policy/core/common/policy_map_unittest.cc
new file mode 100644
index 0000000..691bd21
--- /dev/null
+++ b/components/policy/core/common/policy_map_unittest.cc
@@ -0,0 +1,354 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/policy/core/common/policy_map.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/memory/weak_ptr.h"
+#include "components/policy/core/common/external_data_manager.h"
+#include "components/policy/core/common/policy_types.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace policy {
+
+namespace {
+
+// Dummy policy names.
+const char kTestPolicyName1[] = "policy.test.1";
+const char kTestPolicyName2[] = "policy.test.2";
+const char kTestPolicyName3[] = "policy.test.3";
+const char kTestPolicyName4[] = "policy.test.4";
+const char kTestPolicyName5[] = "policy.test.5";
+const char kTestPolicyName6[] = "policy.test.6";
+const char kTestPolicyName7[] = "policy.test.7";
+const char kTestPolicyName8[] = "policy.test.8";
+
+// Dummy error message.
+const char kTestError[] = "Test error message";
+
+// Utility functions for the tests.
+void SetPolicy(PolicyMap* map,
+               const char* name,
+               std::unique_ptr<base::Value> value) {
+  map->Set(name, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD,
+           std::move(value), nullptr);
+}
+
+void SetPolicy(PolicyMap* map,
+               const char* name,
+               std::unique_ptr<ExternalDataFetcher> external_data_fetcher) {
+  map->Set(name, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD,
+           nullptr, std::move(external_data_fetcher));
+}
+
+}  // namespace
+
+class PolicyMapTest : public testing::Test {
+ protected:
+  std::unique_ptr<ExternalDataFetcher> CreateExternalDataFetcher(
+      const std::string& policy) const;
+};
+
+std::unique_ptr<ExternalDataFetcher> PolicyMapTest::CreateExternalDataFetcher(
+    const std::string& policy) const {
+  return std::make_unique<ExternalDataFetcher>(
+      base::WeakPtr<ExternalDataManager>(), policy);
+}
+
+TEST_F(PolicyMapTest, SetAndGet) {
+  PolicyMap map;
+  SetPolicy(&map, kTestPolicyName1, std::make_unique<base::Value>("aaa"));
+  base::Value expected("aaa");
+  EXPECT_TRUE(expected.Equals(map.GetValue(kTestPolicyName1)));
+  SetPolicy(&map, kTestPolicyName1, std::make_unique<base::Value>("bbb"));
+  base::Value expected_b("bbb");
+  EXPECT_TRUE(expected_b.Equals(map.GetValue(kTestPolicyName1)));
+  SetPolicy(&map, kTestPolicyName1, CreateExternalDataFetcher("dummy"));
+  map.SetError(kTestPolicyName1, kTestError);
+  EXPECT_FALSE(map.GetValue(kTestPolicyName1));
+  const PolicyMap::Entry* entry = map.Get(kTestPolicyName1);
+  ASSERT_TRUE(entry != nullptr);
+  EXPECT_EQ(POLICY_LEVEL_MANDATORY, entry->level);
+  EXPECT_EQ(POLICY_SCOPE_USER, entry->scope);
+  EXPECT_EQ(POLICY_SOURCE_CLOUD, entry->source);
+  EXPECT_EQ(kTestError, entry->error);
+  EXPECT_TRUE(
+      ExternalDataFetcher::Equals(entry->external_data_fetcher.get(),
+                                  CreateExternalDataFetcher("dummy").get()));
+  map.Set(kTestPolicyName1, POLICY_LEVEL_RECOMMENDED, POLICY_SCOPE_MACHINE,
+          POLICY_SOURCE_ENTERPRISE_DEFAULT, nullptr, nullptr);
+  EXPECT_FALSE(map.GetValue(kTestPolicyName1));
+  entry = map.Get(kTestPolicyName1);
+  ASSERT_TRUE(entry != nullptr);
+  EXPECT_EQ(POLICY_LEVEL_RECOMMENDED, entry->level);
+  EXPECT_EQ(POLICY_SCOPE_MACHINE, entry->scope);
+  EXPECT_EQ(POLICY_SOURCE_ENTERPRISE_DEFAULT, entry->source);
+  EXPECT_EQ("", entry->error);
+  EXPECT_FALSE(entry->external_data_fetcher);
+}
+
+TEST_F(PolicyMapTest, Equals) {
+  PolicyMap a;
+  SetPolicy(&a, kTestPolicyName1, std::make_unique<base::Value>("aaa"));
+  PolicyMap a2;
+  SetPolicy(&a2, kTestPolicyName1, std::make_unique<base::Value>("aaa"));
+  PolicyMap b;
+  SetPolicy(&b, kTestPolicyName1, std::make_unique<base::Value>("bbb"));
+  PolicyMap c;
+  SetPolicy(&c, kTestPolicyName1, std::make_unique<base::Value>("aaa"));
+  SetPolicy(&c, kTestPolicyName2, std::make_unique<base::Value>(true));
+  PolicyMap d;
+  SetPolicy(&d, kTestPolicyName1, CreateExternalDataFetcher("ddd"));
+  PolicyMap d2;
+  SetPolicy(&d2, kTestPolicyName1, CreateExternalDataFetcher("ddd"));
+  PolicyMap e;
+  SetPolicy(&e, kTestPolicyName1, CreateExternalDataFetcher("eee"));
+  EXPECT_FALSE(a.Equals(b));
+  EXPECT_FALSE(a.Equals(c));
+  EXPECT_FALSE(a.Equals(d));
+  EXPECT_FALSE(a.Equals(e));
+  EXPECT_FALSE(b.Equals(a));
+  EXPECT_FALSE(b.Equals(c));
+  EXPECT_FALSE(b.Equals(d));
+  EXPECT_FALSE(b.Equals(e));
+  EXPECT_FALSE(c.Equals(a));
+  EXPECT_FALSE(c.Equals(b));
+  EXPECT_FALSE(c.Equals(d));
+  EXPECT_FALSE(c.Equals(e));
+  EXPECT_FALSE(d.Equals(a));
+  EXPECT_FALSE(d.Equals(b));
+  EXPECT_FALSE(d.Equals(c));
+  EXPECT_FALSE(d.Equals(e));
+  EXPECT_FALSE(e.Equals(a));
+  EXPECT_FALSE(e.Equals(b));
+  EXPECT_FALSE(e.Equals(c));
+  EXPECT_FALSE(e.Equals(d));
+  EXPECT_TRUE(a.Equals(a2));
+  EXPECT_TRUE(a2.Equals(a));
+  EXPECT_TRUE(d.Equals(d2));
+  EXPECT_TRUE(d2.Equals(d));
+  PolicyMap empty1;
+  PolicyMap empty2;
+  EXPECT_TRUE(empty1.Equals(empty2));
+  EXPECT_TRUE(empty2.Equals(empty1));
+  EXPECT_FALSE(empty1.Equals(a));
+  EXPECT_FALSE(a.Equals(empty1));
+}
+
+TEST_F(PolicyMapTest, Swap) {
+  PolicyMap a;
+  SetPolicy(&a, kTestPolicyName1, std::make_unique<base::Value>("aaa"));
+  SetPolicy(&a, kTestPolicyName2, CreateExternalDataFetcher("dummy"));
+  PolicyMap b;
+  SetPolicy(&b, kTestPolicyName1, std::make_unique<base::Value>("bbb"));
+  SetPolicy(&b, kTestPolicyName3, std::make_unique<base::Value>(true));
+
+  a.Swap(&b);
+  base::Value expected("bbb");
+  EXPECT_TRUE(expected.Equals(a.GetValue(kTestPolicyName1)));
+  base::Value expected_bool(true);
+  EXPECT_TRUE(expected_bool.Equals(a.GetValue(kTestPolicyName3)));
+  EXPECT_FALSE(a.GetValue(kTestPolicyName2));
+  EXPECT_FALSE(a.Get(kTestPolicyName2));
+  base::Value expected_a("aaa");
+  EXPECT_TRUE(expected_a.Equals(b.GetValue(kTestPolicyName1)));
+  EXPECT_FALSE(b.GetValue(kTestPolicyName3));
+  EXPECT_FALSE(a.GetValue(kTestPolicyName2));
+  const PolicyMap::Entry* entry = b.Get(kTestPolicyName2);
+  ASSERT_TRUE(entry);
+  EXPECT_TRUE(
+      ExternalDataFetcher::Equals(CreateExternalDataFetcher("dummy").get(),
+                                  entry->external_data_fetcher.get()));
+
+  b.Clear();
+  a.Swap(&b);
+  PolicyMap empty;
+  EXPECT_TRUE(a.Equals(empty));
+  EXPECT_FALSE(b.Equals(empty));
+}
+
+TEST_F(PolicyMapTest, MergeFrom) {
+  PolicyMap a;
+  a.Set(kTestPolicyName1, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+        POLICY_SOURCE_CLOUD, std::make_unique<base::Value>("google.com"),
+        nullptr);
+  a.Set(kTestPolicyName2, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE,
+        POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(true), nullptr);
+  a.Set(kTestPolicyName3, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE,
+        POLICY_SOURCE_ENTERPRISE_DEFAULT, nullptr,
+        CreateExternalDataFetcher("a"));
+  a.Set(kTestPolicyName4, POLICY_LEVEL_RECOMMENDED, POLICY_SCOPE_USER,
+        POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(false), nullptr);
+  a.Set(kTestPolicyName5, POLICY_LEVEL_RECOMMENDED, POLICY_SCOPE_MACHINE,
+        POLICY_SOURCE_CLOUD, std::make_unique<base::Value>("google.com/q={x}"),
+        nullptr);
+  a.Set(kTestPolicyName7, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+        POLICY_SOURCE_ENTERPRISE_DEFAULT, std::make_unique<base::Value>(false),
+        nullptr);
+
+  PolicyMap b;
+  b.Set(kTestPolicyName1, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE,
+        POLICY_SOURCE_CLOUD, std::make_unique<base::Value>("chromium.org"),
+        nullptr);
+  b.Set(kTestPolicyName2, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE,
+        POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(false), nullptr);
+  b.Set(kTestPolicyName3, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE,
+        POLICY_SOURCE_ENTERPRISE_DEFAULT, nullptr,
+        CreateExternalDataFetcher("b"));
+  b.Set(kTestPolicyName4, POLICY_LEVEL_RECOMMENDED, POLICY_SCOPE_MACHINE,
+        POLICY_SOURCE_PUBLIC_SESSION_OVERRIDE,
+        std::make_unique<base::Value>(true), nullptr);
+  b.Set(kTestPolicyName5, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE,
+        POLICY_SOURCE_PLATFORM, std::make_unique<base::Value>(std::string()),
+        nullptr);
+  b.Set(kTestPolicyName6, POLICY_LEVEL_RECOMMENDED, POLICY_SCOPE_USER,
+        POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(true), nullptr);
+  b.Set(kTestPolicyName7, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+        POLICY_SOURCE_ACTIVE_DIRECTORY, std::make_unique<base::Value>(true),
+        nullptr);
+
+  a.MergeFrom(b);
+
+  PolicyMap c;
+  // POLICY_SCOPE_MACHINE over POLICY_SCOPE_USER.
+  c.Set(kTestPolicyName1, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE,
+        POLICY_SOURCE_CLOUD, std::make_unique<base::Value>("chromium.org"),
+        nullptr);
+  // |a| has precedence over |b|.
+  c.Set(kTestPolicyName2, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE,
+        POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(true), nullptr);
+  c.Set(kTestPolicyName3, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE,
+        POLICY_SOURCE_ENTERPRISE_DEFAULT, nullptr,
+        CreateExternalDataFetcher("a"));
+  // POLICY_SCOPE_MACHINE over POLICY_SCOPE_USER for POLICY_LEVEL_RECOMMENDED.
+  c.Set(kTestPolicyName4, POLICY_LEVEL_RECOMMENDED, POLICY_SCOPE_MACHINE,
+        POLICY_SOURCE_PUBLIC_SESSION_OVERRIDE,
+        std::make_unique<base::Value>(true), nullptr);
+  // POLICY_LEVEL_MANDATORY over POLICY_LEVEL_RECOMMENDED.
+  c.Set(kTestPolicyName5, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE,
+        POLICY_SOURCE_PLATFORM, std::make_unique<base::Value>(std::string()),
+        nullptr);
+  // Merge new ones.
+  c.Set(kTestPolicyName6, POLICY_LEVEL_RECOMMENDED, POLICY_SCOPE_USER,
+        POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(true), nullptr);
+  // POLICY_SOURCE_ACTIVE_DIRECTORY over POLICY_SOURCE_ENTERPRISE_DEFAULT.
+  c.Set(kTestPolicyName7, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+        POLICY_SOURCE_ACTIVE_DIRECTORY, std::make_unique<base::Value>(true),
+        nullptr);
+
+  EXPECT_TRUE(a.Equals(c));
+}
+
+TEST_F(PolicyMapTest, GetDifferingKeys) {
+  PolicyMap a;
+  a.Set(kTestPolicyName1, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+        POLICY_SOURCE_CLOUD, std::make_unique<base::Value>("google.com"),
+        nullptr);
+  a.Set(kTestPolicyName2, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE,
+        POLICY_SOURCE_CLOUD, nullptr, CreateExternalDataFetcher("dummy"));
+  a.Set(kTestPolicyName3, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE,
+        POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(true), nullptr);
+  a.Set(kTestPolicyName4, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE,
+        POLICY_SOURCE_CLOUD, nullptr, CreateExternalDataFetcher("a"));
+  a.Set(kTestPolicyName5, POLICY_LEVEL_RECOMMENDED, POLICY_SCOPE_USER,
+        POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(false), nullptr);
+  a.Set(kTestPolicyName6, POLICY_LEVEL_RECOMMENDED, POLICY_SCOPE_MACHINE,
+        POLICY_SOURCE_CLOUD, std::make_unique<base::Value>("google.com/q={x}"),
+        nullptr);
+  a.Set(kTestPolicyName7, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+        POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(true), nullptr);
+
+  PolicyMap b;
+  b.Set(kTestPolicyName1, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+        POLICY_SOURCE_CLOUD, std::make_unique<base::Value>("google.com"),
+        nullptr);
+  b.Set(kTestPolicyName2, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE,
+        POLICY_SOURCE_CLOUD, nullptr, CreateExternalDataFetcher("dummy"));
+  b.Set(kTestPolicyName3, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE,
+        POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(false), nullptr);
+  b.Set(kTestPolicyName4, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE,
+        POLICY_SOURCE_CLOUD, nullptr, CreateExternalDataFetcher("b"));
+  b.Set(kTestPolicyName5, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+        POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(false), nullptr);
+  b.Set(kTestPolicyName6, POLICY_LEVEL_RECOMMENDED, POLICY_SCOPE_USER,
+        POLICY_SOURCE_CLOUD, std::make_unique<base::Value>("google.com/q={x}"),
+        nullptr);
+  b.Set(kTestPolicyName8, POLICY_LEVEL_RECOMMENDED, POLICY_SCOPE_USER,
+        POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(true), nullptr);
+
+  std::set<std::string> diff;
+  std::set<std::string> diff2;
+  a.GetDifferingKeys(b, &diff);
+  b.GetDifferingKeys(a, &diff2);
+  // Order shouldn't matter.
+  EXPECT_EQ(diff, diff2);
+  // No change.
+  EXPECT_TRUE(diff.find(kTestPolicyName1) == diff.end());
+  EXPECT_TRUE(diff.find(kTestPolicyName2) == diff.end());
+  // Different values.
+  EXPECT_TRUE(diff.find(kTestPolicyName3) != diff.end());
+  // Different external data references.
+  EXPECT_TRUE(diff.find(kTestPolicyName4) != diff.end());
+  // Different levels.
+  EXPECT_TRUE(diff.find(kTestPolicyName5) != diff.end());
+  // Different scopes.
+  EXPECT_TRUE(diff.find(kTestPolicyName6) != diff.end());
+  // Not in |a|.
+  EXPECT_TRUE(diff.find(kTestPolicyName8) != diff.end());
+  // Not in |b|.
+  EXPECT_TRUE(diff.find(kTestPolicyName7) != diff.end());
+  // No surprises.
+  EXPECT_EQ(6u, diff.size());
+}
+
+TEST_F(PolicyMapTest, LoadFromSetsLevelScopeAndSource) {
+  base::DictionaryValue policies;
+  policies.SetString("TestPolicy1", "google.com");
+  policies.SetBoolean("TestPolicy2", true);
+  policies.SetInteger("TestPolicy3", -12321);
+
+  PolicyMap loaded;
+  loaded.LoadFrom(&policies,
+                  POLICY_LEVEL_MANDATORY,
+                  POLICY_SCOPE_USER,
+                  POLICY_SOURCE_PLATFORM);
+
+  PolicyMap expected;
+  expected.Set("TestPolicy1", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+               POLICY_SOURCE_PLATFORM,
+               std::make_unique<base::Value>("google.com"), nullptr);
+  expected.Set("TestPolicy2", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+               POLICY_SOURCE_PLATFORM, std::make_unique<base::Value>(true),
+               nullptr);
+  expected.Set("TestPolicy3", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+               POLICY_SOURCE_PLATFORM, std::make_unique<base::Value>(-12321),
+               nullptr);
+  EXPECT_TRUE(loaded.Equals(expected));
+}
+
+bool IsMandatory(const PolicyMap::PolicyMapType::const_iterator iter) {
+  return iter->second.level == POLICY_LEVEL_MANDATORY;
+}
+
+TEST_F(PolicyMapTest, EraseNonmatching) {
+  PolicyMap a;
+  a.Set(kTestPolicyName1, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+        POLICY_SOURCE_CLOUD, std::make_unique<base::Value>("google.com"),
+        nullptr);
+  a.Set(kTestPolicyName2, POLICY_LEVEL_RECOMMENDED, POLICY_SCOPE_MACHINE,
+        POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(true), nullptr);
+
+  a.EraseNonmatching(base::Bind(&IsMandatory));
+
+  PolicyMap b;
+  b.Set(kTestPolicyName1, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+        POLICY_SOURCE_CLOUD, std::make_unique<base::Value>("google.com"),
+        nullptr);
+  EXPECT_TRUE(a.Equals(b));
+}
+
+}  // namespace policy
diff --git a/components/policy/core/common/policy_namespace.cc b/components/policy/core/common/policy_namespace.cc
new file mode 100644
index 0000000..33a9992
--- /dev/null
+++ b/components/policy/core/common/policy_namespace.cc
@@ -0,0 +1,43 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/policy/core/common/policy_namespace.h"
+
+#include <tuple>
+
+namespace policy {
+
+PolicyNamespace::PolicyNamespace() {}
+
+PolicyNamespace::PolicyNamespace(PolicyDomain domain,
+                                 const std::string& component_id)
+    : domain(domain),
+      component_id(component_id) {}
+
+PolicyNamespace::PolicyNamespace(const PolicyNamespace& other)
+    : domain(other.domain),
+      component_id(other.component_id) {}
+
+PolicyNamespace::~PolicyNamespace() {}
+
+PolicyNamespace& PolicyNamespace::operator=(const PolicyNamespace& other) {
+  domain = other.domain;
+  component_id = other.component_id;
+  return *this;
+}
+
+bool PolicyNamespace::operator<(const PolicyNamespace& other) const {
+  return std::tie(domain, component_id) <
+         std::tie(other.domain, other.component_id);
+}
+
+bool PolicyNamespace::operator==(const PolicyNamespace& other) const {
+  return domain == other.domain && component_id == other.component_id;
+}
+
+bool PolicyNamespace::operator!=(const PolicyNamespace& other) const {
+  return !(*this == other);
+}
+
+}  // namespace policy
diff --git a/components/policy/core/common/policy_namespace.h b/components/policy/core/common/policy_namespace.h
new file mode 100644
index 0000000..780fa22
--- /dev/null
+++ b/components/policy/core/common/policy_namespace.h
@@ -0,0 +1,66 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_POLICY_CORE_COMMON_POLICY_NAMESPACE_H_
+#define COMPONENTS_POLICY_CORE_COMMON_POLICY_NAMESPACE_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+
+#include "base/containers/hash_tables.h"
+#include "components/policy/policy_export.h"
+
+namespace policy {
+
+// Policies are namespaced by a (PolicyDomain, ID) pair. The meaning of the ID
+// string depends on the domain; for example, if the PolicyDomain is
+// "extensions" then the ID identifies the extension that the policies control.
+enum PolicyDomain {
+  // The component ID for chrome policies is always the empty string.
+  POLICY_DOMAIN_CHROME,
+
+  // The component ID for the extension policies is equal to the extension ID.
+  POLICY_DOMAIN_EXTENSIONS,
+
+  // The namespace that corresponds to the policies for extensions running
+  // under Chrome OS signin profile. The component ID is equal to the extension
+  // ID.
+  POLICY_DOMAIN_SIGNIN_EXTENSIONS,
+
+  // Must be the last entry.
+  POLICY_DOMAIN_SIZE,
+};
+
+// Groups a policy domain and a component ID in a single object representing
+// a policy namespace. Objects of this class can be used as keys in std::maps.
+struct POLICY_EXPORT PolicyNamespace {
+  PolicyNamespace();
+  PolicyNamespace(PolicyDomain domain, const std::string& component_id);
+  PolicyNamespace(const PolicyNamespace& other);
+  ~PolicyNamespace();
+
+  PolicyNamespace& operator=(const PolicyNamespace& other);
+  bool operator<(const PolicyNamespace& other) const;
+  bool operator==(const PolicyNamespace& other) const;
+  bool operator!=(const PolicyNamespace& other) const;
+
+  PolicyDomain domain;
+  std::string component_id;
+};
+
+typedef std::vector<PolicyNamespace> PolicyNamespaceList;
+
+struct PolicyNamespaceHash {
+  size_t operator()(const policy::PolicyNamespace& ns) const {
+    return std::hash<std::string>()(ns.component_id) ^
+           (UINT64_C(1) << ns.domain);
+  }
+};
+
+}  // namespace policy
+
+#endif  // COMPONENTS_POLICY_CORE_COMMON_POLICY_NAMESPACE_H_
diff --git a/components/policy/core/common/policy_pref_names.cc b/components/policy/core/common/policy_pref_names.cc
new file mode 100644
index 0000000..6daa439
--- /dev/null
+++ b/components/policy/core/common/policy_pref_names.cc
@@ -0,0 +1,30 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/policy/core/common/policy_pref_names.h"
+
+namespace policy {
+namespace policy_prefs {
+
+// 64-bit serialization of the time last policy usage statistics were collected
+// by UMA_HISTOGRAM_ENUMERATION.
+const char kLastPolicyStatisticsUpdate[] = "policy.last_statistics_update";
+
+// Blocks access to the listed host patterns.
+const char kUrlBlacklist[] = "policy.url_blacklist";
+
+// Allows access to the listed host patterns, as exceptions to the blacklist.
+const char kUrlWhitelist[] = "policy.url_whitelist";
+
+// Integer that specifies the policy refresh rate for user-policy in
+// milliseconds. Not all values are meaningful, so it is clamped to a sane range
+// by the cloud policy subsystem.
+const char kUserPolicyRefreshRate[] = "policy.user_refresh_rate";
+
+// The enrollment token of machine level user cloud policy
+const char kMachineLevelUserCloudPolicyEnrollmentToken[] =
+    "policy.machine_level_user_cloud_policy_enrollment_token";
+
+}  // namespace policy_prefs
+}  // namespace policy
diff --git a/components/policy/core/common/policy_pref_names.h b/components/policy/core/common/policy_pref_names.h
new file mode 100644
index 0000000..31a8211
--- /dev/null
+++ b/components/policy/core/common/policy_pref_names.h
@@ -0,0 +1,21 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_POLICY_CORE_COMMON_POLICY_PREF_NAMES_H_
+#define COMPONENTS_POLICY_CORE_COMMON_POLICY_PREF_NAMES_H_
+
+#include "components/policy/policy_export.h"
+
+namespace policy {
+namespace policy_prefs {
+
+POLICY_EXPORT extern const char kLastPolicyStatisticsUpdate[];
+POLICY_EXPORT extern const char kUrlBlacklist[];
+POLICY_EXPORT extern const char kUrlWhitelist[];
+POLICY_EXPORT extern const char kUserPolicyRefreshRate[];
+POLICY_EXPORT extern const char kMachineLevelUserCloudPolicyEnrollmentToken[];
+}  // namespace policy_prefs
+}  // namespace policy
+
+#endif  // COMPONENTS_POLICY_CORE_COMMON_POLICY_PREF_NAMES_H_
diff --git a/components/policy/core/common/policy_proto_decoders.cc b/components/policy/core/common/policy_proto_decoders.cc
new file mode 100644
index 0000000..c5c8414
--- /dev/null
+++ b/components/policy/core/common/policy_proto_decoders.cc
@@ -0,0 +1,189 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/policy/core/common/policy_proto_decoders.h"
+
+#include <limits>
+#include <memory>
+
+#include "base/json/json_reader.h"
+#include "base/logging.h"
+#include "base/values.h"
+#include "components/policy/core/common/cloud/cloud_external_data_manager.h"
+#include "components/policy/core/common/policy_map.h"
+#include "components/policy/policy_constants.h"
+#include "components/policy/proto/cloud_policy.pb.h"
+
+namespace policy {
+
+namespace em = enterprise_management;
+
+namespace {
+
+// Returns true and sets |level| to a PolicyLevel if the policy has been set
+// at that level. Returns false if the policy is not set, or has been set at
+// the level of PolicyOptions::UNSET.
+template <class AnyPolicyProto>
+bool GetPolicyLevel(const AnyPolicyProto& policy_proto, PolicyLevel* level) {
+  if (!policy_proto.has_value()) {
+    return false;
+  }
+  if (!policy_proto.has_policy_options()) {
+    *level = POLICY_LEVEL_MANDATORY;  // Default level.
+    return true;
+  }
+  switch (policy_proto.policy_options().mode()) {
+    case em::PolicyOptions::MANDATORY:
+      *level = POLICY_LEVEL_MANDATORY;
+      return true;
+    case em::PolicyOptions::RECOMMENDED:
+      *level = POLICY_LEVEL_RECOMMENDED;
+      return true;
+    case em::PolicyOptions::UNSET:
+      return false;
+  }
+}
+
+// Convert a BooleanPolicyProto to a bool base::Value.
+std::unique_ptr<base::Value> DecodeBooleanProto(
+    const em::BooleanPolicyProto& proto) {
+  return std::make_unique<base::Value>(proto.value());
+}
+
+// Convert an IntegerPolicyProto to an int base::Value.
+std::unique_ptr<base::Value> DecodeIntegerProto(
+    const em::IntegerPolicyProto& proto,
+    std::string* error) {
+  google::protobuf::int64 value = proto.value();
+
+  if (value < std::numeric_limits<int>::min() ||
+      value > std::numeric_limits<int>::max()) {
+    LOG(WARNING) << "Integer value " << value << " out of numeric limits";
+    *error = "Number out of range - invalid int32";
+    return std::make_unique<base::Value>(std::to_string(value));
+  }
+
+  return std::make_unique<base::Value>(static_cast<int>(value));
+}
+
+// Convert a StringPolicyProto to a string base::Value.
+std::unique_ptr<base::Value> DecodeStringProto(
+    const em::StringPolicyProto& proto) {
+  return std::make_unique<base::Value>(proto.value());
+}
+
+// Convert a StringListPolicyProto to a List base::Value, where each list value
+// is of Type::STRING.
+std::unique_ptr<base::Value> DecodeStringListProto(
+    const em::StringListPolicyProto& proto) {
+  auto list_value = std::make_unique<base::ListValue>();
+  for (const auto& entry : proto.value().entries())
+    list_value->AppendString(entry);
+  return std::move(list_value);
+}
+
+// Convert a StringPolicyProto to a base::Value of any type (for example,
+// Type::DICTIONARY or Type::LIST) by parsing it as JSON.
+std::unique_ptr<base::Value> DecodeJsonProto(const em::StringPolicyProto& proto,
+                                             std::string* error) {
+  const std::string& json = proto.value();
+  std::unique_ptr<base::Value> parsed_value =
+      base::JSONReader::ReadAndReturnError(
+          json, base::JSON_ALLOW_TRAILING_COMMAS, nullptr, error);
+
+  if (!parsed_value) {
+    // Can't parse as JSON so return it as a string, and leave it to the handler
+    // to validate.
+    LOG(WARNING) << "Invalid JSON: " << json;
+    return std::make_unique<base::Value>(json);
+  }
+
+  // Accept any Value type that parsed as JSON, and leave it to the handler to
+  // convert and check the concrete type.
+  error->clear();
+  return parsed_value;
+}
+
+}  // namespace
+
+void DecodeProtoFields(
+    const em::CloudPolicySettings& policy,
+    base::WeakPtr<CloudExternalDataManager> external_data_manager,
+    PolicySource source,
+    PolicyScope scope,
+    PolicyMap* map) {
+  PolicyLevel level;
+
+  // Access arrays are terminated by a struct that contains only nullptrs.
+  for (const BooleanPolicyAccess* access = &kBooleanPolicyAccess[0];
+       access->policy_key; access++) {
+    if (!(policy.*access->has_proto)())
+      continue;
+
+    const em::BooleanPolicyProto& proto = (policy.*access->get_proto)();
+    if (!GetPolicyLevel(proto, &level))
+      continue;
+
+    map->Set(access->policy_key, level, scope, source,
+             DecodeBooleanProto(proto), nullptr);
+  }
+
+  for (const IntegerPolicyAccess* access = &kIntegerPolicyAccess[0];
+       access->policy_key; access++) {
+    if (!(policy.*access->has_proto)())
+      continue;
+
+    const em::IntegerPolicyProto& proto = (policy.*access->get_proto)();
+    if (!GetPolicyLevel(proto, &level))
+      continue;
+
+    std::string error;
+    map->Set(access->policy_key, level, scope, source,
+             DecodeIntegerProto(proto, &error), nullptr);
+    if (!error.empty())
+      map->SetError(access->policy_key, error);
+  }
+
+  for (const StringPolicyAccess* access = &kStringPolicyAccess[0];
+       access->policy_key; access++) {
+    if (!(policy.*access->has_proto)())
+      continue;
+
+    const em::StringPolicyProto& proto = (policy.*access->get_proto)();
+    if (!GetPolicyLevel(proto, &level))
+      continue;
+
+    std::string error;
+    std::unique_ptr<base::Value> value =
+        (access->type == StringPolicyType::STRING)
+            ? DecodeStringProto(proto)
+            : DecodeJsonProto(proto, &error);
+
+    std::unique_ptr<ExternalDataFetcher> external_data_fetcher =
+        (access->type == StringPolicyType::EXTERNAL)
+            ? std::make_unique<ExternalDataFetcher>(external_data_manager,
+                                                    access->policy_key)
+            : nullptr;
+
+    map->Set(access->policy_key, level, scope, source, std::move(value),
+             std::move(external_data_fetcher));
+    if (!error.empty())
+      map->SetError(access->policy_key, error);
+  }
+
+  for (const StringListPolicyAccess* access = &kStringListPolicyAccess[0];
+       access->policy_key; access++) {
+    if (!(policy.*access->has_proto)())
+      continue;
+
+    const em::StringListPolicyProto& proto = (policy.*access->get_proto)();
+    if (!GetPolicyLevel(proto, &level))
+      continue;
+
+    map->Set(access->policy_key, level, scope, source,
+             DecodeStringListProto(proto), nullptr);
+  }
+}
+
+}  // namespace policy
\ No newline at end of file
diff --git a/components/policy/core/common/policy_proto_decoders.h b/components/policy/core/common/policy_proto_decoders.h
new file mode 100644
index 0000000..7b95851
--- /dev/null
+++ b/components/policy/core/common/policy_proto_decoders.h
@@ -0,0 +1,34 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_POLICY_CORE_COMMON_POLICY_PROTO_DECODERS_H_
+#define COMPONENTS_POLICY_CORE_COMMON_POLICY_PROTO_DECODERS_H_
+
+#include <string>
+
+#include "base/memory/weak_ptr.h"
+#include "components/policy/core/common/policy_types.h"
+
+namespace enterprise_management {
+class CloudPolicySettings;
+}  // namespace enterprise_management
+
+namespace policy {
+
+class CloudExternalDataManager;
+class PolicyMap;
+
+// Decode all of the fields in |policy| which are recognized (see the metadata
+// in policy_constants.cc) and store them in the given |map|, with the given
+// |source| and |scope|.
+void DecodeProtoFields(
+    const enterprise_management::CloudPolicySettings& policy,
+    base::WeakPtr<CloudExternalDataManager> external_data_manager,
+    PolicySource source,
+    PolicyScope scope,
+    PolicyMap* map);
+
+}  // namespace policy
+
+#endif  // COMPONENTS_POLICY_CORE_COMMON_POLICY_PROTO_DECODERS_H_
diff --git a/components/policy/core/common/policy_scheduler.cc b/components/policy/core/common/policy_scheduler.cc
new file mode 100644
index 0000000..4c8d67d
--- /dev/null
+++ b/components/policy/core/common/policy_scheduler.cc
@@ -0,0 +1,73 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/policy/core/common/policy_scheduler.h"
+
+#include "base/threading/thread_task_runner_handle.h"
+
+namespace policy {
+
+PolicyScheduler::PolicyScheduler(Task task,
+                                 SchedulerCallback callback,
+                                 base::TimeDelta interval)
+    : task_(task), callback_(callback), interval_(interval) {
+  ScheduleTaskNow();
+}
+
+PolicyScheduler::~PolicyScheduler() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
+
+void PolicyScheduler::ScheduleTaskNow() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  ScheduleDelayedTask(base::TimeDelta());
+}
+
+void PolicyScheduler::ScheduleDelayedTask(base::TimeDelta delay) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  if (job_) {
+    job_->Cancel();
+  }
+  job_ = std::make_unique<base::CancelableClosure>(base::Bind(
+      &PolicyScheduler::RunScheduledTask, weak_ptr_factory_.GetWeakPtr()));
+  base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(FROM_HERE,
+                                                       job_->callback(), delay);
+}
+
+void PolicyScheduler::ScheduleNextTask() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  base::TimeDelta interval = overlap_ ? base::TimeDelta() : interval_;
+  const base::TimeTicks now(base::TimeTicks::Now());
+  // Time uses saturated arithmetics thus no under/overflow possible.
+  const base::TimeDelta delay = last_task_ + interval - now;
+  // Clamping delay to non-negative values just to be on the safe side.
+  ScheduleDelayedTask(std::max(base::TimeDelta(), delay));
+}
+
+void PolicyScheduler::RunScheduledTask() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  if (task_in_progress_) {
+    overlap_ = true;
+    return;
+  }
+
+  overlap_ = false;
+  task_in_progress_ = true;
+  task_.Run(base::BindOnce(&PolicyScheduler::OnTaskDone,
+                           weak_ptr_factory_.GetWeakPtr()));
+}
+
+void PolicyScheduler::OnTaskDone(bool success) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  task_in_progress_ = false;
+  last_task_ = base::TimeTicks::Now();
+  callback_.Run(success);
+  ScheduleNextTask();
+}
+
+}  // namespace policy
diff --git a/components/policy/core/common/policy_scheduler.h b/components/policy/core/common/policy_scheduler.h
new file mode 100644
index 0000000..b31314e
--- /dev/null
+++ b/components/policy/core/common/policy_scheduler.h
@@ -0,0 +1,98 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_POLICY_CORE_COMMON_POLICY_SCHEDULER_H_
+#define COMPONENTS_POLICY_CORE_COMMON_POLICY_SCHEDULER_H_
+
+#include <memory>
+
+#include "base/callback.h"
+#include "base/cancelable_callback.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/sequence_checker.h"
+#include "base/time/time.h"
+#include "components/policy/policy_export.h"
+
+namespace policy {
+
+// Scheduler for driving repeated asynchronous tasks such as e.g. policy
+// fetches. Subsequent tasks are guaranteed not to overlap. Tasks are posted to
+// the current thread and therefore must not block (suitable e.g. for
+// asynchronous D-Bus calls).
+// Tasks scheduling begins immediately after instantiation of the class. Upon
+// destruction, scheduled but not yet started tasks are cancelled. The result of
+// started but not finished tasks is NOT reported.
+class POLICY_EXPORT PolicyScheduler {
+ public:
+  // Callback for the task to report success or failure.
+  using TaskCallback = base::OnceCallback<void(bool success)>;
+
+  // Task to be performed at regular intervals. The task takes a |callback| to
+  // return success or failure.
+  using Task = base::RepeatingCallback<void(TaskCallback callback)>;
+
+  // Callback for PolicyScheduler to report success or failure of the tasks.
+  using SchedulerCallback = base::RepeatingCallback<void(bool success)>;
+
+  // Defines the |task| to be run every |interval| and the |callback| for the
+  // scheduler to report the result. (Intervals are computed as the time
+  // difference between the end of the previous and the start of the subsequent
+  // task.) Calling the constructor starts the loop and schedules the first task
+  // to be run without delay.
+  PolicyScheduler(Task task,
+                  SchedulerCallback callback,
+                  base::TimeDelta interval);
+  ~PolicyScheduler();
+
+  // Schedules a task to run immediately. Deletes any previously scheduled but
+  // not yet started tasks. In case a task is running currently, the new task is
+  // scheduled to run immediately after the end of the currently running task.
+  void ScheduleTaskNow();
+
+  base::TimeDelta interval() const { return interval_; }
+
+ private:
+  // Schedules next task to run in |delay|. Deletes any previously scheduled
+  // tasks.
+  void ScheduleDelayedTask(base::TimeDelta delay);
+
+  // Schedules next task to run in |interval_| or immediately in case of
+  // overlap. Deletes any previously scheduled tasks.
+  void ScheduleNextTask();
+
+  // Actually executes the scheduled task.
+  void RunScheduledTask();
+
+  // Reports back the |result| of the previous task and schedules the next one.
+  void OnTaskDone(bool result);
+
+  Task task_;
+  SchedulerCallback callback_;
+  // Tasks are being run every |interval_|.
+  const base::TimeDelta interval_;
+
+  // Whether a task is in progress.
+  bool task_in_progress_ = false;
+
+  // Whether there had been an overlap of tasks and thus the next task needs to
+  // be scheduled without delay.
+  bool overlap_ = false;
+
+  // End time of the previous task. Zero in case no task has ended yet.
+  base::TimeTicks last_task_;
+
+  std::unique_ptr<base::CancelableClosure> job_;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+
+  // Must be last member.
+  base::WeakPtrFactory<PolicyScheduler> weak_ptr_factory_{this};
+
+  DISALLOW_COPY_AND_ASSIGN(PolicyScheduler);
+};
+
+}  // namespace policy
+
+#endif  // COMPONENTS_POLICY_CORE_COMMON_POLICY_SCHEDULER_H_
diff --git a/components/policy/core/common/policy_scheduler_unittest.cc b/components/policy/core/common/policy_scheduler_unittest.cc
new file mode 100644
index 0000000..2950f3f
--- /dev/null
+++ b/components/policy/core/common/policy_scheduler_unittest.cc
@@ -0,0 +1,133 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/policy/core/common/policy_scheduler.h"
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/run_loop.h"
+#include "base/test/scoped_task_environment.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace policy {
+
+class PolicySchedulerTest : public testing::Test {
+ public:
+  void DoTask(PolicyScheduler::TaskCallback callback) {
+    do_counter_++;
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE, base::BindOnce(std::move(callback), true));
+  }
+
+  void OnTaskDone(bool success) {
+    done_counter_++;
+
+    // Terminate PolicyScheduler after 5 iterations.
+    if (done_counter_ >= 5) {
+      base::ThreadTaskRunnerHandle::Get()->PostTask(
+          FROM_HERE, base::BindOnce(&PolicySchedulerTest::Terminate,
+                                    base::Unretained(this)));
+    }
+  }
+
+  // To simulate a slow task the callback is captured instead of running it.
+  void CaptureCallbackForSlowTask(PolicyScheduler::TaskCallback callback) {
+    do_counter_++;
+    slow_callback_ = std::move(callback);
+  }
+
+  // Runs the captured callback to simulate the end of the slow task.
+  void PostSlowTaskCallback() {
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE, base::BindOnce(std::move(slow_callback_), true));
+  }
+
+  void Terminate() { scheduler_.reset(); }
+
+ protected:
+  int do_counter_ = 0;
+  int done_counter_ = 0;
+  std::unique_ptr<PolicyScheduler> scheduler_;
+
+  PolicyScheduler::TaskCallback slow_callback_;
+
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+};
+
+TEST_F(PolicySchedulerTest, Run) {
+  scheduler_ = std::make_unique<PolicyScheduler>(
+      base::BindRepeating(&PolicySchedulerTest::DoTask, base::Unretained(this)),
+      base::BindRepeating(&PolicySchedulerTest::OnTaskDone,
+                          base::Unretained(this)),
+      base::TimeDelta::Max());
+
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(1, done_counter_);
+}
+
+TEST_F(PolicySchedulerTest, Loop) {
+  scheduler_ = std::make_unique<PolicyScheduler>(
+      base::BindRepeating(&PolicySchedulerTest::DoTask, base::Unretained(this)),
+      base::BindRepeating(&PolicySchedulerTest::OnTaskDone,
+                          base::Unretained(this)),
+      base::TimeDelta());
+
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(5, done_counter_);
+}
+
+TEST_F(PolicySchedulerTest, Reschedule) {
+  scheduler_ = std::make_unique<PolicyScheduler>(
+      base::BindRepeating(&PolicySchedulerTest::DoTask, base::Unretained(this)),
+      base::BindRepeating(&PolicySchedulerTest::OnTaskDone,
+                          base::Unretained(this)),
+      base::TimeDelta::Max());
+
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(1, done_counter_);
+
+  // Delayed action is not run.
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(1, done_counter_);
+
+  // Rescheduling with 0 delay causes it to run.
+  scheduler_->ScheduleTaskNow();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(2, done_counter_);
+}
+
+TEST_F(PolicySchedulerTest, OverlappingTasks) {
+  scheduler_ = std::make_unique<PolicyScheduler>(
+      base::BindRepeating(&PolicySchedulerTest::CaptureCallbackForSlowTask,
+                          base::Unretained(this)),
+      base::BindRepeating(&PolicySchedulerTest::OnTaskDone,
+                          base::Unretained(this)),
+      base::TimeDelta::Max());
+
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(1, do_counter_);
+  EXPECT_EQ(0, done_counter_);
+
+  // Second action doesn't start while first is still pending.
+  scheduler_->ScheduleTaskNow();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(1, do_counter_);
+  EXPECT_EQ(0, done_counter_);
+
+  // After first action has finished, the second is started.
+  PostSlowTaskCallback();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(2, do_counter_);
+  EXPECT_EQ(1, done_counter_);
+
+  // Let the second action finish.
+  PostSlowTaskCallback();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(2, do_counter_);
+  EXPECT_EQ(2, done_counter_);
+}
+
+}  // namespace policy
diff --git a/components/policy/core/common/policy_service.cc b/components/policy/core/common/policy_service.cc
new file mode 100644
index 0000000..fe35ca5
--- /dev/null
+++ b/components/policy/core/common/policy_service.cc
@@ -0,0 +1,44 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/policy/core/common/policy_service.h"
+
+#include "base/values.h"
+
+namespace policy {
+
+PolicyChangeRegistrar::PolicyChangeRegistrar(PolicyService* policy_service,
+                                             const PolicyNamespace& ns)
+    : policy_service_(policy_service),
+      ns_(ns) {}
+
+PolicyChangeRegistrar::~PolicyChangeRegistrar() {
+  if (!callback_map_.empty())
+    policy_service_->RemoveObserver(ns_.domain, this);
+}
+
+void PolicyChangeRegistrar::Observe(const std::string& policy_name,
+                                    const UpdateCallback& callback) {
+  if (callback_map_.empty())
+    policy_service_->AddObserver(ns_.domain, this);
+  callback_map_[policy_name] = callback;
+}
+
+void PolicyChangeRegistrar::OnPolicyUpdated(const PolicyNamespace& ns,
+                                            const PolicyMap& previous,
+                                            const PolicyMap& current) {
+  if (ns != ns_)
+    return;
+  for (CallbackMap::iterator it = callback_map_.begin();
+       it != callback_map_.end(); ++it) {
+    const base::Value* prev = previous.GetValue(it->first);
+    const base::Value* cur = current.GetValue(it->first);
+
+    // Check if the values pointed to by |prev| and |cur| are different.
+    if ((!prev ^ !cur) || (prev && cur && *prev != *cur))
+      it->second.Run(prev, cur);
+  }
+}
+
+}  // namespace policy
diff --git a/components/policy/core/common/policy_service.h b/components/policy/core/common/policy_service.h
new file mode 100644
index 0000000..b9897e1
--- /dev/null
+++ b/components/policy/core/common/policy_service.h
@@ -0,0 +1,116 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_POLICY_CORE_COMMON_POLICY_SERVICE_H_
+#define COMPONENTS_POLICY_CORE_COMMON_POLICY_SERVICE_H_
+
+#include <map>
+#include <string>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "components/policy/core/common/policy_map.h"
+#include "components/policy/core/common/policy_namespace.h"
+#include "components/policy/policy_export.h"
+
+namespace policy {
+
+// The PolicyService merges policies from all available sources, taking into
+// account their priorities. Policy clients can retrieve policy for their domain
+// and register for notifications on policy updates.
+//
+// The PolicyService is available from BrowserProcess as a global singleton.
+// There is also a PolicyService for browser-wide policies available from
+// BrowserProcess as a global singleton.
+class POLICY_EXPORT PolicyService {
+ public:
+  class POLICY_EXPORT Observer {
+   public:
+    // Invoked whenever policies for the given |ns| namespace are modified.
+    // This is only invoked for changes that happen after AddObserver is called.
+    // |previous| contains the values of the policies before the update,
+    // and |current| contains the current values.
+    virtual void OnPolicyUpdated(const PolicyNamespace& ns,
+                                 const PolicyMap& previous,
+                                 const PolicyMap& current) = 0;
+
+    // Invoked at most once for each |domain|, when the PolicyService becomes
+    // ready. If IsInitializationComplete() is false, then this will be invoked
+    // once all the policy providers have finished loading their policies for
+    // |domain|.
+    virtual void OnPolicyServiceInitialized(PolicyDomain domain) {}
+
+   protected:
+    virtual ~Observer() {}
+  };
+
+  virtual ~PolicyService() {}
+
+  // Observes changes to all components of the given |domain|.
+  virtual void AddObserver(PolicyDomain domain, Observer* observer) = 0;
+
+  virtual void RemoveObserver(PolicyDomain domain, Observer* observer) = 0;
+
+  virtual const PolicyMap& GetPolicies(const PolicyNamespace& ns) const = 0;
+
+  // The PolicyService loads policy from several sources, and some require
+  // asynchronous loads. IsInitializationComplete() returns true once all
+  // sources have loaded their policies for the given |domain|.
+  // It is safe to read policy from the PolicyService even if
+  // IsInitializationComplete() is false; there will be an OnPolicyUpdated()
+  // notification once new policies become available.
+  //
+  // OnPolicyServiceInitialized() is called when IsInitializationComplete()
+  // becomes true, which happens at most once for each domain.
+  // If IsInitializationComplete() is already true for |domain| when an Observer
+  // is registered, then that Observer will not receive an
+  // OnPolicyServiceInitialized() notification.
+  virtual bool IsInitializationComplete(PolicyDomain domain) const = 0;
+
+  // Asks the PolicyService to reload policy from all available policy sources.
+  // |callback| is invoked once every source has reloaded its policies, and
+  // GetPolicies() is guaranteed to return the updated values at that point.
+  virtual void RefreshPolicies(const base::Closure& callback) = 0;
+};
+
+// A registrar that only observes changes to particular policies within the
+// PolicyMap for the given policy namespace.
+class POLICY_EXPORT PolicyChangeRegistrar : public PolicyService::Observer {
+ public:
+  typedef base::Callback<void(const base::Value*,
+                              const base::Value*)> UpdateCallback;
+
+  // Observes updates to the given (domain, component_id) namespace in the given
+  // |policy_service|, and notifies |observer| whenever any of the registered
+  // policy keys changes. Both the |policy_service| and the |observer| must
+  // outlive |this|.
+  PolicyChangeRegistrar(PolicyService* policy_service,
+                        const PolicyNamespace& ns);
+
+  ~PolicyChangeRegistrar() override;
+
+  // Will invoke |callback| whenever |policy_name| changes its value, as long
+  // as this registrar exists.
+  // Only one callback can be registed per policy name; a second call with the
+  // same |policy_name| will overwrite the previous callback.
+  void Observe(const std::string& policy_name, const UpdateCallback& callback);
+
+  // Implementation of PolicyService::Observer:
+  void OnPolicyUpdated(const PolicyNamespace& ns,
+                       const PolicyMap& previous,
+                       const PolicyMap& current) override;
+
+ private:
+  typedef std::map<std::string, UpdateCallback> CallbackMap;
+
+  PolicyService* policy_service_;
+  PolicyNamespace ns_;
+  CallbackMap callback_map_;
+
+  DISALLOW_COPY_AND_ASSIGN(PolicyChangeRegistrar);
+};
+
+}  // namespace policy
+
+#endif  // COMPONENTS_POLICY_CORE_COMMON_POLICY_SERVICE_H_
diff --git a/components/policy/core/common/policy_service_impl.cc b/components/policy/core/common/policy_service_impl.cc
new file mode 100644
index 0000000..dd555a1
--- /dev/null
+++ b/components/policy/core/common/policy_service_impl.cc
@@ -0,0 +1,278 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/policy/core/common/policy_service_impl.h"
+
+#include <stddef.h>
+
+#include <algorithm>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/macros.h"
+#include "base/single_thread_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/values.h"
+#include "components/policy/core/common/policy_bundle.h"
+#include "components/policy/core/common/policy_map.h"
+#include "components/policy/core/common/policy_types.h"
+#include "components/policy/policy_constants.h"
+
+namespace policy {
+
+namespace {
+
+const char* kProxyPolicies[] = {
+  key::kProxyMode,
+  key::kProxyServerMode,
+  key::kProxyServer,
+  key::kProxyPacUrl,
+  key::kProxyBypassList,
+};
+
+// Maps the separate policies for proxy settings into a single Dictionary
+// policy. This allows to keep the logic of merging policies from different
+// sources simple, as all separate proxy policies should be considered as a
+// single whole during merging.
+void RemapProxyPolicies(PolicyMap* policies) {
+  // The highest (level, scope) pair for an existing proxy policy is determined
+  // first, and then only policies with those exact attributes are merged.
+  PolicyMap::Entry current_priority;  // Defaults to the lowest priority.
+  PolicySource inherited_source = POLICY_SOURCE_ENTERPRISE_DEFAULT;
+  std::unique_ptr<base::DictionaryValue> proxy_settings(
+      new base::DictionaryValue);
+  for (size_t i = 0; i < arraysize(kProxyPolicies); ++i) {
+    const PolicyMap::Entry* entry = policies->Get(kProxyPolicies[i]);
+    if (entry) {
+      if (entry->has_higher_priority_than(current_priority)) {
+        proxy_settings->Clear();
+        current_priority = entry->DeepCopy();
+        if (entry->source > inherited_source)  // Higher priority?
+          inherited_source = entry->source;
+      }
+      if (!entry->has_higher_priority_than(current_priority) &&
+          !current_priority.has_higher_priority_than(*entry)) {
+        proxy_settings->Set(kProxyPolicies[i], entry->value->CreateDeepCopy());
+      }
+      policies->Erase(kProxyPolicies[i]);
+    }
+  }
+  // Sets the new |proxy_settings| if kProxySettings isn't set yet, or if the
+  // new priority is higher.
+  const PolicyMap::Entry* existing = policies->Get(key::kProxySettings);
+  if (!proxy_settings->empty() &&
+      (!existing || current_priority.has_higher_priority_than(*existing))) {
+    policies->Set(key::kProxySettings, current_priority.level,
+                  current_priority.scope, inherited_source,
+                  std::move(proxy_settings), nullptr);
+  }
+}
+
+}  // namespace
+
+PolicyServiceImpl::PolicyServiceImpl(Providers providers)
+    : update_task_ptr_factory_(this) {
+  providers_ = std::move(providers);
+  for (int domain = 0; domain < POLICY_DOMAIN_SIZE; ++domain)
+    initialization_complete_[domain] = true;
+  for (auto* provider : providers_) {
+    provider->AddObserver(this);
+    for (int domain = 0; domain < POLICY_DOMAIN_SIZE; ++domain) {
+      initialization_complete_[domain] &=
+          provider->IsInitializationComplete(static_cast<PolicyDomain>(domain));
+    }
+  }
+  // There are no observers yet, but calls to GetPolicies() should already get
+  // the processed policy values.
+  MergeAndTriggerUpdates();
+}
+
+PolicyServiceImpl::~PolicyServiceImpl() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  for (auto* provider : providers_)
+    provider->RemoveObserver(this);
+}
+
+void PolicyServiceImpl::AddObserver(PolicyDomain domain,
+                                    PolicyService::Observer* observer) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  std::unique_ptr<Observers>& list = observers_[domain];
+  if (!list)
+    list = std::make_unique<Observers>();
+  list->AddObserver(observer);
+}
+
+void PolicyServiceImpl::RemoveObserver(PolicyDomain domain,
+                                       PolicyService::Observer* observer) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  auto it = observers_.find(domain);
+  if (it == observers_.end()) {
+    NOTREACHED();
+    return;
+  }
+  it->second->RemoveObserver(observer);
+  if (!it->second->might_have_observers()) {
+    observers_.erase(it);
+  }
+}
+
+const PolicyMap& PolicyServiceImpl::GetPolicies(
+    const PolicyNamespace& ns) const {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  return policy_bundle_.Get(ns);
+}
+
+bool PolicyServiceImpl::IsInitializationComplete(PolicyDomain domain) const {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(domain >= 0 && domain < POLICY_DOMAIN_SIZE);
+  return initialization_complete_[domain];
+}
+
+void PolicyServiceImpl::RefreshPolicies(const base::Closure& callback) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  if (!callback.is_null())
+    refresh_callbacks_.push_back(callback);
+
+  if (providers_.empty()) {
+    // Refresh is immediately complete if there are no providers. See the note
+    // on OnUpdatePolicy() about why this is a posted task.
+    update_task_ptr_factory_.InvalidateWeakPtrs();
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE, base::Bind(&PolicyServiceImpl::MergeAndTriggerUpdates,
+                              update_task_ptr_factory_.GetWeakPtr()));
+  } else {
+    // Some providers might invoke OnUpdatePolicy synchronously while handling
+    // RefreshPolicies. Mark all as pending before refreshing.
+    for (auto* provider : providers_)
+      refresh_pending_.insert(provider);
+    for (auto* provider : providers_)
+      provider->RefreshPolicies();
+  }
+}
+
+void PolicyServiceImpl::OnUpdatePolicy(ConfigurationPolicyProvider* provider) {
+  DCHECK_EQ(1, std::count(providers_.begin(), providers_.end(), provider));
+  refresh_pending_.erase(provider);
+
+  // Note: a policy change may trigger further policy changes in some providers.
+  // For example, disabling SigninAllowed would cause the CloudPolicyManager to
+  // drop all its policies, which makes this method enter again for that
+  // provider.
+  //
+  // Therefore this update is posted asynchronously, to prevent reentrancy in
+  // MergeAndTriggerUpdates. Also, cancel a pending update if there is any,
+  // since both will produce the same PolicyBundle.
+  update_task_ptr_factory_.InvalidateWeakPtrs();
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::Bind(&PolicyServiceImpl::MergeAndTriggerUpdates,
+                            update_task_ptr_factory_.GetWeakPtr()));
+}
+
+void PolicyServiceImpl::NotifyNamespaceUpdated(
+    const PolicyNamespace& ns,
+    const PolicyMap& previous,
+    const PolicyMap& current) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  auto iterator = observers_.find(ns.domain);
+  if (iterator != observers_.end()) {
+    for (auto& observer : *iterator->second)
+      observer.OnPolicyUpdated(ns, previous, current);
+  }
+}
+
+void PolicyServiceImpl::MergeAndTriggerUpdates() {
+  // Merge from each provider in their order of priority.
+  const PolicyNamespace chrome_namespace(POLICY_DOMAIN_CHROME, std::string());
+  PolicyBundle bundle;
+  for (auto* provider : providers_) {
+    PolicyBundle provided_bundle;
+    provided_bundle.CopyFrom(provider->policies());
+    RemapProxyPolicies(&provided_bundle.Get(chrome_namespace));
+    bundle.MergeFrom(provided_bundle);
+  }
+
+  // Swap first, so that observers that call GetPolicies() see the current
+  // values.
+  policy_bundle_.Swap(&bundle);
+
+  // Only notify observers of namespaces that have been modified.
+  const PolicyMap kEmpty;
+  PolicyBundle::const_iterator it_new = policy_bundle_.begin();
+  PolicyBundle::const_iterator end_new = policy_bundle_.end();
+  PolicyBundle::const_iterator it_old = bundle.begin();
+  PolicyBundle::const_iterator end_old = bundle.end();
+  while (it_new != end_new && it_old != end_old) {
+    if (it_new->first < it_old->first) {
+      // A new namespace is available.
+      NotifyNamespaceUpdated(it_new->first, kEmpty, *it_new->second);
+      ++it_new;
+    } else if (it_old->first < it_new->first) {
+      // A previously available namespace is now gone.
+      NotifyNamespaceUpdated(it_old->first, *it_old->second, kEmpty);
+      ++it_old;
+    } else {
+      if (!it_new->second->Equals(*it_old->second)) {
+        // An existing namespace's policies have changed.
+        NotifyNamespaceUpdated(it_new->first, *it_old->second, *it_new->second);
+      }
+      ++it_new;
+      ++it_old;
+    }
+  }
+
+  // Send updates for the remaining new namespaces, if any.
+  for (; it_new != end_new; ++it_new)
+    NotifyNamespaceUpdated(it_new->first, kEmpty, *it_new->second);
+
+  // Sends updates for the remaining removed namespaces, if any.
+  for (; it_old != end_old; ++it_old)
+    NotifyNamespaceUpdated(it_old->first, *it_old->second, kEmpty);
+
+  CheckInitializationComplete();
+  CheckRefreshComplete();
+}
+
+void PolicyServiceImpl::CheckInitializationComplete() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  // Check if all the providers just became initialized for each domain; if so,
+  // notify that domain's observers.
+  for (int domain = 0; domain < POLICY_DOMAIN_SIZE; ++domain) {
+    if (initialization_complete_[domain])
+      continue;
+
+    PolicyDomain policy_domain = static_cast<PolicyDomain>(domain);
+
+    bool all_complete = true;
+    for (auto* provider : providers_) {
+      if (!provider->IsInitializationComplete(policy_domain)) {
+        all_complete = false;
+        break;
+      }
+    }
+    if (all_complete) {
+      initialization_complete_[domain] = true;
+      auto iter = observers_.find(policy_domain);
+      if (iter != observers_.end()) {
+        for (auto& observer : *iter->second)
+          observer.OnPolicyServiceInitialized(policy_domain);
+      }
+    }
+  }
+}
+
+void PolicyServiceImpl::CheckRefreshComplete() {
+  // Invoke all the callbacks if a refresh has just fully completed.
+  if (refresh_pending_.empty() && !refresh_callbacks_.empty()) {
+    std::vector<base::Closure> callbacks;
+    callbacks.swap(refresh_callbacks_);
+    std::vector<base::Closure>::iterator it;
+    for (it = callbacks.begin(); it != callbacks.end(); ++it)
+      it->Run();
+  }
+}
+
+}  // namespace policy
diff --git a/components/policy/core/common/policy_service_impl.h b/components/policy/core/common/policy_service_impl.h
new file mode 100644
index 0000000..985b27e
--- /dev/null
+++ b/components/policy/core/common/policy_service_impl.h
@@ -0,0 +1,103 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_POLICY_CORE_COMMON_POLICY_SERVICE_IMPL_H_
+#define COMPONENTS_POLICY_CORE_COMMON_POLICY_SERVICE_IMPL_H_
+
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/observer_list.h"
+#include "base/threading/thread_checker.h"
+#include "components/policy/core/common/configuration_policy_provider.h"
+#include "components/policy/core/common/policy_bundle.h"
+#include "components/policy/core/common/policy_service.h"
+#include "components/policy/policy_export.h"
+
+namespace policy {
+
+class PolicyMap;
+
+class POLICY_EXPORT PolicyServiceImpl
+    : public PolicyService,
+      public ConfigurationPolicyProvider::Observer {
+ public:
+  using Providers = std::vector<ConfigurationPolicyProvider*>;
+
+  // Creates a new PolicyServiceImpl with the list of
+  // ConfigurationPolicyProviders, in order of decreasing priority.
+  explicit PolicyServiceImpl(Providers providers);
+  ~PolicyServiceImpl() override;
+
+  // PolicyService overrides:
+  void AddObserver(PolicyDomain domain,
+                   PolicyService::Observer* observer) override;
+  void RemoveObserver(PolicyDomain domain,
+                      PolicyService::Observer* observer) override;
+  const PolicyMap& GetPolicies(const PolicyNamespace& ns) const override;
+  bool IsInitializationComplete(PolicyDomain domain) const override;
+  void RefreshPolicies(const base::Closure& callback) override;
+
+ private:
+  using Observers = base::ObserverList<PolicyService::Observer, true>;
+
+  // ConfigurationPolicyProvider::Observer overrides:
+  void OnUpdatePolicy(ConfigurationPolicyProvider* provider) override;
+
+  // Posts a task to notify observers of |ns| that its policies have changed,
+  // passing along the |previous| and the |current| policies.
+  void NotifyNamespaceUpdated(const PolicyNamespace& ns,
+                              const PolicyMap& previous,
+                              const PolicyMap& current);
+
+  // Combines the policies from all the providers, and notifies the observers
+  // of namespaces whose policies have been modified.
+  void MergeAndTriggerUpdates();
+
+  // Checks if all providers are initialized, and notifies the observers
+  // if the service just became initialized.
+  void CheckInitializationComplete();
+
+  // Invokes all the refresh callbacks if there are no more refreshes pending.
+  void CheckRefreshComplete();
+
+  // The providers, in order of decreasing priority.
+  Providers providers_;
+
+  // Maps each policy namespace to its current policies.
+  PolicyBundle policy_bundle_;
+
+  // Maps each policy domain to its observer list.
+  std::map<PolicyDomain, std::unique_ptr<Observers>> observers_;
+
+  // True if all the providers are initialized for the indexed policy domain.
+  bool initialization_complete_[POLICY_DOMAIN_SIZE];
+
+  // Set of providers that have a pending update that was triggered by a
+  // call to RefreshPolicies().
+  std::set<ConfigurationPolicyProvider*> refresh_pending_;
+
+  // List of callbacks to invoke once all providers refresh after a
+  // RefreshPolicies() call.
+  std::vector<base::Closure> refresh_callbacks_;
+
+  // Used to verify thread-safe usage.
+  base::ThreadChecker thread_checker_;
+
+  // Used to create tasks to delay new policy updates while we may be already
+  // processing previous policy updates.
+  base::WeakPtrFactory<PolicyServiceImpl> update_task_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(PolicyServiceImpl);
+};
+
+}  // namespace policy
+
+#endif  // COMPONENTS_POLICY_CORE_COMMON_POLICY_SERVICE_IMPL_H_
diff --git a/components/policy/core/common/policy_service_impl_unittest.cc b/components/policy/core/common/policy_service_impl_unittest.cc
new file mode 100644
index 0000000..bc84baa
--- /dev/null
+++ b/components/policy/core/common/policy_service_impl_unittest.cc
@@ -0,0 +1,706 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/policy/core/common/policy_service_impl.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/values.h"
+#include "components/policy/core/common/external_data_fetcher.h"
+#include "components/policy/core/common/mock_configuration_policy_provider.h"
+#include "components/policy/core/common/mock_policy_service.h"
+#include "components/policy/core/common/policy_types.h"
+#include "components/policy/policy_constants.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::AnyNumber;
+using ::testing::Mock;
+using ::testing::Return;
+using ::testing::_;
+
+namespace policy {
+
+namespace {
+
+const char kExtension[] = "extension-id";
+const char kSameLevelPolicy[] = "policy-same-level-and-scope";
+const char kDiffLevelPolicy[] = "chrome-diff-level-and-scope";
+
+// Helper to compare the arguments to an EXPECT_CALL of OnPolicyUpdated() with
+// their expected values.
+MATCHER_P(PolicyEquals, expected, "") {
+  return arg.Equals(*expected);
+}
+
+// Helper to compare the arguments to an EXPECT_CALL of OnPolicyValueUpdated()
+// with their expected values.
+MATCHER_P(ValueEquals, expected, "") {
+  return *expected == *arg;
+}
+
+// Helper that fills |bundle| with test policies.
+void AddTestPolicies(PolicyBundle* bundle,
+                     const char* value,
+                     PolicyLevel level,
+                     PolicyScope scope) {
+  PolicyMap* policy_map =
+      &bundle->Get(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()));
+  policy_map->Set(kSameLevelPolicy, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+                  POLICY_SOURCE_ENTERPRISE_DEFAULT,
+                  std::make_unique<base::Value>(value), nullptr);
+  policy_map->Set(kDiffLevelPolicy, level, scope, POLICY_SOURCE_PLATFORM,
+                  std::make_unique<base::Value>(value), nullptr);
+  policy_map =
+      &bundle->Get(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, kExtension));
+  policy_map->Set(kSameLevelPolicy, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+                  POLICY_SOURCE_ENTERPRISE_DEFAULT,
+                  std::make_unique<base::Value>(value), nullptr);
+  policy_map->Set(kDiffLevelPolicy, level, scope, POLICY_SOURCE_PLATFORM,
+                  std::make_unique<base::Value>(value), nullptr);
+}
+
+// Observer class that changes the policy in the passed provider when the
+// callback is invoked.
+class ChangePolicyObserver : public PolicyService::Observer {
+ public:
+  explicit ChangePolicyObserver(MockConfigurationPolicyProvider* provider)
+      : provider_(provider),
+        observer_invoked_(false) {}
+
+  void OnPolicyUpdated(const PolicyNamespace&,
+                       const PolicyMap& previous,
+                       const PolicyMap& current) override {
+    PolicyMap new_policy;
+    new_policy.Set("foo", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+                   POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(14),
+                   nullptr);
+    provider_->UpdateChromePolicy(new_policy);
+    observer_invoked_ = true;
+  }
+
+  bool observer_invoked() const { return observer_invoked_; }
+
+ private:
+  MockConfigurationPolicyProvider* provider_;
+  bool observer_invoked_;
+};
+
+}  // namespace
+
+class PolicyServiceTest : public testing::Test {
+ public:
+  PolicyServiceTest() {}
+  void SetUp() override {
+    EXPECT_CALL(provider0_, IsInitializationComplete(_))
+        .WillRepeatedly(Return(true));
+    EXPECT_CALL(provider1_, IsInitializationComplete(_))
+        .WillRepeatedly(Return(true));
+    EXPECT_CALL(provider2_, IsInitializationComplete(_))
+        .WillRepeatedly(Return(true));
+
+    provider0_.Init();
+    provider1_.Init();
+    provider2_.Init();
+
+    policy0_.Set("pre", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+                 POLICY_SOURCE_ENTERPRISE_DEFAULT,
+                 std::make_unique<base::Value>(13), nullptr);
+    provider0_.UpdateChromePolicy(policy0_);
+
+    PolicyServiceImpl::Providers providers;
+    providers.push_back(&provider0_);
+    providers.push_back(&provider1_);
+    providers.push_back(&provider2_);
+    policy_service_ = std::make_unique<PolicyServiceImpl>(std::move(providers));
+  }
+
+  void TearDown() override {
+    provider0_.Shutdown();
+    provider1_.Shutdown();
+    provider2_.Shutdown();
+  }
+
+  MOCK_METHOD2(OnPolicyValueUpdated, void(const base::Value*,
+                                          const base::Value*));
+
+  MOCK_METHOD0(OnPolicyRefresh, void());
+
+  // Returns true if the policies for namespace |ns| match |expected|.
+  bool VerifyPolicies(const PolicyNamespace& ns,
+                      const PolicyMap& expected) {
+    return policy_service_->GetPolicies(ns).Equals(expected);
+  }
+
+  void RunUntilIdle() {
+    base::RunLoop loop;
+    loop.RunUntilIdle();
+  }
+
+ protected:
+  base::MessageLoop loop_;
+  MockConfigurationPolicyProvider provider0_;
+  MockConfigurationPolicyProvider provider1_;
+  MockConfigurationPolicyProvider provider2_;
+  PolicyMap policy0_;
+  PolicyMap policy1_;
+  PolicyMap policy2_;
+  std::unique_ptr<PolicyServiceImpl> policy_service_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(PolicyServiceTest);
+};
+
+TEST_F(PolicyServiceTest, LoadsPoliciesBeforeProvidersRefresh) {
+  PolicyMap expected;
+  expected.Set("pre", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+               POLICY_SOURCE_ENTERPRISE_DEFAULT,
+               std::make_unique<base::Value>(13), nullptr);
+  EXPECT_TRUE(VerifyPolicies(
+      PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()), expected));
+}
+
+TEST_F(PolicyServiceTest, NotifyObservers) {
+  MockPolicyServiceObserver observer;
+  policy_service_->AddObserver(POLICY_DOMAIN_CHROME, &observer);
+
+  PolicyMap expectedPrevious;
+  expectedPrevious.Set("pre", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+                       POLICY_SOURCE_ENTERPRISE_DEFAULT,
+                       std::make_unique<base::Value>(13), nullptr);
+
+  PolicyMap expectedCurrent;
+  expectedCurrent.CopyFrom(expectedPrevious);
+  expectedCurrent.Set("aaa", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+                      POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(123),
+                      nullptr);
+  policy0_.Set("aaa", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+               POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(123),
+               nullptr);
+  EXPECT_CALL(observer, OnPolicyUpdated(PolicyNamespace(POLICY_DOMAIN_CHROME,
+                                                        std::string()),
+                                        PolicyEquals(&expectedPrevious),
+                                        PolicyEquals(&expectedCurrent)));
+  provider0_.UpdateChromePolicy(policy0_);
+  Mock::VerifyAndClearExpectations(&observer);
+
+  // No changes.
+  EXPECT_CALL(observer, OnPolicyUpdated(_, _, _)).Times(0);
+  provider0_.UpdateChromePolicy(policy0_);
+  Mock::VerifyAndClearExpectations(&observer);
+  EXPECT_TRUE(VerifyPolicies(
+      PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()), expectedCurrent));
+
+  // New policy.
+  expectedPrevious.CopyFrom(expectedCurrent);
+  expectedCurrent.Set("bbb", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+                      POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(456),
+                      nullptr);
+  policy0_.Set("bbb", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+               POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(456),
+               nullptr);
+  EXPECT_CALL(observer, OnPolicyUpdated(PolicyNamespace(POLICY_DOMAIN_CHROME,
+                                                        std::string()),
+                                        PolicyEquals(&expectedPrevious),
+                                        PolicyEquals(&expectedCurrent)));
+  provider0_.UpdateChromePolicy(policy0_);
+  Mock::VerifyAndClearExpectations(&observer);
+
+  // Removed policy.
+  expectedPrevious.CopyFrom(expectedCurrent);
+  expectedCurrent.Erase("bbb");
+  policy0_.Erase("bbb");
+  EXPECT_CALL(observer, OnPolicyUpdated(PolicyNamespace(POLICY_DOMAIN_CHROME,
+                                                        std::string()),
+                                        PolicyEquals(&expectedPrevious),
+                                        PolicyEquals(&expectedCurrent)));
+  provider0_.UpdateChromePolicy(policy0_);
+  Mock::VerifyAndClearExpectations(&observer);
+
+  // Changed policy.
+  expectedPrevious.CopyFrom(expectedCurrent);
+  expectedCurrent.Set("aaa", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+                      POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(789),
+                      nullptr);
+  policy0_.Set("aaa", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+               POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(789),
+               nullptr);
+
+  EXPECT_CALL(observer, OnPolicyUpdated(PolicyNamespace(POLICY_DOMAIN_CHROME,
+                                                        std::string()),
+                                        PolicyEquals(&expectedPrevious),
+                                        PolicyEquals(&expectedCurrent)));
+  provider0_.UpdateChromePolicy(policy0_);
+  Mock::VerifyAndClearExpectations(&observer);
+
+  // No changes again.
+  EXPECT_CALL(observer, OnPolicyUpdated(_, _, _)).Times(0);
+  provider0_.UpdateChromePolicy(policy0_);
+  Mock::VerifyAndClearExpectations(&observer);
+  EXPECT_TRUE(VerifyPolicies(
+      PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()), expectedCurrent));
+
+  policy_service_->RemoveObserver(POLICY_DOMAIN_CHROME, &observer);
+}
+
+TEST_F(PolicyServiceTest, NotifyObserversInMultipleNamespaces) {
+  const std::string kExtension0("extension-0");
+  const std::string kExtension1("extension-1");
+  const std::string kExtension2("extension-2");
+  MockPolicyServiceObserver chrome_observer;
+  MockPolicyServiceObserver extension_observer;
+  policy_service_->AddObserver(POLICY_DOMAIN_CHROME, &chrome_observer);
+  policy_service_->AddObserver(POLICY_DOMAIN_EXTENSIONS, &extension_observer);
+
+  PolicyMap previous_policy_map;
+  previous_policy_map.Set("pre", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+                          POLICY_SOURCE_ENTERPRISE_DEFAULT,
+                          std::make_unique<base::Value>(13), nullptr);
+  PolicyMap policy_map;
+  policy_map.CopyFrom(previous_policy_map);
+  policy_map.Set("policy", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+                 POLICY_SOURCE_CLOUD, std::make_unique<base::Value>("value"),
+                 nullptr);
+
+  std::unique_ptr<PolicyBundle> bundle(new PolicyBundle());
+  // The initial setup includes a policy for chrome that is now changing.
+  bundle->Get(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()))
+      .CopyFrom(policy_map);
+  bundle->Get(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, kExtension0))
+      .CopyFrom(policy_map);
+  bundle->Get(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, kExtension1))
+      .CopyFrom(policy_map);
+
+  const PolicyMap kEmptyPolicyMap;
+  EXPECT_CALL(
+      chrome_observer,
+      OnPolicyUpdated(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()),
+                      PolicyEquals(&previous_policy_map),
+                      PolicyEquals(&policy_map)));
+  EXPECT_CALL(
+      extension_observer,
+      OnPolicyUpdated(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, kExtension0),
+                      PolicyEquals(&kEmptyPolicyMap),
+                      PolicyEquals(&policy_map)));
+  EXPECT_CALL(
+      extension_observer,
+      OnPolicyUpdated(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, kExtension1),
+                      PolicyEquals(&kEmptyPolicyMap),
+                      PolicyEquals(&policy_map)));
+  provider0_.UpdatePolicy(std::move(bundle));
+  RunUntilIdle();
+  Mock::VerifyAndClearExpectations(&chrome_observer);
+  Mock::VerifyAndClearExpectations(&extension_observer);
+
+  // Chrome policy stays the same, kExtension0 is gone, kExtension1 changes,
+  // and kExtension2 is new.
+  previous_policy_map.CopyFrom(policy_map);
+  bundle.reset(new PolicyBundle());
+  bundle->Get(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()))
+      .CopyFrom(policy_map);
+  policy_map.Set("policy", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+                 POLICY_SOURCE_CLOUD,
+                 std::make_unique<base::Value>("another value"), nullptr);
+  bundle->Get(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, kExtension1))
+      .CopyFrom(policy_map);
+  bundle->Get(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, kExtension2))
+      .CopyFrom(policy_map);
+
+  EXPECT_CALL(chrome_observer, OnPolicyUpdated(_, _, _)).Times(0);
+  EXPECT_CALL(
+      extension_observer,
+      OnPolicyUpdated(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, kExtension0),
+                      PolicyEquals(&previous_policy_map),
+                      PolicyEquals(&kEmptyPolicyMap)));
+  EXPECT_CALL(
+      extension_observer,
+      OnPolicyUpdated(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, kExtension1),
+                      PolicyEquals(&previous_policy_map),
+                      PolicyEquals(&policy_map)));
+  EXPECT_CALL(
+      extension_observer,
+      OnPolicyUpdated(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, kExtension2),
+                      PolicyEquals(&kEmptyPolicyMap),
+                      PolicyEquals(&policy_map)));
+  provider0_.UpdatePolicy(std::move(bundle));
+  RunUntilIdle();
+  Mock::VerifyAndClearExpectations(&chrome_observer);
+  Mock::VerifyAndClearExpectations(&extension_observer);
+
+  policy_service_->RemoveObserver(POLICY_DOMAIN_CHROME, &chrome_observer);
+  policy_service_->RemoveObserver(POLICY_DOMAIN_EXTENSIONS,
+                                  &extension_observer);
+}
+
+TEST_F(PolicyServiceTest, ObserverChangesPolicy) {
+  ChangePolicyObserver observer(&provider0_);
+  policy_service_->AddObserver(POLICY_DOMAIN_CHROME, &observer);
+  policy0_.Set("aaa", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+               POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(123),
+               nullptr);
+  policy0_.Set("bbb", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+               POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(1234),
+               nullptr);
+  // Should not crash.
+  provider0_.UpdateChromePolicy(policy0_);
+  policy_service_->RemoveObserver(POLICY_DOMAIN_CHROME, &observer);
+  EXPECT_TRUE(observer.observer_invoked());
+}
+
+TEST_F(PolicyServiceTest, Priorities) {
+  PolicyMap expected;
+  expected.Set("pre", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+               POLICY_SOURCE_ENTERPRISE_DEFAULT,
+               std::make_unique<base::Value>(13), nullptr);
+  expected.Set("aaa", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+               POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(0), nullptr);
+  policy0_.Set("aaa", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+               POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(0), nullptr);
+  policy1_.Set("aaa", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+               POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(1), nullptr);
+  policy2_.Set("aaa", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+               POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(2), nullptr);
+  provider0_.UpdateChromePolicy(policy0_);
+  provider1_.UpdateChromePolicy(policy1_);
+  provider2_.UpdateChromePolicy(policy2_);
+  EXPECT_TRUE(VerifyPolicies(
+      PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()), expected));
+
+  expected.Set("aaa", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+               POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(1), nullptr);
+  policy0_.Erase("aaa");
+  provider0_.UpdateChromePolicy(policy0_);
+  EXPECT_TRUE(VerifyPolicies(
+      PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()), expected));
+
+  expected.Set("aaa", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+               POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(2), nullptr);
+  policy1_.Set("aaa", POLICY_LEVEL_RECOMMENDED, POLICY_SCOPE_USER,
+               POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(1), nullptr);
+  provider1_.UpdateChromePolicy(policy1_);
+  EXPECT_TRUE(VerifyPolicies(
+      PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()), expected));
+}
+
+TEST_F(PolicyServiceTest, PolicyChangeRegistrar) {
+  std::unique_ptr<PolicyChangeRegistrar> registrar(new PolicyChangeRegistrar(
+      policy_service_.get(),
+      PolicyNamespace(POLICY_DOMAIN_CHROME, std::string())));
+
+  // Starting to observe existing policies doesn't trigger a notification.
+  EXPECT_CALL(*this, OnPolicyValueUpdated(_, _)).Times(0);
+  registrar->Observe("pre", base::Bind(
+      &PolicyServiceTest::OnPolicyValueUpdated,
+      base::Unretained(this)));
+  registrar->Observe("aaa", base::Bind(
+      &PolicyServiceTest::OnPolicyValueUpdated,
+      base::Unretained(this)));
+  RunUntilIdle();
+  Mock::VerifyAndClearExpectations(this);
+
+  // Changing it now triggers a notification.
+  base::Value kValue0(0);
+  EXPECT_CALL(*this, OnPolicyValueUpdated(NULL, ValueEquals(&kValue0)));
+  policy0_.Set("aaa", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+               POLICY_SOURCE_CLOUD, kValue0.CreateDeepCopy(), nullptr);
+  provider0_.UpdateChromePolicy(policy0_);
+  Mock::VerifyAndClearExpectations(this);
+
+  // Changing other values doesn't trigger a notification.
+  EXPECT_CALL(*this, OnPolicyValueUpdated(_, _)).Times(0);
+  policy0_.Set("bbb", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+               POLICY_SOURCE_CLOUD, kValue0.CreateDeepCopy(), nullptr);
+  provider0_.UpdateChromePolicy(policy0_);
+  Mock::VerifyAndClearExpectations(this);
+
+  // Modifying the value triggers a notification.
+  base::Value kValue1(1);
+  EXPECT_CALL(*this, OnPolicyValueUpdated(ValueEquals(&kValue0),
+                                          ValueEquals(&kValue1)));
+  policy0_.Set("aaa", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+               POLICY_SOURCE_CLOUD, kValue1.CreateDeepCopy(), nullptr);
+  provider0_.UpdateChromePolicy(policy0_);
+  Mock::VerifyAndClearExpectations(this);
+
+  // Removing the value triggers a notification.
+  EXPECT_CALL(*this, OnPolicyValueUpdated(ValueEquals(&kValue1), NULL));
+  policy0_.Erase("aaa");
+  provider0_.UpdateChromePolicy(policy0_);
+  Mock::VerifyAndClearExpectations(this);
+
+  // No more notifications after destroying the registrar.
+  EXPECT_CALL(*this, OnPolicyValueUpdated(_, _)).Times(0);
+  registrar.reset();
+  policy0_.Set("aaa", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+               POLICY_SOURCE_CLOUD, kValue1.CreateDeepCopy(), nullptr);
+  policy0_.Set("pre", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+               POLICY_SOURCE_ENTERPRISE_DEFAULT, kValue1.CreateDeepCopy(),
+               nullptr);
+  provider0_.UpdateChromePolicy(policy0_);
+  Mock::VerifyAndClearExpectations(this);
+}
+
+TEST_F(PolicyServiceTest, RefreshPolicies) {
+  EXPECT_CALL(provider0_, RefreshPolicies()).Times(AnyNumber());
+  EXPECT_CALL(provider1_, RefreshPolicies()).Times(AnyNumber());
+  EXPECT_CALL(provider2_, RefreshPolicies()).Times(AnyNumber());
+
+  EXPECT_CALL(*this, OnPolicyRefresh()).Times(0);
+  policy_service_->RefreshPolicies(base::Bind(
+      &PolicyServiceTest::OnPolicyRefresh,
+      base::Unretained(this)));
+  // Let any queued observer tasks run.
+  RunUntilIdle();
+  Mock::VerifyAndClearExpectations(this);
+
+  EXPECT_CALL(*this, OnPolicyRefresh()).Times(0);
+  base::Value kValue0(0);
+  policy0_.Set("aaa", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+               POLICY_SOURCE_CLOUD, kValue0.CreateDeepCopy(), nullptr);
+  provider0_.UpdateChromePolicy(policy0_);
+  Mock::VerifyAndClearExpectations(this);
+
+  EXPECT_CALL(*this, OnPolicyRefresh()).Times(0);
+  base::Value kValue1(1);
+  policy1_.Set("aaa", POLICY_LEVEL_RECOMMENDED, POLICY_SCOPE_USER,
+               POLICY_SOURCE_CLOUD, kValue1.CreateDeepCopy(), nullptr);
+  provider1_.UpdateChromePolicy(policy1_);
+  Mock::VerifyAndClearExpectations(this);
+
+  // A provider can refresh more than once after a RefreshPolicies call, but
+  // OnPolicyRefresh should be triggered only after all providers are
+  // refreshed.
+  EXPECT_CALL(*this, OnPolicyRefresh()).Times(0);
+  policy1_.Set("bbb", POLICY_LEVEL_RECOMMENDED, POLICY_SCOPE_USER,
+               POLICY_SOURCE_CLOUD, kValue1.CreateDeepCopy(), nullptr);
+  provider1_.UpdateChromePolicy(policy1_);
+  Mock::VerifyAndClearExpectations(this);
+
+  // If another RefreshPolicies() call happens while waiting for a previous
+  // one to complete, then all providers must refresh again.
+  EXPECT_CALL(*this, OnPolicyRefresh()).Times(0);
+  policy_service_->RefreshPolicies(base::Bind(
+      &PolicyServiceTest::OnPolicyRefresh,
+      base::Unretained(this)));
+  RunUntilIdle();
+  Mock::VerifyAndClearExpectations(this);
+
+  EXPECT_CALL(*this, OnPolicyRefresh()).Times(0);
+  policy2_.Set("bbb", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+               POLICY_SOURCE_CLOUD, kValue0.CreateDeepCopy(), nullptr);
+  provider2_.UpdateChromePolicy(policy2_);
+  Mock::VerifyAndClearExpectations(this);
+
+  // Providers 0 and 1 must reload again.
+  EXPECT_CALL(*this, OnPolicyRefresh()).Times(2);
+  base::Value kValue2(2);
+  policy0_.Set("aaa", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+               POLICY_SOURCE_CLOUD, kValue2.CreateDeepCopy(), nullptr);
+  provider0_.UpdateChromePolicy(policy0_);
+  provider1_.UpdateChromePolicy(policy1_);
+  Mock::VerifyAndClearExpectations(this);
+
+  const PolicyMap& policies = policy_service_->GetPolicies(
+      PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()));
+  EXPECT_EQ(kValue2, *policies.GetValue("aaa"));
+  EXPECT_EQ(kValue0, *policies.GetValue("bbb"));
+}
+
+TEST_F(PolicyServiceTest, NamespaceMerge) {
+  std::unique_ptr<PolicyBundle> bundle0(new PolicyBundle());
+  std::unique_ptr<PolicyBundle> bundle1(new PolicyBundle());
+  std::unique_ptr<PolicyBundle> bundle2(new PolicyBundle());
+
+  AddTestPolicies(bundle0.get(), "bundle0",
+                  POLICY_LEVEL_RECOMMENDED, POLICY_SCOPE_USER);
+  AddTestPolicies(bundle1.get(), "bundle1",
+                  POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER);
+  AddTestPolicies(bundle2.get(), "bundle2",
+                  POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE);
+
+  provider0_.UpdatePolicy(std::move(bundle0));
+  provider1_.UpdatePolicy(std::move(bundle1));
+  provider2_.UpdatePolicy(std::move(bundle2));
+  RunUntilIdle();
+
+  PolicyMap expected;
+  // For policies of the same level and scope, the first provider takes
+  // precedence, on every namespace.
+  expected.Set(kSameLevelPolicy, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+               POLICY_SOURCE_ENTERPRISE_DEFAULT,
+               std::make_unique<base::Value>("bundle0"), nullptr);
+  // For policies with different levels and scopes, the highest priority
+  // level/scope combination takes precedence, on every namespace.
+  expected.Set(kDiffLevelPolicy, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE,
+               POLICY_SOURCE_PLATFORM, std::make_unique<base::Value>("bundle2"),
+               nullptr);
+  EXPECT_TRUE(policy_service_->GetPolicies(
+      PolicyNamespace(POLICY_DOMAIN_CHROME, std::string())).Equals(expected));
+  EXPECT_TRUE(policy_service_->GetPolicies(
+      PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, kExtension)).Equals(expected));
+}
+
+TEST_F(PolicyServiceTest, IsInitializationComplete) {
+  // |provider0| has all domains initialized.
+  Mock::VerifyAndClearExpectations(&provider1_);
+  Mock::VerifyAndClearExpectations(&provider2_);
+  EXPECT_CALL(provider1_, IsInitializationComplete(_))
+      .WillRepeatedly(Return(false));
+  EXPECT_CALL(provider2_, IsInitializationComplete(_))
+      .WillRepeatedly(Return(false));
+  PolicyServiceImpl::Providers providers;
+  providers.push_back(&provider0_);
+  providers.push_back(&provider1_);
+  providers.push_back(&provider2_);
+  policy_service_ = std::make_unique<PolicyServiceImpl>(std::move(providers));
+  EXPECT_FALSE(policy_service_->IsInitializationComplete(POLICY_DOMAIN_CHROME));
+  EXPECT_FALSE(
+      policy_service_->IsInitializationComplete(POLICY_DOMAIN_EXTENSIONS));
+  EXPECT_FALSE(policy_service_->IsInitializationComplete(
+      POLICY_DOMAIN_SIGNIN_EXTENSIONS));
+
+  // |provider2_| still doesn't have POLICY_DOMAIN_CHROME initialized, so
+  // the initialization status of that domain won't change.
+  MockPolicyServiceObserver observer;
+  policy_service_->AddObserver(POLICY_DOMAIN_CHROME, &observer);
+  policy_service_->AddObserver(POLICY_DOMAIN_EXTENSIONS, &observer);
+  policy_service_->AddObserver(POLICY_DOMAIN_SIGNIN_EXTENSIONS, &observer);
+  EXPECT_CALL(observer, OnPolicyServiceInitialized(_)).Times(0);
+  Mock::VerifyAndClearExpectations(&provider1_);
+  EXPECT_CALL(provider1_, IsInitializationComplete(POLICY_DOMAIN_CHROME))
+      .WillRepeatedly(Return(true));
+  EXPECT_CALL(provider1_, IsInitializationComplete(POLICY_DOMAIN_EXTENSIONS))
+      .WillRepeatedly(Return(false));
+  EXPECT_CALL(provider1_,
+              IsInitializationComplete(POLICY_DOMAIN_SIGNIN_EXTENSIONS))
+      .WillRepeatedly(Return(false));
+  const PolicyMap kPolicyMap;
+  provider1_.UpdateChromePolicy(kPolicyMap);
+  Mock::VerifyAndClearExpectations(&observer);
+  EXPECT_FALSE(policy_service_->IsInitializationComplete(POLICY_DOMAIN_CHROME));
+  EXPECT_FALSE(
+      policy_service_->IsInitializationComplete(POLICY_DOMAIN_EXTENSIONS));
+  EXPECT_FALSE(policy_service_->IsInitializationComplete(
+      POLICY_DOMAIN_SIGNIN_EXTENSIONS));
+
+  // Same if |provider1_| doesn't have POLICY_DOMAIN_EXTENSIONS initialized.
+  EXPECT_CALL(observer, OnPolicyServiceInitialized(_)).Times(0);
+  Mock::VerifyAndClearExpectations(&provider2_);
+  EXPECT_CALL(provider2_, IsInitializationComplete(POLICY_DOMAIN_CHROME))
+      .WillRepeatedly(Return(false));
+  EXPECT_CALL(provider2_, IsInitializationComplete(POLICY_DOMAIN_EXTENSIONS))
+      .WillRepeatedly(Return(true));
+  EXPECT_CALL(provider2_,
+              IsInitializationComplete(POLICY_DOMAIN_SIGNIN_EXTENSIONS))
+      .WillRepeatedly(Return(true));
+  provider2_.UpdateChromePolicy(kPolicyMap);
+  Mock::VerifyAndClearExpectations(&observer);
+  EXPECT_FALSE(policy_service_->IsInitializationComplete(POLICY_DOMAIN_CHROME));
+  EXPECT_FALSE(
+      policy_service_->IsInitializationComplete(POLICY_DOMAIN_EXTENSIONS));
+  EXPECT_FALSE(policy_service_->IsInitializationComplete(
+      POLICY_DOMAIN_SIGNIN_EXTENSIONS));
+
+  // Now initialize POLICY_DOMAIN_CHROME on all the providers.
+  EXPECT_CALL(observer, OnPolicyServiceInitialized(POLICY_DOMAIN_CHROME));
+  Mock::VerifyAndClearExpectations(&provider2_);
+  EXPECT_CALL(provider2_, IsInitializationComplete(POLICY_DOMAIN_CHROME))
+      .WillRepeatedly(Return(true));
+  EXPECT_CALL(provider2_, IsInitializationComplete(POLICY_DOMAIN_EXTENSIONS))
+      .WillRepeatedly(Return(true));
+  EXPECT_CALL(provider2_,
+              IsInitializationComplete(POLICY_DOMAIN_SIGNIN_EXTENSIONS))
+      .WillRepeatedly(Return(true));
+  provider2_.UpdateChromePolicy(kPolicyMap);
+  Mock::VerifyAndClearExpectations(&observer);
+  EXPECT_TRUE(policy_service_->IsInitializationComplete(POLICY_DOMAIN_CHROME));
+  // Other domains are still not initialized.
+  EXPECT_FALSE(
+      policy_service_->IsInitializationComplete(POLICY_DOMAIN_EXTENSIONS));
+  EXPECT_FALSE(policy_service_->IsInitializationComplete(
+      POLICY_DOMAIN_SIGNIN_EXTENSIONS));
+
+  // Initialize the remaining domain.
+  EXPECT_CALL(observer, OnPolicyServiceInitialized(POLICY_DOMAIN_EXTENSIONS));
+  EXPECT_CALL(observer,
+              OnPolicyServiceInitialized(POLICY_DOMAIN_SIGNIN_EXTENSIONS));
+  Mock::VerifyAndClearExpectations(&provider1_);
+  EXPECT_CALL(provider1_, IsInitializationComplete(POLICY_DOMAIN_CHROME))
+      .WillRepeatedly(Return(true));
+  EXPECT_CALL(provider1_, IsInitializationComplete(POLICY_DOMAIN_EXTENSIONS))
+      .WillRepeatedly(Return(true));
+  EXPECT_CALL(provider1_,
+              IsInitializationComplete(POLICY_DOMAIN_SIGNIN_EXTENSIONS))
+      .WillRepeatedly(Return(true));
+  provider1_.UpdateChromePolicy(kPolicyMap);
+  Mock::VerifyAndClearExpectations(&observer);
+  EXPECT_TRUE(policy_service_->IsInitializationComplete(POLICY_DOMAIN_CHROME));
+  EXPECT_TRUE(
+      policy_service_->IsInitializationComplete(POLICY_DOMAIN_EXTENSIONS));
+  EXPECT_TRUE(policy_service_->IsInitializationComplete(
+      POLICY_DOMAIN_SIGNIN_EXTENSIONS));
+
+  // Cleanup.
+  policy_service_->RemoveObserver(POLICY_DOMAIN_CHROME, &observer);
+  policy_service_->RemoveObserver(POLICY_DOMAIN_EXTENSIONS, &observer);
+  policy_service_->RemoveObserver(POLICY_DOMAIN_SIGNIN_EXTENSIONS, &observer);
+}
+
+TEST_F(PolicyServiceTest, SeparateProxyPoliciesMerging) {
+  const PolicyNamespace chrome_namespace(POLICY_DOMAIN_CHROME, std::string());
+  const PolicyNamespace extension_namespace(POLICY_DOMAIN_EXTENSIONS, "xyz");
+
+  std::unique_ptr<PolicyBundle> policy_bundle(new PolicyBundle());
+  PolicyMap& policy_map = policy_bundle->Get(chrome_namespace);
+  // Individual proxy policy values in the Chrome namespace should be collected
+  // into a dictionary.
+  policy_map.Set(key::kProxyServerMode, POLICY_LEVEL_MANDATORY,
+                 POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD,
+                 std::make_unique<base::Value>(3), nullptr);
+
+  // Both these policies should be ignored, since there's a higher priority
+  // policy available.
+  policy_map.Set(key::kProxyMode, POLICY_LEVEL_RECOMMENDED, POLICY_SCOPE_USER,
+                 POLICY_SOURCE_CLOUD,
+                 std::make_unique<base::Value>("pac_script"), nullptr);
+  policy_map.Set(key::kProxyPacUrl, POLICY_LEVEL_RECOMMENDED, POLICY_SCOPE_USER,
+                 POLICY_SOURCE_CLOUD,
+                 std::make_unique<base::Value>("http://example.com/wpad.dat"),
+                 nullptr);
+
+  // Add a value to a non-Chrome namespace.
+  policy_bundle->Get(extension_namespace)
+      .Set(key::kProxyServerMode, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+           POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(3), nullptr);
+
+  // The resulting Chrome namespace map should have the collected policy.
+  PolicyMap expected_chrome;
+  std::unique_ptr<base::DictionaryValue> expected_value(
+      new base::DictionaryValue);
+  expected_value->SetInteger(key::kProxyServerMode, 3);
+  expected_chrome.Set(key::kProxySettings, POLICY_LEVEL_MANDATORY,
+                      POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD,
+                      std::move(expected_value), nullptr);
+
+  // The resulting Extensions namespace map shouldn't have been modified.
+  PolicyMap expected_extension;
+  expected_extension.Set(key::kProxyServerMode, POLICY_LEVEL_MANDATORY,
+                         POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD,
+                         std::make_unique<base::Value>(3), nullptr);
+
+  provider0_.UpdatePolicy(std::move(policy_bundle));
+  RunUntilIdle();
+
+  EXPECT_TRUE(VerifyPolicies(chrome_namespace, expected_chrome));
+  EXPECT_TRUE(VerifyPolicies(extension_namespace, expected_extension));
+}
+
+}  // namespace policy
diff --git a/components/policy/core/common/policy_service_stub.cc b/components/policy/core/common/policy_service_stub.cc
new file mode 100644
index 0000000..025ff86
--- /dev/null
+++ b/components/policy/core/common/policy_service_stub.cc
@@ -0,0 +1,34 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/policy/core/common/policy_service_stub.h"
+
+
+namespace policy {
+
+PolicyServiceStub::PolicyServiceStub() {}
+
+PolicyServiceStub::~PolicyServiceStub() {}
+
+void PolicyServiceStub::AddObserver(PolicyDomain domain,
+                                    Observer* observer) {}
+
+void PolicyServiceStub::RemoveObserver(PolicyDomain domain,
+                                       Observer* observer) {}
+
+const PolicyMap& PolicyServiceStub::GetPolicies(
+    const PolicyNamespace& ns) const {
+  return kEmpty_;
+}
+
+bool PolicyServiceStub::IsInitializationComplete(PolicyDomain domain) const {
+  return true;
+}
+
+void PolicyServiceStub::RefreshPolicies(const base::Closure& callback) {
+  if (!callback.is_null())
+    callback.Run();
+}
+
+}  // namespace policy
diff --git a/components/policy/core/common/policy_service_stub.h b/components/policy/core/common/policy_service_stub.h
new file mode 100644
index 0000000..8759336
--- /dev/null
+++ b/components/policy/core/common/policy_service_stub.h
@@ -0,0 +1,42 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_POLICY_CORE_COMMON_POLICY_SERVICE_STUB_H_
+#define COMPONENTS_POLICY_CORE_COMMON_POLICY_SERVICE_STUB_H_
+
+#include "base/macros.h"
+#include "components/policy/core/common/policy_map.h"
+#include "components/policy/core/common/policy_service.h"
+#include "components/policy/policy_export.h"
+
+namespace policy {
+
+// A stub implementation, that is used when ENABLE_CONFIGURATION_POLICY is not
+// set. This allows client code to compile without requiring #ifdefs.
+class POLICY_EXPORT PolicyServiceStub : public PolicyService {
+ public:
+  PolicyServiceStub();
+  ~PolicyServiceStub() override;
+
+  void AddObserver(PolicyDomain domain,
+                   Observer* observer) override;
+
+  void RemoveObserver(PolicyDomain domain,
+                      Observer* observer) override;
+
+  const PolicyMap& GetPolicies(
+      const PolicyNamespace& ns) const override;
+
+  bool IsInitializationComplete(PolicyDomain domain) const override;
+
+  void RefreshPolicies(const base::Closure& callback) override;
+ private:
+  const PolicyMap kEmpty_;
+
+  DISALLOW_COPY_AND_ASSIGN(PolicyServiceStub);
+};
+
+}  // namespace policy
+
+#endif  // COMPONENTS_POLICY_CORE_COMMON_POLICY_SERVICE_STUB_H_
diff --git a/components/policy/core/common/policy_statistics_collector.cc b/components/policy/core/common/policy_statistics_collector.cc
new file mode 100644
index 0000000..de3cd16
--- /dev/null
+++ b/components/policy/core/common/policy_statistics_collector.cc
@@ -0,0 +1,94 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/policy/core/common/policy_statistics_collector.h"
+
+#include <algorithm>
+#include <string>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/metrics/histogram_functions.h"
+#include "base/task_runner.h"
+#include "components/policy/core/common/policy_pref_names.h"
+#include "components/policy/core/common/policy_service.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/pref_service.h"
+
+namespace policy {
+
+const int PolicyStatisticsCollector::kStatisticsUpdateRate =
+    24 * 60 * 60 * 1000;  // 24 hours.
+
+PolicyStatisticsCollector::PolicyStatisticsCollector(
+    const GetChromePolicyDetailsCallback& get_details,
+    const Schema& chrome_schema,
+    PolicyService* policy_service,
+    PrefService* prefs,
+    const scoped_refptr<base::TaskRunner>& task_runner)
+    : get_details_(get_details),
+      chrome_schema_(chrome_schema),
+      policy_service_(policy_service),
+      prefs_(prefs),
+      task_runner_(task_runner) {
+}
+
+PolicyStatisticsCollector::~PolicyStatisticsCollector() {
+}
+
+void PolicyStatisticsCollector::Initialize() {
+  using base::Time;
+  using base::TimeDelta;
+
+  TimeDelta update_rate = TimeDelta::FromMilliseconds(kStatisticsUpdateRate);
+  Time last_update = Time::FromInternalValue(
+      prefs_->GetInt64(policy_prefs::kLastPolicyStatisticsUpdate));
+  TimeDelta delay = std::max(Time::Now() - last_update, TimeDelta::FromDays(0));
+  if (delay >= update_rate)
+    CollectStatistics();
+  else
+    ScheduleUpdate(update_rate - delay);
+}
+
+// static
+void PolicyStatisticsCollector::RegisterPrefs(PrefRegistrySimple* registry) {
+  registry->RegisterInt64Pref(policy_prefs::kLastPolicyStatisticsUpdate, 0);
+}
+
+void PolicyStatisticsCollector::RecordPolicyUse(int id) {
+  base::UmaHistogramSparse("Enterprise.Policies", id);
+}
+
+void PolicyStatisticsCollector::CollectStatistics() {
+  const PolicyMap& policies = policy_service_->GetPolicies(
+      PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()));
+
+  // Collect statistics.
+  for (Schema::Iterator it(chrome_schema_.GetPropertiesIterator());
+       !it.IsAtEnd(); it.Advance()) {
+    if (policies.Get(it.key())) {
+      const PolicyDetails* details = get_details_.Run(it.key());
+      if (details)
+        RecordPolicyUse(details->id);
+      else
+        NOTREACHED();
+    }
+  }
+
+  // Take care of next update.
+  prefs_->SetInt64(policy_prefs::kLastPolicyStatisticsUpdate,
+                   base::Time::Now().ToInternalValue());
+  ScheduleUpdate(base::TimeDelta::FromMilliseconds(kStatisticsUpdateRate));
+}
+
+void PolicyStatisticsCollector::ScheduleUpdate(base::TimeDelta delay) {
+  update_callback_.Reset(base::Bind(
+      &PolicyStatisticsCollector::CollectStatistics,
+      base::Unretained(this)));
+  task_runner_->PostDelayedTask(FROM_HERE, update_callback_.callback(), delay);
+}
+
+}  // namespace policy
diff --git a/components/policy/core/common/policy_statistics_collector.h b/components/policy/core/common/policy_statistics_collector.h
new file mode 100644
index 0000000..f373fd5
--- /dev/null
+++ b/components/policy/core/common/policy_statistics_collector.h
@@ -0,0 +1,69 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_POLICY_CORE_COMMON_POLICY_STATISTICS_COLLECTOR_H_
+#define COMPONENTS_POLICY_CORE_COMMON_POLICY_STATISTICS_COLLECTOR_H_
+
+#include "base/cancelable_callback.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/time/time.h"
+#include "components/policy/core/common/policy_details.h"
+#include "components/policy/core/common/schema.h"
+#include "components/policy/policy_export.h"
+
+class PrefService;
+class PrefRegistrySimple;
+
+namespace base {
+class TaskRunner;
+}
+
+namespace policy {
+
+class PolicyService;
+
+// Manages regular updates of policy usage UMA histograms.
+class POLICY_EXPORT PolicyStatisticsCollector {
+ public:
+  // Policy usage statistics update rate, in milliseconds.
+  static const int kStatisticsUpdateRate;
+
+  // Neither |policy_service| nor |prefs| can be NULL and must stay valid
+  // throughout the lifetime of PolicyStatisticsCollector.
+  PolicyStatisticsCollector(const GetChromePolicyDetailsCallback& get_details,
+                            const Schema& chrome_schema,
+                            PolicyService* policy_service,
+                            PrefService* prefs,
+                            const scoped_refptr<base::TaskRunner>& task_runner);
+  virtual ~PolicyStatisticsCollector();
+
+  // Completes initialization and starts periodical statistic updates.
+  void Initialize();
+
+  static void RegisterPrefs(PrefRegistrySimple* registry);
+
+ protected:
+  // protected virtual for mocking.
+  virtual void RecordPolicyUse(int id);
+
+ private:
+  void CollectStatistics();
+  void ScheduleUpdate(base::TimeDelta delay);
+
+  GetChromePolicyDetailsCallback get_details_;
+  Schema chrome_schema_;
+  PolicyService* policy_service_;
+  PrefService* prefs_;
+
+  base::CancelableClosure update_callback_;
+
+  const scoped_refptr<base::TaskRunner> task_runner_;
+
+  DISALLOW_COPY_AND_ASSIGN(PolicyStatisticsCollector);
+};
+
+}  // namespace policy
+
+#endif  // COMPONENTS_POLICY_CORE_COMMON_POLICY_STATISTICS_COLLECTOR_H_
diff --git a/components/policy/core/common/policy_statistics_collector_unittest.cc b/components/policy/core/common/policy_statistics_collector_unittest.cc
new file mode 100644
index 0000000..80d2498
--- /dev/null
+++ b/components/policy/core/common/policy_statistics_collector_unittest.cc
@@ -0,0 +1,189 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/policy/core/common/policy_statistics_collector.h"
+
+#include <cstring>
+#include <memory>
+#include <string>
+
+#include "base/callback.h"
+#include "base/compiler_specific.h"
+#include "base/test/test_simple_task_runner.h"
+#include "base/values.h"
+#include "components/policy/core/common/external_data_fetcher.h"
+#include "components/policy/core/common/mock_policy_service.h"
+#include "components/policy/core/common/policy_map.h"
+#include "components/policy/core/common/policy_pref_names.h"
+#include "components/policy/core/common/policy_test_utils.h"
+#include "components/policy/core/common/policy_types.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/testing_pref_service.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace policy {
+
+namespace {
+
+using testing::ReturnRef;
+
+// Arbitrary policy names used for testing.
+const char kTestPolicy1[] = "Test Policy 1";
+const char kTestPolicy2[] = "Test Policy 2";
+
+const int kTestPolicy1Id = 42;
+const int kTestPolicy2Id = 123;
+
+const char kTestChromeSchema[] =
+    "{"
+    "  \"type\": \"object\","
+    "  \"properties\": {"
+    "    \"Test Policy 1\": { \"type\": \"string\" },"
+    "    \"Test Policy 2\": { \"type\": \"string\" }"
+    "  }"
+    "}";
+
+const PolicyDetails kTestPolicyDetails[] = {
+  // is_deprecated  is_device_policy              id  max_external_data_size
+  {          false,            false, kTestPolicy1Id,                      0 },
+  {          false,            false, kTestPolicy2Id,                      0 },
+};
+
+class TestPolicyStatisticsCollector : public PolicyStatisticsCollector {
+ public:
+  TestPolicyStatisticsCollector(
+      const GetChromePolicyDetailsCallback& get_details,
+      const Schema& chrome_schema,
+      PolicyService* policy_service,
+      PrefService* prefs,
+      const scoped_refptr<base::TaskRunner>& task_runner)
+      : PolicyStatisticsCollector(get_details,
+                                  chrome_schema,
+                                  policy_service,
+                                  prefs,
+                                  task_runner) {}
+
+  MOCK_METHOD1(RecordPolicyUse, void(int));
+};
+
+}  // namespace
+
+class PolicyStatisticsCollectorTest : public testing::Test {
+ protected:
+  PolicyStatisticsCollectorTest()
+      : update_delay_(base::TimeDelta::FromMilliseconds(
+            PolicyStatisticsCollector::kStatisticsUpdateRate)),
+        task_runner_(new base::TestSimpleTaskRunner()) {
+  }
+
+  void SetUp() override {
+    std::string error;
+    chrome_schema_ = Schema::Parse(kTestChromeSchema, &error);
+    ASSERT_TRUE(chrome_schema_.valid()) << error;
+
+    policy_details_.SetDetails(kTestPolicy1, &kTestPolicyDetails[0]);
+    policy_details_.SetDetails(kTestPolicy2, &kTestPolicyDetails[1]);
+
+    prefs_.registry()->RegisterInt64Pref(
+        policy_prefs::kLastPolicyStatisticsUpdate, 0);
+
+    // Set up default function behaviour.
+    EXPECT_CALL(policy_service_,
+                GetPolicies(PolicyNamespace(POLICY_DOMAIN_CHROME,
+                                            std::string())))
+        .WillRepeatedly(ReturnRef(policy_map_));
+
+    // Arbitrary negative value (so it'll be different from |update_delay_|).
+    last_delay_ = base::TimeDelta::FromDays(-1);
+    policy_map_.Clear();
+    policy_statistics_collector_.reset(new TestPolicyStatisticsCollector(
+        policy_details_.GetCallback(),
+        chrome_schema_,
+        &policy_service_,
+        &prefs_,
+        task_runner_));
+  }
+
+  void SetPolicy(const std::string& name) {
+    policy_map_.Set(name, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+                    POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(true),
+                    nullptr);
+  }
+
+  base::TimeDelta GetFirstDelay() const {
+    if (!task_runner_->HasPendingTask()) {
+      ADD_FAILURE();
+      return base::TimeDelta();
+    }
+    return task_runner_->NextPendingTaskDelay();
+  }
+
+  const base::TimeDelta update_delay_;
+
+  base::TimeDelta last_delay_;
+
+  PolicyDetailsMap policy_details_;
+  Schema chrome_schema_;
+  TestingPrefServiceSimple prefs_;
+  MockPolicyService policy_service_;
+  PolicyMap policy_map_;
+
+  scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+  std::unique_ptr<TestPolicyStatisticsCollector> policy_statistics_collector_;
+};
+
+TEST_F(PolicyStatisticsCollectorTest, CollectPending) {
+  SetPolicy(kTestPolicy1);
+
+  prefs_.SetInt64(policy_prefs::kLastPolicyStatisticsUpdate,
+                  (base::Time::Now() - update_delay_).ToInternalValue());
+
+  EXPECT_CALL(*policy_statistics_collector_, RecordPolicyUse(kTestPolicy1Id));
+
+  policy_statistics_collector_->Initialize();
+  EXPECT_EQ(1u, task_runner_->NumPendingTasks());
+  EXPECT_EQ(update_delay_, GetFirstDelay());
+}
+
+TEST_F(PolicyStatisticsCollectorTest, CollectPendingVeryOld) {
+  SetPolicy(kTestPolicy1);
+
+  // Must not be 0.0 (read comment for Time::FromDoubleT).
+  prefs_.SetInt64(policy_prefs::kLastPolicyStatisticsUpdate,
+                  base::Time::FromDoubleT(1.0).ToInternalValue());
+
+  EXPECT_CALL(*policy_statistics_collector_, RecordPolicyUse(kTestPolicy1Id));
+
+  policy_statistics_collector_->Initialize();
+  EXPECT_EQ(1u, task_runner_->NumPendingTasks());
+  EXPECT_EQ(update_delay_, GetFirstDelay());
+}
+
+TEST_F(PolicyStatisticsCollectorTest, CollectLater) {
+  SetPolicy(kTestPolicy1);
+
+  prefs_.SetInt64(policy_prefs::kLastPolicyStatisticsUpdate,
+                  (base::Time::Now() - update_delay_ / 2).ToInternalValue());
+
+  policy_statistics_collector_->Initialize();
+  EXPECT_EQ(1u, task_runner_->NumPendingTasks());
+  EXPECT_LT(GetFirstDelay(), update_delay_);
+}
+
+TEST_F(PolicyStatisticsCollectorTest, MultiplePolicies) {
+  SetPolicy(kTestPolicy1);
+  SetPolicy(kTestPolicy2);
+
+  prefs_.SetInt64(policy_prefs::kLastPolicyStatisticsUpdate,
+                  (base::Time::Now() - update_delay_).ToInternalValue());
+
+  EXPECT_CALL(*policy_statistics_collector_, RecordPolicyUse(kTestPolicy1Id));
+  EXPECT_CALL(*policy_statistics_collector_, RecordPolicyUse(kTestPolicy2Id));
+
+  policy_statistics_collector_->Initialize();
+  EXPECT_EQ(1u, task_runner_->NumPendingTasks());
+}
+
+}  // namespace policy
diff --git a/components/policy/core/common/policy_switches.cc b/components/policy/core/common/policy_switches.cc
new file mode 100644
index 0000000..7bea73e
--- /dev/null
+++ b/components/policy/core/common/policy_switches.cc
@@ -0,0 +1,22 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/policy/core/common/policy_switches.h"
+
+namespace policy {
+namespace switches {
+
+// Specifies the URL at which to fetch configuration policy from the device
+// management backend.
+const char kDeviceManagementUrl[]           = "device-management-url";
+
+// Disables fetching and storing cloud policy for components.
+const char kDisableComponentCloudPolicy[]   = "disable-component-cloud-policy";
+
+// Always treat user as affiliated.
+// TODO(antrim): Remove once test servers correctly produce affiliation ids.
+const char kUserAlwaysAffiliated[]  = "user-always-affiliated";
+
+}  // namespace switches
+}  // namespace policy
diff --git a/components/policy/core/common/policy_switches.h b/components/policy/core/common/policy_switches.h
new file mode 100644
index 0000000..08d13cb
--- /dev/null
+++ b/components/policy/core/common/policy_switches.h
@@ -0,0 +1,22 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_POLICY_CORE_COMMON_POLICY_SWITCHES_H_
+#define COMPONENTS_POLICY_CORE_COMMON_POLICY_SWITCHES_H_
+
+#include "components/policy/policy_export.h"
+
+#include "build/build_config.h"
+
+namespace policy {
+namespace switches {
+
+POLICY_EXPORT extern const char kDeviceManagementUrl[];
+POLICY_EXPORT extern const char kDisableComponentCloudPolicy[];
+POLICY_EXPORT extern const char kUserAlwaysAffiliated[];
+
+}  // namespace switches
+}  // namespace policy
+
+#endif  // COMPONENTS_POLICY_CORE_COMMON_POLICY_SWITCHES_H_
diff --git a/components/policy/core/common/policy_test_utils.cc b/components/policy/core/common/policy_test_utils.cc
new file mode 100644
index 0000000..d51eea9
--- /dev/null
+++ b/components/policy/core/common/policy_test_utils.cc
@@ -0,0 +1,209 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/policy/core/common/policy_test_utils.h"
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/strings/sys_string_conversions.h"
+#include "base/values.h"
+#include "build/build_config.h"
+#include "components/policy/core/common/policy_bundle.h"
+
+#if defined(OS_IOS) || defined(OS_MACOSX)
+#include <CoreFoundation/CoreFoundation.h>
+
+#include "base/mac/scoped_cftyperef.h"
+#endif
+
+namespace policy {
+
+PolicyDetailsMap::PolicyDetailsMap() {}
+
+PolicyDetailsMap::~PolicyDetailsMap() {}
+
+GetChromePolicyDetailsCallback PolicyDetailsMap::GetCallback() const {
+  return base::Bind(&PolicyDetailsMap::Lookup, base::Unretained(this));
+}
+
+void PolicyDetailsMap::SetDetails(const std::string& policy,
+                                  const PolicyDetails* details) {
+  map_[policy] = details;
+}
+
+const PolicyDetails* PolicyDetailsMap::Lookup(const std::string& policy) const {
+  PolicyDetailsMapping::const_iterator it = map_.find(policy);
+  return it == map_.end() ? NULL : it->second;
+}
+
+bool PolicyServiceIsEmpty(const PolicyService* service) {
+  const PolicyMap& map = service->GetPolicies(
+      PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()));
+  if (!map.empty()) {
+    base::DictionaryValue dict;
+    for (PolicyMap::const_iterator it = map.begin(); it != map.end(); ++it)
+      dict.SetKey(it->first, it->second.value->Clone());
+    LOG(WARNING) << "There are pre-existing policies in this machine: " << dict;
+  }
+  return map.empty();
+}
+
+#if defined(OS_IOS) || defined(OS_MACOSX)
+CFPropertyListRef ValueToProperty(const base::Value& value) {
+  switch (value.type()) {
+    case base::Value::Type::NONE:
+      return kCFNull;
+
+    case base::Value::Type::BOOLEAN: {
+      bool bool_value;
+      if (value.GetAsBoolean(&bool_value))
+        return bool_value ? kCFBooleanTrue : kCFBooleanFalse;
+      break;
+    }
+
+    case base::Value::Type::INTEGER: {
+      int int_value;
+      if (value.GetAsInteger(&int_value)) {
+        return CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType,
+                              &int_value);
+      }
+      break;
+    }
+
+    case base::Value::Type::DOUBLE: {
+      double double_value;
+      if (value.GetAsDouble(&double_value)) {
+        return CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType,
+                              &double_value);
+      }
+      break;
+    }
+
+    case base::Value::Type::STRING: {
+      std::string string_value;
+      if (value.GetAsString(&string_value))
+        return base::SysUTF8ToCFStringRef(string_value);
+      break;
+    }
+
+    case base::Value::Type::DICTIONARY: {
+      const base::DictionaryValue* dict_value;
+      if (value.GetAsDictionary(&dict_value)) {
+        // |dict| is owned by the caller.
+        CFMutableDictionaryRef dict = CFDictionaryCreateMutable(
+            kCFAllocatorDefault, dict_value->size(),
+            &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+        for (base::DictionaryValue::Iterator iterator(*dict_value);
+             !iterator.IsAtEnd(); iterator.Advance()) {
+          // CFDictionaryAddValue() retains both |key| and |value|, so make sure
+          // the references are balanced.
+          base::ScopedCFTypeRef<CFStringRef> key(
+              base::SysUTF8ToCFStringRef(iterator.key()));
+          base::ScopedCFTypeRef<CFPropertyListRef> cf_value(
+              ValueToProperty(iterator.value()));
+          if (cf_value)
+            CFDictionaryAddValue(dict, key, cf_value);
+        }
+        return dict;
+      }
+      break;
+    }
+
+    case base::Value::Type::LIST: {
+      const base::ListValue* list;
+      if (value.GetAsList(&list)) {
+        CFMutableArrayRef array =
+            CFArrayCreateMutable(NULL, list->GetSize(), &kCFTypeArrayCallBacks);
+        for (const auto& entry : *list) {
+          // CFArrayAppendValue() retains |cf_value|, so make sure the reference
+          // created by ValueToProperty() is released.
+          base::ScopedCFTypeRef<CFPropertyListRef> cf_value(
+              ValueToProperty(entry));
+          if (cf_value)
+            CFArrayAppendValue(array, cf_value);
+        }
+        return array;
+      }
+      break;
+    }
+
+    case base::Value::Type::BINARY:
+      // This type isn't converted (though it can be represented as CFData)
+      // because there's no equivalent JSON type, and policy values can only
+      // take valid JSON values.
+      break;
+  }
+
+  return NULL;
+}
+#endif  // defined(OS_IOS) || defined(OS_MACOSX)
+
+}  // namespace policy
+
+std::ostream& operator<<(std::ostream& os, const policy::PolicyBundle& bundle) {
+  os << "{" << std::endl;
+  for (policy::PolicyBundle::const_iterator iter = bundle.begin();
+       iter != bundle.end(); ++iter) {
+    os << "  \"" << iter->first << "\": " << *iter->second << "," << std::endl;
+  }
+  os << "}";
+  return os;
+}
+
+std::ostream& operator<<(std::ostream& os, policy::PolicyScope scope) {
+  switch (scope) {
+    case policy::POLICY_SCOPE_USER:
+      return os << "POLICY_SCOPE_USER";
+    case policy::POLICY_SCOPE_MACHINE:
+      return os << "POLICY_SCOPE_MACHINE";
+  }
+  return os << "POLICY_SCOPE_UNKNOWN(" << int(scope) << ")";
+}
+
+std::ostream& operator<<(std::ostream& os, policy::PolicyLevel level) {
+  switch (level) {
+    case policy::POLICY_LEVEL_RECOMMENDED:
+      return os << "POLICY_LEVEL_RECOMMENDED";
+    case policy::POLICY_LEVEL_MANDATORY:
+      return os << "POLICY_LEVEL_MANDATORY";
+  }
+  return os << "POLICY_LEVEL_UNKNOWN(" << int(level) << ")";
+}
+
+std::ostream& operator<<(std::ostream& os, policy::PolicyDomain domain) {
+  switch (domain) {
+    case policy::POLICY_DOMAIN_CHROME:
+      return os << "POLICY_DOMAIN_CHROME";
+    case policy::POLICY_DOMAIN_EXTENSIONS:
+      return os << "POLICY_DOMAIN_EXTENSIONS";
+    case policy::POLICY_DOMAIN_SIGNIN_EXTENSIONS:
+      return os << "POLICY_DOMAIN_SIGNIN_EXTENSIONS";
+    case policy::POLICY_DOMAIN_SIZE:
+      break;
+  }
+  return os << "POLICY_DOMAIN_UNKNOWN(" << int(domain) << ")";
+}
+
+std::ostream& operator<<(std::ostream& os, const policy::PolicyMap& policies) {
+  os << "{" << std::endl;
+  for (const auto& iter : policies)
+    os << "  \"" << iter.first << "\": " << iter.second << "," << std::endl;
+  os << "}";
+  return os;
+}
+
+std::ostream& operator<<(std::ostream& os, const policy::PolicyMap::Entry& e) {
+  return os << "{" << std::endl
+            << "  \"level\": " << e.level << "," << std::endl
+            << "  \"scope\": " << e.scope << "," << std::endl
+            << "  \"value\": " << *e.value << "}";
+}
+
+std::ostream& operator<<(std::ostream& os, const policy::PolicyNamespace& ns) {
+  return os << ns.domain << "/" << ns.component_id;
+}
diff --git a/components/policy/core/common/policy_test_utils.h b/components/policy/core/common/policy_test_utils.h
new file mode 100644
index 0000000..c76a7bb
--- /dev/null
+++ b/components/policy/core/common/policy_test_utils.h
@@ -0,0 +1,69 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_POLICY_CORE_COMMON_POLICY_TEST_UTILS_H_
+#define COMPONENTS_POLICY_CORE_COMMON_POLICY_TEST_UTILS_H_
+
+#include <map>
+#include <ostream>
+#include <string>
+
+#include "base/macros.h"
+#include "build/build_config.h"
+#include "components/policy/core/common/policy_details.h"
+#include "components/policy/core/common/policy_map.h"
+#include "components/policy/core/common/policy_service.h"
+#include "components/policy/core/common/policy_types.h"
+
+namespace policy {
+
+class PolicyBundle;
+struct PolicyNamespace;
+
+// A mapping of policy names to PolicyDetails that can be used to set the
+// PolicyDetails for test policies.
+class PolicyDetailsMap {
+ public:
+  PolicyDetailsMap();
+  ~PolicyDetailsMap();
+
+  // The returned callback's lifetime is tied to |this| object.
+  GetChromePolicyDetailsCallback GetCallback() const;
+
+  // Does not take ownership of |details|.
+  void SetDetails(const std::string& policy, const PolicyDetails* details);
+
+ private:
+  typedef std::map<std::string, const PolicyDetails*> PolicyDetailsMapping;
+
+  const PolicyDetails* Lookup(const std::string& policy) const;
+
+  PolicyDetailsMapping map_;
+
+  DISALLOW_COPY_AND_ASSIGN(PolicyDetailsMap);
+};
+
+// Returns true if |service| is not serving any policies. Otherwise logs the
+// current policies and returns false.
+bool PolicyServiceIsEmpty(const PolicyService* service);
+
+#if defined(OS_IOS) || defined(OS_MACOSX)
+
+// Converts a base::Value to the equivalent CFPropertyListRef.
+// The returned value is owned by the caller.
+CFPropertyListRef ValueToProperty(const base::Value& value);
+
+#endif
+
+}  // namespace policy
+
+std::ostream& operator<<(std::ostream& os, const policy::PolicyBundle& bundle);
+std::ostream& operator<<(std::ostream& os, policy::PolicyScope scope);
+std::ostream& operator<<(std::ostream& os, policy::PolicyLevel level);
+std::ostream& operator<<(std::ostream& os, policy::PolicyDomain domain);
+std::ostream& operator<<(std::ostream& os, const policy::PolicyMap& policies);
+std::ostream& operator<<(std::ostream& os, const policy::PolicyMap::Entry& e);
+std::ostream& operator<<(std::ostream& os, const policy::PolicyNamespace& ns);
+
+#endif  // COMPONENTS_POLICY_CORE_COMMON_POLICY_TEST_UTILS_H_
diff --git a/components/policy/core/common/preg_parser.cc b/components/policy/core/common/preg_parser.cc
new file mode 100644
index 0000000..a4fada3
--- /dev/null
+++ b/components/policy/core/common/preg_parser.cc
@@ -0,0 +1,410 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/policy/core/common/preg_parser.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <algorithm>
+#include <iterator>
+#include <limits>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/files/memory_mapped_file.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/sys_byteorder.h"
+#include "base/values.h"
+#include "components/policy/core/common/registry_dict.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#else
+// Registry data type constants.
+#define REG_NONE 0
+#define REG_SZ 1
+#define REG_EXPAND_SZ 2
+#define REG_BINARY 3
+#define REG_DWORD_LITTLE_ENDIAN 4
+#define REG_DWORD_BIG_ENDIAN 5
+#define REG_LINK 6
+#define REG_MULTI_SZ 7
+#define REG_RESOURCE_LIST 8
+#define REG_FULL_RESOURCE_DESCRIPTOR 9
+#define REG_RESOURCE_REQUIREMENTS_LIST 10
+#define REG_QWORD_LITTLE_ENDIAN 11
+#endif
+
+using RegistryDict = policy::RegistryDict;
+
+namespace {
+
+// Maximum PReg file size we're willing to accept.
+const int64_t kMaxPRegFileSize = 1024 * 1024 * 16;
+static_assert(kMaxPRegFileSize <= std::numeric_limits<ptrdiff_t>::max(),
+              "Max PReg file size too large.");
+
+// Maximum number of components in registry key names. This corresponds to the
+// maximum nesting level of RegistryDict trees.
+const size_t kMaxKeyNameComponents = 1024;
+
+// Constants for PReg file delimiters.
+const base::char16 kDelimBracketOpen = L'[';
+const base::char16 kDelimBracketClose = L']';
+const base::char16 kDelimSemicolon = L';';
+
+// Registry path separator.
+const base::char16 kRegistryPathSeparator[] = {L'\\', L'\0'};
+
+// Magic strings for the PReg value field to trigger special actions.
+const char kActionTriggerPrefix[] = "**";
+const char kActionTriggerDeleteValues[] = "deletevalues";
+const char kActionTriggerDel[] = "del.";
+const char kActionTriggerDelVals[] = "delvals";
+const char kActionTriggerDeleteKeys[] = "deletekeys";
+const char kActionTriggerSecureKey[] = "securekey";
+const char kActionTriggerSoft[] = "soft";
+
+// Returns the character at |cursor| and increments it, unless the end is here
+// in which case -1 is returned. The calling code must guarantee that
+// end - *cursor does not overflow ptrdiff_t.
+int NextChar(const uint8_t** cursor, const uint8_t* end) {
+  // Only read the character if a full base::char16 is available.
+  // This comparison makes sure no overflow can happen.
+  if (*cursor >= end ||
+      end - *cursor < static_cast<ptrdiff_t>(sizeof(base::char16)))
+    return -1;
+
+  int result = **cursor | (*(*cursor + 1) << 8);
+  *cursor += sizeof(base::char16);
+  return result;
+}
+
+// Reads a fixed-size field from a PReg file. The calling code must guarantee
+// that both end - *cursor and size do not overflow ptrdiff_t.
+bool ReadFieldBinary(const uint8_t** cursor,
+                     const uint8_t* end,
+                     uint32_t size,
+                     uint8_t* data) {
+  if (size == 0)
+    return true;
+
+  // Be careful to prevent possible overflows here (don't do *cursor + size).
+  if (*cursor >= end || end - *cursor < static_cast<ptrdiff_t>(size))
+    return false;
+  const uint8_t* field_end = *cursor + size;
+  std::copy(*cursor, field_end, data);
+  *cursor = field_end;
+  return true;
+}
+
+bool ReadField32(const uint8_t** cursor, const uint8_t* end, uint32_t* data) {
+  uint32_t value = 0;
+  if (!ReadFieldBinary(cursor, end, sizeof(uint32_t),
+                       reinterpret_cast<uint8_t*>(&value))) {
+    return false;
+  }
+  *data = base::ByteSwapToLE32(value);
+  return true;
+}
+
+// Reads a string field from a file.
+bool ReadFieldString(const uint8_t** cursor,
+                     const uint8_t* end,
+                     base::string16* str) {
+  int current = -1;
+  while ((current = NextChar(cursor, end)) > 0x0000)
+    *str += current;
+
+  return current == L'\0';
+}
+
+// Converts the UTF16 |data| to an UTF8 string |value|. Returns false if the
+// resulting UTF8 string contains invalid characters.
+bool DecodePRegStringValue(const std::vector<uint8_t>& data,
+                           std::string* value) {
+  size_t len = data.size() / sizeof(base::char16);
+  if (len <= 0) {
+    value->clear();
+    return true;
+  }
+
+  const base::char16* chars =
+      reinterpret_cast<const base::char16*>(data.data());
+  base::string16 utf16_str;
+  std::transform(chars, chars + len - 1, std::back_inserter(utf16_str),
+                 base::ByteSwapToLE16);
+  // Note: UTF16ToUTF8() only checks whether all chars are valid code points,
+  // but not whether they're valid characters. IsStringUTF8(), however, does.
+  *value = base::UTF16ToUTF8(utf16_str);
+  if (!base::IsStringUTF8(*value)) {
+    LOG(ERROR) << "String '" << *value << "' is not a valid UTF8 string";
+    value->clear();
+    return false;
+  }
+  return true;
+}
+
+// Decodes a value from a PReg file given as a uint8_t vector.
+bool DecodePRegValue(uint32_t type,
+                     const std::vector<uint8_t>& data,
+                     std::unique_ptr<base::Value>* value) {
+  std::string data_utf8;
+  switch (type) {
+    case REG_SZ:
+    case REG_EXPAND_SZ:
+      if (!DecodePRegStringValue(data, &data_utf8))
+        return false;
+      value->reset(new base::Value(data_utf8));
+      return true;
+    case REG_DWORD_LITTLE_ENDIAN:
+    case REG_DWORD_BIG_ENDIAN:
+      if (data.size() == sizeof(uint32_t)) {
+        uint32_t val = *reinterpret_cast<const uint32_t*>(data.data());
+        if (type == REG_DWORD_BIG_ENDIAN)
+          val = base::NetToHost32(val);
+        else
+          val = base::ByteSwapToLE32(val);
+        value->reset(new base::Value(static_cast<int>(val)));
+        return true;
+      } else {
+        LOG(ERROR) << "Bad data size " << data.size();
+      }
+      break;
+    case REG_NONE:
+    case REG_LINK:
+    case REG_MULTI_SZ:
+    case REG_RESOURCE_LIST:
+    case REG_FULL_RESOURCE_DESCRIPTOR:
+    case REG_RESOURCE_REQUIREMENTS_LIST:
+    case REG_QWORD_LITTLE_ENDIAN:
+    default:
+      LOG(ERROR) << "Unsupported registry data type " << type;
+  }
+
+  return false;
+}
+
+// Returns true if the registry key |key_name| belongs to the sub-tree specified
+// by the key |root|.
+bool KeyRootEquals(const base::string16& key_name, const base::string16& root) {
+  if (root.empty())
+    return true;
+
+  if (!base::StartsWith(key_name, root, base::CompareCase::INSENSITIVE_ASCII))
+    return false;
+
+  // Handle the case where |root| == "ABC" and |key_name| == "ABCDE\FG". This
+  // should not be interpreted as a match.
+  return key_name.length() == root.length() ||
+         key_name.at(root.length()) == kRegistryPathSeparator[0];
+}
+
+// Adds |value| and |data| to |dict| or an appropriate sub-dictionary indicated
+// by |key_name|. Creates sub-dictionaries if necessary. Also handles special
+// action triggers, see |kActionTrigger*|, that can, for instance, remove an
+// existing value.
+void HandleRecord(const base::string16& key_name,
+                  const base::string16& value,
+                  uint32_t type,
+                  const std::vector<uint8_t>& data,
+                  RegistryDict* dict) {
+  // Locate/create the dictionary to place the value in.
+  std::vector<base::string16> path;
+
+  std::vector<base::StringPiece16> key_name_components =
+      base::SplitStringPiece(key_name, kRegistryPathSeparator,
+                             base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+  if (key_name_components.size() > kMaxKeyNameComponents) {
+    LOG(ERROR) << "Encountered a key which has more than "
+               << kMaxKeyNameComponents << " components.";
+    return;
+  }
+  for (const base::StringPiece16& key_name_component : key_name_components) {
+    if (key_name_component.empty())
+      continue;
+
+    const std::string name = base::UTF16ToUTF8(key_name_component);
+    RegistryDict* subdict = dict->GetKey(name);
+    if (!subdict) {
+      subdict = new RegistryDict();
+      dict->SetKey(name, base::WrapUnique(subdict));
+    }
+    dict = subdict;
+  }
+
+  if (value.empty())
+    return;
+
+  std::string value_name(base::UTF16ToUTF8(value));
+  if (!base::StartsWith(value_name, kActionTriggerPrefix,
+                        base::CompareCase::SENSITIVE)) {
+    std::unique_ptr<base::Value> value;
+    if (DecodePRegValue(type, data, &value))
+      dict->SetValue(value_name, std::move(value));
+    return;
+  }
+
+  std::string data_utf8;
+  std::string action_trigger(base::ToLowerASCII(
+      value_name.substr(arraysize(kActionTriggerPrefix) - 1)));
+  if (action_trigger == kActionTriggerDeleteValues) {
+    if (DecodePRegStringValue(data, &data_utf8)) {
+      for (const std::string& value :
+           base::SplitString(data_utf8, ";", base::KEEP_WHITESPACE,
+                             base::SPLIT_WANT_NONEMPTY))
+        dict->RemoveValue(value);
+    }
+  } else if (base::StartsWith(action_trigger, kActionTriggerDeleteKeys,
+                              base::CompareCase::SENSITIVE)) {
+    if (DecodePRegStringValue(data, &data_utf8)) {
+      for (const std::string& key :
+           base::SplitString(data_utf8, ";", base::KEEP_WHITESPACE,
+                             base::SPLIT_WANT_NONEMPTY))
+        dict->RemoveKey(key);
+    }
+  } else if (base::StartsWith(action_trigger, kActionTriggerDel,
+                              base::CompareCase::SENSITIVE)) {
+    dict->RemoveValue(value_name.substr(arraysize(kActionTriggerPrefix) - 1 +
+                                        arraysize(kActionTriggerDel) - 1));
+  } else if (base::StartsWith(action_trigger, kActionTriggerDelVals,
+                              base::CompareCase::SENSITIVE)) {
+    // Delete all values.
+    dict->ClearValues();
+  } else if (base::StartsWith(action_trigger, kActionTriggerSecureKey,
+                              base::CompareCase::SENSITIVE) ||
+             base::StartsWith(action_trigger, kActionTriggerSoft,
+                              base::CompareCase::SENSITIVE)) {
+    // Doesn't affect values.
+  } else {
+    LOG(ERROR) << "Bad action trigger " << value_name;
+  }
+}
+
+}  // namespace
+
+namespace policy {
+namespace preg_parser {
+
+const char kPRegFileHeader[8] = {'P',    'R',    'e',    'g',
+                                 '\x01', '\x00', '\x00', '\x00'};
+
+bool ReadFile(const base::FilePath& file_path,
+              const base::string16& root,
+              RegistryDict* dict,
+              PolicyLoadStatusSampler* status) {
+  base::MemoryMappedFile mapped_file;
+  if (!mapped_file.Initialize(file_path) || !mapped_file.IsValid()) {
+    PLOG(ERROR) << "Failed to map " << file_path.value();
+    status->Add(POLICY_LOAD_STATUS_READ_ERROR);
+    return false;
+  }
+
+  return ReadDataInternal(
+      mapped_file.data(), mapped_file.length(), root, dict, status,
+      base::StringPrintf("file '%" PRIsFP "'", file_path.value().c_str()));
+}
+
+POLICY_EXPORT bool ReadDataInternal(const uint8_t* preg_data,
+                                    size_t preg_data_size,
+                                    const base::string16& root,
+                                    RegistryDict* dict,
+                                    PolicyLoadStatusSampler* status,
+                                    const std::string& debug_name) {
+  DCHECK(status);
+  DCHECK(root.empty() || root.back() != kRegistryPathSeparator[0]);
+
+  // Check data size.
+  if (preg_data_size > kMaxPRegFileSize) {
+    LOG(ERROR) << "PReg " << debug_name << " too large: " << preg_data_size;
+    status->Add(POLICY_LOAD_STATUS_TOO_BIG);
+    return false;
+  }
+
+  // Check the header.
+  const int kHeaderSize = arraysize(kPRegFileHeader);
+  if (!preg_data || preg_data_size < kHeaderSize ||
+      memcmp(kPRegFileHeader, preg_data, kHeaderSize) != 0) {
+    LOG(ERROR) << "Bad PReg " << debug_name;
+    status->Add(POLICY_LOAD_STATUS_PARSE_ERROR);
+    return false;
+  }
+
+  // Parse data, which is expected to be UCS-2 and little-endian. The latter I
+  // couldn't find documentation on, but the example I saw were all
+  // little-endian. It'd be interesting to check on big-endian hardware.
+  const uint8_t* cursor = preg_data + kHeaderSize;
+  const uint8_t* end = preg_data + preg_data_size;
+  while (true) {
+    if (cursor == end)
+      return true;
+
+    if (NextChar(&cursor, end) != kDelimBracketOpen)
+      break;
+
+    // Read the record fields.
+    base::string16 key_name;
+    base::string16 value;
+    uint32_t type = 0;
+    uint32_t size = 0;
+    std::vector<uint8_t> data;
+
+    if (!ReadFieldString(&cursor, end, &key_name))
+      break;
+
+    int current = NextChar(&cursor, end);
+    if (current == kDelimSemicolon) {
+      if (!ReadFieldString(&cursor, end, &value))
+        break;
+      current = NextChar(&cursor, end);
+    }
+
+    if (current == kDelimSemicolon) {
+      if (!ReadField32(&cursor, end, &type))
+        break;
+      current = NextChar(&cursor, end);
+    }
+
+    if (current == kDelimSemicolon) {
+      if (!ReadField32(&cursor, end, &size))
+        break;
+      current = NextChar(&cursor, end);
+    }
+
+    if (current == kDelimSemicolon) {
+      if (size > kMaxPRegFileSize)
+        break;
+      data.resize(size);
+      if (!ReadFieldBinary(&cursor, end, size, data.data()))
+        break;
+      current = NextChar(&cursor, end);
+    }
+
+    if (current != kDelimBracketClose)
+      break;
+
+    // Process the record if it is within the |root| subtree.
+    if (KeyRootEquals(key_name, root))
+      HandleRecord(key_name.substr(root.size()), value, type, data, dict);
+  }
+
+  LOG(ERROR) << "Error parsing PReg " << debug_name << " at offset "
+             << (reinterpret_cast<const uint8_t*>(cursor - 1) - preg_data);
+  status->Add(POLICY_LOAD_STATUS_PARSE_ERROR);
+  return false;
+}
+
+}  // namespace preg_parser
+}  // namespace policy
diff --git a/components/policy/core/common/preg_parser.h b/components/policy/core/common/preg_parser.h
new file mode 100644
index 0000000..cf9bb37
--- /dev/null
+++ b/components/policy/core/common/preg_parser.h
@@ -0,0 +1,56 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file provides a parser for PReg files which are used for storing group
+// policy settings in the file system. The file format is documented here:
+//
+// http://msdn.microsoft.com/en-us/library/windows/desktop/aa374407(v=vs.85).aspx
+
+#ifndef COMPONENTS_POLICY_CORE_COMMON_PREG_PARSER_H_
+#define COMPONENTS_POLICY_CORE_COMMON_PREG_PARSER_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/strings/string16.h"
+#include "components/policy/core/common/policy_load_status.h"
+#include "components/policy/policy_export.h"
+
+namespace base {
+class FilePath;
+}
+
+namespace policy {
+
+class RegistryDict;
+
+namespace preg_parser {
+
+// The magic header in PReg files: ASCII "PReg" + version (0x0001).
+POLICY_EXPORT extern const char kPRegFileHeader[8];
+
+// Reads the PReg file at |file_path| and writes the registry data to |dict|.
+// |root| specifies the registry subtree the caller is interested in, everything
+// else gets ignored. It may be empty if all keys should be returned, but it
+// must NOT end with a backslash.
+POLICY_EXPORT bool ReadFile(const base::FilePath& file_path,
+                            const base::string16& root,
+                            RegistryDict* dict,
+                            PolicyLoadStatusSampler* status);
+
+// Similar to ReadFile, but reads from |preg_data| of length |preg_data_size|
+// instead of a file. |debug_name| is printed out along with error messages.
+// Used internally and for testing only. All other callers should use ReadFile
+// instead.
+POLICY_EXPORT bool ReadDataInternal(const uint8_t* preg_data,
+                                    size_t preg_data_size,
+                                    const base::string16& root,
+                                    RegistryDict* dict,
+                                    PolicyLoadStatusSampler* status,
+                                    const std::string& debug_name);
+
+}  // namespace preg_parser
+}  // namespace policy
+
+#endif  // COMPONENTS_POLICY_CORE_COMMON_PREG_PARSER_H_
diff --git a/components/policy/core/common/preg_parser_fuzzer.cc b/components/policy/core/common/preg_parser_fuzzer.cc
new file mode 100644
index 0000000..003031f
--- /dev/null
+++ b/components/policy/core/common/preg_parser_fuzzer.cc
@@ -0,0 +1,45 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <base/strings/utf_string_conversions.h>
+#include "base/strings/string16.h"
+
+#include "components/policy/core/common/policy_load_status.h"
+#include "components/policy/core/common/preg_parser.h"
+#include "components/policy/core/common/registry_dict.h"
+
+namespace {
+
+const char kRegistryChromePolicyKey[] = "SOFTWARE\\Policies\\Chromium";
+
+}  // namespace
+
+namespace policy {
+namespace preg_parser {
+
+// Disable logging.
+struct Environment {
+  Environment() : root(base::ASCIIToUTF16(kRegistryChromePolicyKey)) {
+    logging::SetMinLogLevel(logging::LOG_FATAL);
+  }
+
+  const base::string16 root;
+};
+
+Environment* env = new Environment();
+
+// Entry point for LibFuzzer.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  // Note: Don't use PolicyLoadStatusUmaReporter here, it leaks!
+  PolicyLoadStatusSampler status;
+  RegistryDict dict;
+  ReadDataInternal(data, size, env->root, &dict, &status, "data");
+  return 0;
+}
+
+}  // namespace preg_parser
+}  // namespace policy
diff --git a/components/policy/core/common/preg_parser_unittest.cc b/components/policy/core/common/preg_parser_unittest.cc
new file mode 100644
index 0000000..43d84c2
--- /dev/null
+++ b/components/policy/core/common/preg_parser_unittest.cc
@@ -0,0 +1,194 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/policy/core/common/preg_parser.h"
+
+#include <utility>
+
+#include "base/base_paths.h"
+#include "base/bind.h"
+#include "base/files/file_path.h"
+#include "base/json/json_writer.h"
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/path_service.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "components/policy/core/common/policy_load_status.h"
+#include "components/policy/core/common/registry_dict.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace policy {
+namespace preg_parser {
+namespace {
+
+// Preg files are relative to |kRegistryPolBaseDir|.
+const char kRegistryPolBaseDir[] = "chrome/test/data/policy/gpo";
+const char kRegistryPolFile[] = "parser_test/registry.pol";
+const char kInvalidEncodingRegistryPolFile[] = "invalid_encoding/registry.pol";
+const char kNonExistingRegistryPolFile[] = "does_not_exist.pol";
+
+const char kRegistryKey[] = "SOFTWARE\\Policies\\Chromium";
+
+// Check whether two RegistryDicts equal each other.
+testing::AssertionResult RegistryDictEquals(const RegistryDict& a,
+                                            const RegistryDict& b) {
+  auto iter_key_a = a.keys().begin();
+  auto iter_key_b = b.keys().begin();
+  for (; iter_key_a != a.keys().end() && iter_key_b != b.keys().end();
+       ++iter_key_a, ++iter_key_b) {
+    if (iter_key_a->first != iter_key_b->first) {
+      return testing::AssertionFailure() << "Key mismatch " << iter_key_a->first
+                                         << " vs. " << iter_key_b->first;
+    }
+    testing::AssertionResult result =
+        RegistryDictEquals(*iter_key_a->second, *iter_key_b->second);
+    if (!result)
+      return result;
+  }
+  if (iter_key_a != a.keys().end())
+    return testing::AssertionFailure()
+           << "key mismatch, a has extra key " << iter_key_a->first;
+  if (iter_key_b != b.keys().end())
+    return testing::AssertionFailure()
+           << "key mismatch, b has extra key " << iter_key_b->first;
+
+  auto iter_value_a = a.values().begin();
+  auto iter_value_b = b.values().begin();
+  for (; iter_value_a != a.values().end() && iter_value_b != b.values().end();
+       ++iter_value_a, ++iter_value_b) {
+    if (iter_value_a->first != iter_value_b->first ||
+        *iter_value_a->second != *iter_value_b->second) {
+      return testing::AssertionFailure()
+             << "Value mismatch " << iter_value_a->first << "="
+             << *iter_value_a->second << " vs. " << iter_value_b->first << "="
+             << *iter_value_b->second;
+    }
+  }
+  if (iter_value_a != a.values().end())
+    return testing::AssertionFailure()
+           << "Value mismatch, a has extra value " << iter_value_a->first << "="
+           << *iter_value_a->second;
+  if (iter_value_b != b.values().end())
+    return testing::AssertionFailure()
+           << "Value mismatch, b has extra value " << iter_value_b->first << "="
+           << *iter_value_b->second;
+
+  return testing::AssertionSuccess();
+}
+
+void SetInteger(RegistryDict* dict, const std::string& name, int value) {
+  dict->SetValue(name, base::WrapUnique<base::Value>(new base::Value(value)));
+}
+
+void SetString(RegistryDict* dict,
+               const std::string& name,
+               const std::string& value) {
+  dict->SetValue(name, base::WrapUnique<base::Value>(new base::Value(value)));
+}
+
+class PRegParserTest : public testing::Test {
+ protected:
+  void SetUp() override {
+    ASSERT_TRUE(base::PathService::Get(base::DIR_SOURCE_ROOT, &test_data_dir_));
+    test_data_dir_ = test_data_dir_.AppendASCII(kRegistryPolBaseDir);
+  }
+
+  base::FilePath test_data_dir_;
+};
+
+TEST_F(PRegParserTest, TestParseFile) {
+  // Prepare the test dictionary with some data so the test can check that the
+  // PReg action triggers work, i.e. remove these items.
+  RegistryDict dict;
+  SetInteger(&dict, "DeleteValuesTest1", 1);
+  SetString(&dict, "DeleteValuesTest2", "2");
+  dict.SetKey("DeleteKeysTest1", std::make_unique<RegistryDict>());
+  std::unique_ptr<RegistryDict> delete_keys_test(new RegistryDict());
+  SetInteger(delete_keys_test.get(), "DeleteKeysTest2Entry", 1);
+  dict.SetKey("DeleteKeysTest2", std::move(delete_keys_test));
+  SetInteger(&dict, "DelTest", 1);
+  std::unique_ptr<RegistryDict> subdict(new RegistryDict());
+  SetInteger(subdict.get(), "DelValsTest1", 1);
+  SetString(subdict.get(), "DelValsTest2", "2");
+  subdict->SetKey("DelValsTest3", std::make_unique<RegistryDict>());
+  dict.SetKey("DelValsTest", std::move(subdict));
+
+  // Run the parser.
+  base::FilePath test_file(test_data_dir_.AppendASCII(kRegistryPolFile));
+  PolicyLoadStatusUmaReporter status;
+  ASSERT_TRUE(preg_parser::ReadFile(test_file, base::ASCIIToUTF16(kRegistryKey),
+                                    &dict, &status));
+
+  // Build the expected output dictionary.
+  RegistryDict expected;
+  std::unique_ptr<RegistryDict> del_vals_dict(new RegistryDict());
+  del_vals_dict->SetKey("DelValsTest3", std::make_unique<RegistryDict>());
+  expected.SetKey("DelValsTest", std::move(del_vals_dict));
+  SetInteger(&expected, "HomepageIsNewTabPage", 1);
+  SetString(&expected, "HomepageLocation", "http://www.example.com");
+  SetInteger(&expected, "RestoreOnStartup", 4);
+  std::unique_ptr<RegistryDict> startup_urls(new RegistryDict());
+  SetString(startup_urls.get(), "1", "http://www.chromium.org");
+  SetString(startup_urls.get(), "2", "http://www.example.com");
+  expected.SetKey("RestoreOnStartupURLs", std::move(startup_urls));
+  SetInteger(&expected, "ShowHomeButton", 1);
+  SetString(&expected, "Snowman", "\xE2\x98\x83");
+  SetString(&expected, "Empty", "");
+
+  EXPECT_TRUE(RegistryDictEquals(dict, expected));
+}
+
+TEST_F(PRegParserTest, SubstringRootInvalid) {
+  // A root of "Aa/Bb/Cc" should not be considered a valid root for a
+  // key like "Aa/Bb/C".
+  base::FilePath test_file(test_data_dir_.AppendASCII(kRegistryPolFile));
+  RegistryDict empty;
+  PolicyLoadStatusUmaReporter status;
+
+  // No data should be loaded for partial roots ("Aa/Bb/C").
+  RegistryDict dict1;
+  ASSERT_TRUE(preg_parser::ReadFile(
+      test_file, base::ASCIIToUTF16("SOFTWARE\\Policies\\Chro"), &dict1,
+      &status));
+  EXPECT_TRUE(RegistryDictEquals(dict1, empty));
+
+  // Safety check with kRegistryKey (dict should not be empty).
+  RegistryDict dict2;
+  ASSERT_TRUE(preg_parser::ReadFile(test_file, base::ASCIIToUTF16(kRegistryKey),
+                                    &dict2, &status));
+  EXPECT_FALSE(RegistryDictEquals(dict2, empty));
+}
+
+TEST_F(PRegParserTest, RejectInvalidStrings) {
+  // Tests whether strings with invalid characters are rejected.
+  base::FilePath test_file(
+      test_data_dir_.AppendASCII(kInvalidEncodingRegistryPolFile));
+  PolicyLoadStatusUmaReporter status;
+  RegistryDict dict;
+  ASSERT_TRUE(preg_parser::ReadFile(test_file, base::ASCIIToUTF16(kRegistryKey),
+                                    &dict, &status));
+
+  RegistryDict empty;
+  EXPECT_TRUE(RegistryDictEquals(dict, empty));
+}
+
+TEST_F(PRegParserTest, LoadStatusSampling) {
+  // Tests load status sampling.
+  PolicyLoadStatusUmaReporter status;
+  RegistryDict dict;
+  base::FilePath test_file(
+      test_data_dir_.AppendASCII(kNonExistingRegistryPolFile));
+  ASSERT_FALSE(preg_parser::ReadFile(
+      test_file, base::ASCIIToUTF16(kRegistryKey), &dict, &status));
+
+  PolicyLoadStatusSampler::StatusSet expected_status_set;
+  expected_status_set[POLICY_LOAD_STATUS_STARTED] = true;
+  expected_status_set[POLICY_LOAD_STATUS_READ_ERROR] = true;
+  EXPECT_EQ(expected_status_set, status.GetStatusSet());
+}
+
+}  // namespace
+}  // namespace preg_parser
+}  // namespace policy
diff --git a/components/policy/core/common/proxy_policy_provider.cc b/components/policy/core/common/proxy_policy_provider.cc
new file mode 100644
index 0000000..a1af2c2
--- /dev/null
+++ b/components/policy/core/common/proxy_policy_provider.cc
@@ -0,0 +1,65 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/policy/core/common/proxy_policy_provider.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/logging.h"
+#include "components/policy/core/common/policy_bundle.h"
+
+namespace policy {
+
+ProxyPolicyProvider::ProxyPolicyProvider() : delegate_(NULL) {}
+
+ProxyPolicyProvider::~ProxyPolicyProvider() {
+  DCHECK(!delegate_);
+}
+
+void ProxyPolicyProvider::SetDelegate(ConfigurationPolicyProvider* delegate) {
+  if (delegate_)
+    delegate_->RemoveObserver(this);
+  delegate_ = delegate;
+  if (delegate_) {
+    delegate_->AddObserver(this);
+    OnUpdatePolicy(delegate_);
+  } else {
+    UpdatePolicy(std::unique_ptr<PolicyBundle>(new PolicyBundle()));
+  }
+}
+
+void ProxyPolicyProvider::Shutdown() {
+  // Note: the delegate is not owned by the proxy provider, so this call is not
+  // forwarded. The same applies for the Init() call.
+  // Just drop the delegate without propagating updates here.
+  if (delegate_) {
+    delegate_->RemoveObserver(this);
+    delegate_ = NULL;
+  }
+  ConfigurationPolicyProvider::Shutdown();
+}
+
+void ProxyPolicyProvider::RefreshPolicies() {
+  if (delegate_) {
+    delegate_->RefreshPolicies();
+  } else {
+    // Subtle: if a RefreshPolicies() call comes after Shutdown() then the
+    // current bundle should be served instead. This also does the right thing
+    // if SetDelegate() was never called before.
+    std::unique_ptr<PolicyBundle> bundle(new PolicyBundle());
+    bundle->CopyFrom(policies());
+    UpdatePolicy(std::move(bundle));
+  }
+}
+
+void ProxyPolicyProvider::OnUpdatePolicy(
+    ConfigurationPolicyProvider* provider) {
+  DCHECK_EQ(delegate_, provider);
+  std::unique_ptr<PolicyBundle> bundle(new PolicyBundle());
+  bundle->CopyFrom(delegate_->policies());
+  UpdatePolicy(std::move(bundle));
+}
+
+}  // namespace policy
diff --git a/components/policy/core/common/proxy_policy_provider.h b/components/policy/core/common/proxy_policy_provider.h
new file mode 100644
index 0000000..2585d14
--- /dev/null
+++ b/components/policy/core/common/proxy_policy_provider.h
@@ -0,0 +1,64 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_POLICY_CORE_COMMON_PROXY_POLICY_PROVIDER_H_
+#define COMPONENTS_POLICY_CORE_COMMON_PROXY_POLICY_PROVIDER_H_
+
+#include "base/macros.h"
+#include "components/policy/core/common/configuration_policy_provider.h"
+#include "components/policy/policy_export.h"
+
+namespace policy {
+
+// A policy provider implementation that acts as a proxy for another policy
+// provider, swappable at any point.
+//
+// Note that ProxyPolicyProvider correctly forwards RefreshPolicies() calls to
+// the delegate if present. If there is no delegate, the refresh results in an
+// immediate (empty) policy update.
+//
+// Furthermore, IsInitializationComplete() is implemented trivially - it always
+// returns true. Given that the delegate may be swapped at any point, there's no
+// point in trying to carry over initialization status from the delegate.
+//
+// This policy provider implementation is used to inject browser-global policy
+// originating from the user policy configured on the primary Chrome OS user
+// (i.e. the user logging in from the login screen). This way, policy settings
+// on the primary user propagate into g_browser_process->local_state_().
+//
+// The bizarre situation of user-scoped policy settings which are implemented
+// browser-global wouldn't exist in an ideal world. However, for historic
+// and technical reasons there are policy settings that are scoped to the user
+// but are implemented to take effect for the entire browser instance. A good
+// example for this are policies that affect the Chrome network stack in areas
+// where there's no profile-specific context. The meta data in
+// policy_templates.json allows to identify the policies in this bucket; they'll
+// have per_profile set to False, supported_on including chrome_os, and
+// dynamic_refresh set to True.
+class POLICY_EXPORT ProxyPolicyProvider
+    : public ConfigurationPolicyProvider,
+      public ConfigurationPolicyProvider::Observer {
+ public:
+  ProxyPolicyProvider();
+  ~ProxyPolicyProvider() override;
+
+  // Updates the provider this proxy delegates to.
+  void SetDelegate(ConfigurationPolicyProvider* delegate);
+
+  // ConfigurationPolicyProvider:
+  void Shutdown() override;
+  void RefreshPolicies() override;
+
+  // ConfigurationPolicyProvider::Observer:
+  void OnUpdatePolicy(ConfigurationPolicyProvider* provider) override;
+
+ private:
+  ConfigurationPolicyProvider* delegate_;
+
+  DISALLOW_COPY_AND_ASSIGN(ProxyPolicyProvider);
+};
+
+}  // namespace policy
+
+#endif  // COMPONENTS_POLICY_CORE_COMMON_PROXY_POLICY_PROVIDER_H_
diff --git a/components/policy/core/common/proxy_policy_provider_unittest.cc b/components/policy/core/common/proxy_policy_provider_unittest.cc
new file mode 100644
index 0000000..fcd9317
--- /dev/null
+++ b/components/policy/core/common/proxy_policy_provider_unittest.cc
@@ -0,0 +1,102 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/policy/core/common/proxy_policy_provider.h"
+#include <memory>
+#include "base/callback.h"
+#include "base/macros.h"
+#include "components/policy/core/common/external_data_fetcher.h"
+#include "components/policy/core/common/mock_configuration_policy_provider.h"
+#include "components/policy/core/common/policy_types.h"
+#include "components/policy/core/common/schema_registry.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::Mock;
+
+namespace policy {
+
+class ProxyPolicyProviderTest : public testing::Test {
+ protected:
+  ProxyPolicyProviderTest() {
+    mock_provider_.Init();
+    proxy_provider_.Init(&schema_registry_);
+    proxy_provider_.AddObserver(&observer_);
+  }
+
+  ~ProxyPolicyProviderTest() override {
+    proxy_provider_.RemoveObserver(&observer_);
+    proxy_provider_.Shutdown();
+    mock_provider_.Shutdown();
+  }
+
+  SchemaRegistry schema_registry_;
+  MockConfigurationPolicyObserver observer_;
+  MockConfigurationPolicyProvider mock_provider_;
+  ProxyPolicyProvider proxy_provider_;
+
+  static std::unique_ptr<PolicyBundle> CopyBundle(const PolicyBundle& bundle) {
+    std::unique_ptr<PolicyBundle> copy(new PolicyBundle());
+    copy->CopyFrom(bundle);
+    return copy;
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ProxyPolicyProviderTest);
+};
+
+TEST_F(ProxyPolicyProviderTest, Init) {
+  EXPECT_TRUE(proxy_provider_.IsInitializationComplete(POLICY_DOMAIN_CHROME));
+  EXPECT_TRUE(PolicyBundle().Equals(proxy_provider_.policies()));
+}
+
+TEST_F(ProxyPolicyProviderTest, Delegate) {
+  PolicyBundle bundle;
+  bundle.Get(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()))
+      .Set("policy", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+           POLICY_SOURCE_CLOUD, std::make_unique<base::Value>("value"),
+           nullptr);
+  mock_provider_.UpdatePolicy(CopyBundle(bundle));
+
+  EXPECT_CALL(observer_, OnUpdatePolicy(&proxy_provider_));
+  proxy_provider_.SetDelegate(&mock_provider_);
+  Mock::VerifyAndClearExpectations(&observer_);
+  EXPECT_TRUE(bundle.Equals(proxy_provider_.policies()));
+
+  EXPECT_CALL(observer_, OnUpdatePolicy(&proxy_provider_));
+  bundle.Get(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()))
+      .Set("policy", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+           POLICY_SOURCE_CLOUD, std::make_unique<base::Value>("new value"),
+           nullptr);
+  mock_provider_.UpdatePolicy(CopyBundle(bundle));
+  Mock::VerifyAndClearExpectations(&observer_);
+  EXPECT_TRUE(bundle.Equals(proxy_provider_.policies()));
+
+  EXPECT_CALL(observer_, OnUpdatePolicy(&proxy_provider_));
+  proxy_provider_.SetDelegate(NULL);
+  EXPECT_TRUE(PolicyBundle().Equals(proxy_provider_.policies()));
+}
+
+TEST_F(ProxyPolicyProviderTest, RefreshPolicies) {
+  EXPECT_CALL(observer_, OnUpdatePolicy(&proxy_provider_));
+  proxy_provider_.RefreshPolicies();
+  Mock::VerifyAndClearExpectations(&observer_);
+
+  EXPECT_CALL(observer_, OnUpdatePolicy(&proxy_provider_));
+  proxy_provider_.SetDelegate(&mock_provider_);
+  Mock::VerifyAndClearExpectations(&observer_);
+
+  EXPECT_CALL(observer_, OnUpdatePolicy(&proxy_provider_)).Times(0);
+  EXPECT_CALL(mock_provider_, RefreshPolicies());
+  proxy_provider_.RefreshPolicies();
+  Mock::VerifyAndClearExpectations(&observer_);
+  Mock::VerifyAndClearExpectations(&mock_provider_);
+
+  EXPECT_CALL(observer_, OnUpdatePolicy(&proxy_provider_));
+  mock_provider_.UpdatePolicy(
+      std::unique_ptr<PolicyBundle>(new PolicyBundle()));
+  Mock::VerifyAndClearExpectations(&observer_);
+}
+
+}  // namespace policy
diff --git a/components/policy/core/common/schema_map.cc b/components/policy/core/common/schema_map.cc
new file mode 100644
index 0000000..0779cd3
--- /dev/null
+++ b/components/policy/core/common/schema_map.cc
@@ -0,0 +1,118 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/policy/core/common/schema_map.h"
+
+#include "base/logging.h"
+#include "base/values.h"
+#include "components/policy/core/common/policy_bundle.h"
+#include "components/policy/core/common/policy_map.h"
+
+namespace policy {
+
+SchemaMap::SchemaMap() {}
+
+SchemaMap::SchemaMap(DomainMap& map) {
+  map_.swap(map);
+}
+
+SchemaMap::~SchemaMap() {}
+
+const DomainMap& SchemaMap::GetDomains() const {
+  return map_;
+}
+
+const ComponentMap* SchemaMap::GetComponents(PolicyDomain domain) const {
+  const auto it = map_.find(domain);
+  return it == map_.end() ? nullptr : &it->second;
+}
+
+const Schema* SchemaMap::GetSchema(const PolicyNamespace& ns) const {
+  const ComponentMap* map = GetComponents(ns.domain);
+  if (!map)
+    return nullptr;
+  const auto it = map->find(ns.component_id);
+  return it == map->end() ? nullptr : &it->second;
+}
+
+void SchemaMap::FilterBundle(PolicyBundle* bundle) const {
+  for (const auto& bundle_item : *bundle) {
+    const PolicyNamespace& ns = bundle_item.first;
+    const std::unique_ptr<PolicyMap>& policy_map = bundle_item.second;
+
+    // Chrome policies are not filtered, so that typos appear in about:policy.
+    // Everything else gets filtered, so that components only see valid policy.
+    if (ns.domain == POLICY_DOMAIN_CHROME)
+      continue;
+
+    const Schema* schema = GetSchema(ns);
+
+    if (!schema) {
+      policy_map->Clear();
+      continue;
+    }
+
+    if (!schema->valid()) {
+      // Don't serve unknown policies.
+      policy_map->Clear();
+      continue;
+    }
+
+    for (auto it_map = policy_map->begin(); it_map != policy_map->end();) {
+      const std::string& policy_name = it_map->first;
+      const base::Value* policy_value = it_map->second.value.get();
+      Schema policy_schema = schema->GetProperty(policy_name);
+      ++it_map;
+      std::string error_path;
+      std::string error;
+      if (!policy_value ||
+          !policy_schema.Validate(*policy_value,
+                                  SCHEMA_STRICT,
+                                  &error_path,
+                                  &error)) {
+        LOG(ERROR) << "Dropping policy " << policy_name << " of component "
+                   << ns.component_id << " due to error at "
+                   << (error_path.empty() ? "root" : error_path) << ": "
+                   << error;
+        policy_map->Erase(policy_name);
+      }
+    }
+  }
+}
+
+bool SchemaMap::HasComponents() const {
+  for (const auto& item : map_) {
+    const PolicyDomain& domain = item.first;
+    const ComponentMap& component_map = item.second;
+    if (domain == POLICY_DOMAIN_CHROME)
+      continue;
+    if (!component_map.empty())
+      return true;
+  }
+  return false;
+}
+
+void SchemaMap::GetChanges(const scoped_refptr<SchemaMap>& older,
+                           PolicyNamespaceList* removed,
+                           PolicyNamespaceList* added) const {
+  GetNamespacesNotInOther(older.get(), added);
+  older->GetNamespacesNotInOther(this, removed);
+}
+
+void SchemaMap::GetNamespacesNotInOther(const SchemaMap* other,
+                                        PolicyNamespaceList* list) const {
+  list->clear();
+  for (const auto& item : map_) {
+    const PolicyDomain& domain = item.first;
+    const ComponentMap& component_map = item.second;
+    for (const auto& comp : component_map) {
+      const std::string& component_id = comp.first;
+      const PolicyNamespace ns(domain, component_id);
+      if (!other->GetSchema(ns))
+        list->push_back(ns);
+    }
+  }
+}
+
+}  // namespace policy
diff --git a/components/policy/core/common/schema_map.h b/components/policy/core/common/schema_map.h
new file mode 100644
index 0000000..835941e
--- /dev/null
+++ b/components/policy/core/common/schema_map.h
@@ -0,0 +1,68 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_POLICY_CORE_COMMON_SCHEMA_MAP_H_
+#define COMPONENTS_POLICY_CORE_COMMON_SCHEMA_MAP_H_
+
+#include <map>
+#include <string>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "components/policy/core/common/policy_namespace.h"
+#include "components/policy/core/common/schema.h"
+#include "components/policy/policy_export.h"
+
+namespace policy {
+
+class PolicyBundle;
+
+// Maps component id (e.g. extension id) to schema.
+typedef std::map<std::string, Schema> ComponentMap;
+typedef std::map<PolicyDomain, ComponentMap> DomainMap;
+
+// Contains a mapping of policy namespaces (domain + component ID) to its
+// corresponding Schema.
+// This class is thread-safe.
+class POLICY_EXPORT SchemaMap : public base::RefCountedThreadSafe<SchemaMap> {
+ public:
+  SchemaMap();
+  // Takes ownership of |map| (its contents will be swapped).
+  // TODO(emaxx): Change to use move semantics.
+  explicit SchemaMap(DomainMap& map);
+
+  const DomainMap& GetDomains() const;
+
+  const ComponentMap* GetComponents(PolicyDomain domain) const;
+
+  const Schema* GetSchema(const PolicyNamespace& ns) const;
+
+  // Removes all the policies in |bundle| that don't match the known schemas.
+  // Unknown components are also dropped.
+  void FilterBundle(PolicyBundle* bundle) const;
+
+  // Returns true if this map contains at least one component of a domain other
+  // than POLICY_DOMAIN_CHROME.
+  bool HasComponents() const;
+
+  void GetChanges(const scoped_refptr<SchemaMap>& older,
+                  PolicyNamespaceList* removed,
+                  PolicyNamespaceList* added) const;
+
+ private:
+  friend class base::RefCountedThreadSafe<SchemaMap>;
+
+  void GetNamespacesNotInOther(const SchemaMap* other,
+                               PolicyNamespaceList* list) const;
+
+  ~SchemaMap();
+
+  DomainMap map_;
+
+  DISALLOW_COPY_AND_ASSIGN(SchemaMap);
+};
+
+}  // namespace policy
+
+#endif  // COMPONENTS_POLICY_CORE_COMMON_SCHEMA_MAP_H_
diff --git a/components/policy/core/common/schema_map_unittest.cc b/components/policy/core/common/schema_map_unittest.cc
new file mode 100644
index 0000000..96916e5
--- /dev/null
+++ b/components/policy/core/common/schema_map_unittest.cc
@@ -0,0 +1,308 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/policy/core/common/schema_map.h"
+#include <memory>
+
+#include "base/memory/weak_ptr.h"
+#include "base/values.h"
+#include "components/policy/core/common/external_data_fetcher.h"
+#include "components/policy/core/common/external_data_manager.h"
+#include "components/policy/core/common/policy_bundle.h"
+#include "components/policy/core/common/policy_map.h"
+#include "components/policy/core/common/policy_types.h"
+#include "components/policy/core/common/schema.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace policy {
+
+namespace {
+
+const char kTestSchema[] =
+    "{"
+    "  \"type\": \"object\","
+    "  \"properties\": {"
+    "    \"string\": { \"type\": \"string\" },"
+    "    \"integer\": { \"type\": \"integer\" },"
+    "    \"boolean\": { \"type\": \"boolean\" },"
+    "    \"null\": { \"type\": \"null\" },"
+    "    \"double\": { \"type\": \"number\" },"
+    "    \"list\": {"
+    "      \"type\": \"array\","
+    "      \"items\": { \"type\": \"string\" }"
+    "    },"
+    "    \"object\": {"
+    "      \"type\": \"object\","
+    "      \"properties\": {"
+    "        \"a\": { \"type\": \"string\" },"
+    "        \"b\": { \"type\": \"integer\" }"
+    "      }"
+    "    }"
+    "  }"
+    "}";
+
+}  // namespace
+
+class SchemaMapTest : public testing::Test {
+ protected:
+  Schema CreateTestSchema() {
+    std::string error;
+    Schema schema = Schema::Parse(kTestSchema, &error);
+    if (!schema.valid())
+      ADD_FAILURE() << error;
+    return schema;
+  }
+
+  scoped_refptr<SchemaMap> CreateTestMap() {
+    Schema schema = CreateTestSchema();
+    ComponentMap component_map;
+    component_map["extension-1"] = schema;
+    component_map["extension-2"] = schema;
+    component_map["legacy-extension"] = Schema();
+
+    DomainMap domain_map;
+    domain_map[POLICY_DOMAIN_EXTENSIONS] = component_map;
+
+    return new SchemaMap(domain_map);
+  }
+};
+
+TEST_F(SchemaMapTest, Empty) {
+  scoped_refptr<SchemaMap> map = new SchemaMap();
+  EXPECT_TRUE(map->GetDomains().empty());
+  EXPECT_FALSE(map->GetComponents(POLICY_DOMAIN_CHROME));
+  EXPECT_FALSE(map->GetComponents(POLICY_DOMAIN_EXTENSIONS));
+  EXPECT_FALSE(map->GetSchema(PolicyNamespace(POLICY_DOMAIN_CHROME, "")));
+  EXPECT_FALSE(map->HasComponents());
+}
+
+TEST_F(SchemaMapTest, HasComponents) {
+  scoped_refptr<SchemaMap> map = new SchemaMap();
+  EXPECT_FALSE(map->HasComponents());
+
+  // The Chrome schema does not count as a component.
+  Schema schema = CreateTestSchema();
+  ComponentMap component_map;
+  component_map[""] = schema;
+  DomainMap domain_map;
+  domain_map[POLICY_DOMAIN_CHROME] = component_map;
+  map = new SchemaMap(domain_map);
+  EXPECT_FALSE(map->HasComponents());
+
+  // An extension schema does.
+  domain_map[POLICY_DOMAIN_EXTENSIONS] = component_map;
+  map = new SchemaMap(domain_map);
+  EXPECT_TRUE(map->HasComponents());
+}
+
+TEST_F(SchemaMapTest, Lookups) {
+  scoped_refptr<SchemaMap> map = CreateTestMap();
+  ASSERT_TRUE(map.get());
+  EXPECT_TRUE(map->HasComponents());
+
+  EXPECT_FALSE(map->GetSchema(
+      PolicyNamespace(POLICY_DOMAIN_CHROME, "")));
+  EXPECT_FALSE(map->GetSchema(
+      PolicyNamespace(POLICY_DOMAIN_CHROME, "extension-1")));
+  EXPECT_FALSE(map->GetSchema(
+      PolicyNamespace(POLICY_DOMAIN_CHROME, "legacy-extension")));
+  EXPECT_FALSE(map->GetSchema(
+      PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "")));
+  EXPECT_FALSE(map->GetSchema(
+      PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "extension-3")));
+
+  const Schema* schema =
+      map->GetSchema(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "extension-1"));
+  ASSERT_TRUE(schema);
+  EXPECT_TRUE(schema->valid());
+
+  schema = map->GetSchema(
+      PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "legacy-extension"));
+  ASSERT_TRUE(schema);
+  EXPECT_FALSE(schema->valid());
+}
+
+TEST_F(SchemaMapTest, FilterBundle) {
+  std::string error;
+  Schema schema = Schema::Parse(kTestSchema, &error);
+  ASSERT_TRUE(schema.valid()) << error;
+
+  DomainMap domain_map;
+  domain_map[POLICY_DOMAIN_EXTENSIONS]["abc"] = schema;
+  scoped_refptr<SchemaMap> schema_map = new SchemaMap(domain_map);
+
+  PolicyBundle bundle;
+  schema_map->FilterBundle(&bundle);
+  const PolicyBundle empty_bundle;
+  EXPECT_TRUE(bundle.Equals(empty_bundle));
+
+  // The Chrome namespace isn't filtered.
+  PolicyBundle expected_bundle;
+  PolicyNamespace chrome_ns(POLICY_DOMAIN_CHROME, "");
+  expected_bundle.Get(chrome_ns).Set(
+      "ChromePolicy", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+      POLICY_SOURCE_CLOUD, std::make_unique<base::Value>("value"), nullptr);
+  bundle.CopyFrom(expected_bundle);
+
+  // Unknown components are filtered out.
+  PolicyNamespace another_extension_ns(POLICY_DOMAIN_EXTENSIONS, "xyz");
+  bundle.Get(another_extension_ns)
+      .Set("AnotherExtensionPolicy", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+           POLICY_SOURCE_CLOUD, std::make_unique<base::Value>("value"),
+           nullptr);
+  schema_map->FilterBundle(&bundle);
+  EXPECT_TRUE(bundle.Equals(expected_bundle));
+
+  PolicyNamespace extension_ns(POLICY_DOMAIN_EXTENSIONS, "abc");
+  PolicyMap& map = expected_bundle.Get(extension_ns);
+  base::ListValue list;
+  list.AppendString("a");
+  list.AppendString("b");
+  map.Set("list", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+          POLICY_SOURCE_CLOUD, list.CreateDeepCopy(), nullptr);
+  map.Set("boolean", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+          POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(true), nullptr);
+  map.Set("integer", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+          POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(1), nullptr);
+  map.Set("null", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+          POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(), nullptr);
+  map.Set("double", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+          POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(1.2), nullptr);
+  base::DictionaryValue dict;
+  dict.SetString("a", "b");
+  dict.SetInteger("b", 2);
+  map.Set("object", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+          POLICY_SOURCE_CLOUD, dict.CreateDeepCopy(), nullptr);
+  map.Set("string", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+          POLICY_SOURCE_CLOUD, std::make_unique<base::Value>("value"), nullptr);
+
+  bundle.MergeFrom(expected_bundle);
+  bundle.Get(extension_ns)
+      .Set("Unexpected", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+           POLICY_SOURCE_CLOUD, std::make_unique<base::Value>("to-be-removed"),
+           nullptr);
+
+  schema_map->FilterBundle(&bundle);
+  EXPECT_TRUE(bundle.Equals(expected_bundle));
+
+  // Mismatched types are also removed.
+  bundle.Clear();
+  PolicyMap& badmap = bundle.Get(extension_ns);
+  badmap.Set("list", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+             POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(false),
+             nullptr);
+  badmap.Set("boolean", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+             POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(0), nullptr);
+  badmap.Set("integer", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+             POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(false),
+             nullptr);
+  badmap.Set("null", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+             POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(false),
+             nullptr);
+  badmap.Set("double", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+             POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(false),
+             nullptr);
+  badmap.Set("object", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+             POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(false),
+             nullptr);
+  badmap.Set("string", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+             POLICY_SOURCE_CLOUD, nullptr,
+             std::make_unique<ExternalDataFetcher>(nullptr, std::string()));
+
+  schema_map->FilterBundle(&bundle);
+  EXPECT_TRUE(bundle.Equals(empty_bundle));
+}
+
+TEST_F(SchemaMapTest, LegacyComponents) {
+  std::string error;
+  Schema schema = Schema::Parse(
+      "{"
+      "  \"type\":\"object\","
+      "  \"properties\": {"
+      "    \"String\": { \"type\": \"string\" }"
+      "  }"
+      "}", &error);
+  ASSERT_TRUE(schema.valid()) << error;
+
+  DomainMap domain_map;
+  domain_map[POLICY_DOMAIN_EXTENSIONS]["with-schema"] = schema;
+  domain_map[POLICY_DOMAIN_EXTENSIONS]["without-schema"] = Schema();
+  scoped_refptr<SchemaMap> schema_map = new SchemaMap(domain_map);
+
+  // |bundle| contains policies loaded by a policy provider.
+  PolicyBundle bundle;
+
+  // Known components with schemas are filtered.
+  PolicyNamespace extension_ns(POLICY_DOMAIN_EXTENSIONS, "with-schema");
+  bundle.Get(extension_ns)
+      .Set("String", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+           POLICY_SOURCE_CLOUD, std::make_unique<base::Value>("value 1"),
+           nullptr);
+
+  // The Chrome namespace isn't filtered.
+  PolicyNamespace chrome_ns(POLICY_DOMAIN_CHROME, "");
+  bundle.Get(chrome_ns).Set("ChromePolicy", POLICY_LEVEL_MANDATORY,
+                            POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD,
+                            std::make_unique<base::Value>("value 3"), nullptr);
+
+  PolicyBundle expected_bundle;
+  expected_bundle.MergeFrom(bundle);
+
+  // Known components without a schema are filtered out completely.
+  PolicyNamespace without_schema_ns(POLICY_DOMAIN_EXTENSIONS, "without-schema");
+  bundle.Get(without_schema_ns)
+      .Set("Schemaless", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+           POLICY_SOURCE_CLOUD, std::make_unique<base::Value>("value 2"),
+           nullptr);
+
+  // Unknown policies of known components with a schema are removed.
+  bundle.Get(extension_ns)
+      .Set("Surprise", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+           POLICY_SOURCE_CLOUD, std::make_unique<base::Value>("value 4"),
+           nullptr);
+
+  // Unknown components are removed.
+  PolicyNamespace unknown_ns(POLICY_DOMAIN_EXTENSIONS, "unknown");
+  bundle.Get(unknown_ns)
+      .Set("Surprise", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+           POLICY_SOURCE_CLOUD, std::make_unique<base::Value>("value 5"),
+           nullptr);
+
+  schema_map->FilterBundle(&bundle);
+  EXPECT_TRUE(bundle.Equals(expected_bundle));
+}
+
+TEST_F(SchemaMapTest, GetChanges) {
+  DomainMap map;
+  map[POLICY_DOMAIN_CHROME][""] = Schema();
+  scoped_refptr<SchemaMap> older = new SchemaMap(map);
+  map[POLICY_DOMAIN_CHROME][""] = Schema();
+  scoped_refptr<SchemaMap> newer = new SchemaMap(map);
+
+  PolicyNamespaceList removed;
+  PolicyNamespaceList added;
+  newer->GetChanges(older, &removed, &added);
+  EXPECT_TRUE(removed.empty());
+  EXPECT_TRUE(added.empty());
+
+  map[POLICY_DOMAIN_CHROME][""] = Schema();
+  map[POLICY_DOMAIN_EXTENSIONS]["xyz"] = Schema();
+  newer = new SchemaMap(map);
+  newer->GetChanges(older, &removed, &added);
+  EXPECT_TRUE(removed.empty());
+  ASSERT_EQ(1u, added.size());
+  EXPECT_EQ(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "xyz"), added[0]);
+
+  older = newer;
+  map[POLICY_DOMAIN_EXTENSIONS]["abc"] = Schema();
+  newer = new SchemaMap(map);
+  newer->GetChanges(older, &removed, &added);
+  ASSERT_EQ(2u, removed.size());
+  EXPECT_EQ(PolicyNamespace(POLICY_DOMAIN_CHROME, ""), removed[0]);
+  EXPECT_EQ(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "xyz"), removed[1]);
+  ASSERT_EQ(1u, added.size());
+  EXPECT_EQ(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "abc"), added[0]);
+}
+
+}  // namespace policy
diff --git a/components/policy/core/common/schema_registry.cc b/components/policy/core/common/schema_registry.cc
new file mode 100644
index 0000000..e44f3d7
--- /dev/null
+++ b/components/policy/core/common/schema_registry.cc
@@ -0,0 +1,278 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/policy/core/common/schema_registry.h"
+
+#include "base/logging.h"
+#include "extensions/buildflags/buildflags.h"
+
+namespace policy {
+
+SchemaRegistry::Observer::~Observer() {}
+
+SchemaRegistry::InternalObserver::~InternalObserver() {}
+
+SchemaRegistry::SchemaRegistry() : schema_map_(new SchemaMap) {
+  for (int i = 0; i < POLICY_DOMAIN_SIZE; ++i)
+    domains_ready_[i] = false;
+#if !BUILDFLAG(ENABLE_EXTENSIONS)
+  SetExtensionsDomainsReady();
+#endif
+}
+
+SchemaRegistry::~SchemaRegistry() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  for (auto& observer : internal_observers_)
+    observer.OnSchemaRegistryShuttingDown(this);
+}
+
+void SchemaRegistry::RegisterComponent(const PolicyNamespace& ns,
+                                       const Schema& schema) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  ComponentMap map;
+  map[ns.component_id] = schema;
+  RegisterComponents(ns.domain, map);
+}
+
+void SchemaRegistry::RegisterComponents(PolicyDomain domain,
+                                        const ComponentMap& components) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  // Don't issue notifications if nothing is being registered.
+  if (components.empty())
+    return;
+  // Assume that a schema was updated if the namespace was already registered
+  // before.
+  DomainMap map(schema_map_->GetDomains());
+  for (ComponentMap::const_iterator it = components.begin();
+       it != components.end(); ++it) {
+    map[domain][it->first] = it->second;
+  }
+  schema_map_ = new SchemaMap(map);
+  Notify(true);
+}
+
+void SchemaRegistry::UnregisterComponent(const PolicyNamespace& ns) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DomainMap map(schema_map_->GetDomains());
+  if (map[ns.domain].erase(ns.component_id) != 0) {
+    schema_map_ = new SchemaMap(map);
+    Notify(false);
+  } else {
+    NOTREACHED();
+  }
+}
+
+bool SchemaRegistry::IsReady() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  for (int i = 0; i < POLICY_DOMAIN_SIZE; ++i) {
+    if (!domains_ready_[i])
+      return false;
+  }
+  return true;
+}
+
+void SchemaRegistry::SetDomainReady(PolicyDomain domain) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (domains_ready_[domain])
+    return;
+  domains_ready_[domain] = true;
+  if (IsReady()) {
+    for (auto& observer : observers_)
+      observer.OnSchemaRegistryReady();
+  }
+}
+
+void SchemaRegistry::SetAllDomainsReady() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  for (int i = 0; i < POLICY_DOMAIN_SIZE; ++i)
+    SetDomainReady(static_cast<PolicyDomain>(i));
+}
+
+void SchemaRegistry::SetExtensionsDomainsReady() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  SetDomainReady(POLICY_DOMAIN_EXTENSIONS);
+  SetDomainReady(POLICY_DOMAIN_SIGNIN_EXTENSIONS);
+}
+
+void SchemaRegistry::AddObserver(Observer* observer) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  observers_.AddObserver(observer);
+}
+
+void SchemaRegistry::RemoveObserver(Observer* observer) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  observers_.RemoveObserver(observer);
+}
+
+void SchemaRegistry::AddInternalObserver(InternalObserver* observer) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  internal_observers_.AddObserver(observer);
+}
+
+void SchemaRegistry::RemoveInternalObserver(InternalObserver* observer) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  internal_observers_.RemoveObserver(observer);
+}
+
+void SchemaRegistry::Notify(bool has_new_schemas) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  for (auto& observer : observers_)
+    observer.OnSchemaRegistryUpdated(has_new_schemas);
+}
+
+CombinedSchemaRegistry::CombinedSchemaRegistry()
+    : own_schema_map_(new SchemaMap) {
+  // The combined registry is always ready, since it can always start tracking
+  // another registry that is not ready yet and going from "ready" to "not
+  // ready" is not allowed.
+  SetAllDomainsReady();
+}
+
+CombinedSchemaRegistry::~CombinedSchemaRegistry() {}
+
+void CombinedSchemaRegistry::Track(SchemaRegistry* registry) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  registries_.insert(registry);
+  registry->AddObserver(this);
+  registry->AddInternalObserver(this);
+  // Recombine the maps only if the |registry| has any components other than
+  // POLICY_DOMAIN_CHROME.
+  if (registry->schema_map()->HasComponents())
+    Combine(true);
+}
+
+void CombinedSchemaRegistry::RegisterComponents(
+    PolicyDomain domain,
+    const ComponentMap& components) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DomainMap map(own_schema_map_->GetDomains());
+  for (ComponentMap::const_iterator it = components.begin();
+       it != components.end(); ++it) {
+    map[domain][it->first] = it->second;
+  }
+  own_schema_map_ = new SchemaMap(map);
+  Combine(true);
+}
+
+void CombinedSchemaRegistry::UnregisterComponent(const PolicyNamespace& ns) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DomainMap map(own_schema_map_->GetDomains());
+  if (map[ns.domain].erase(ns.component_id) != 0) {
+    own_schema_map_ = new SchemaMap(map);
+    Combine(false);
+  } else {
+    NOTREACHED();
+  }
+}
+
+void CombinedSchemaRegistry::OnSchemaRegistryUpdated(bool has_new_schemas) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  Combine(has_new_schemas);
+}
+
+void CombinedSchemaRegistry::OnSchemaRegistryShuttingDown(
+    SchemaRegistry* registry) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  registry->RemoveObserver(this);
+  registry->RemoveInternalObserver(this);
+  if (registries_.erase(registry) != 0) {
+    if (registry->schema_map()->HasComponents())
+      Combine(false);
+  } else {
+    NOTREACHED();
+  }
+}
+
+void CombinedSchemaRegistry::Combine(bool has_new_schemas) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  // If two registries publish a Schema for the same component then it's
+  // undefined which version gets in the combined registry.
+  //
+  // The common case is that both registries want policy for the same component,
+  // and the Schemas should be the same; in that case this makes no difference.
+  //
+  // But if the Schemas are different then one of the components is out of date.
+  // In that case the policy loaded will be valid only for one of them, until
+  // the outdated components are updated. This is a known limitation of the
+  // way policies are loaded currently, but isn't a problem worth fixing for
+  // the time being.
+  DomainMap map(own_schema_map_->GetDomains());
+  for (std::set<SchemaRegistry*>::const_iterator reg_it = registries_.begin();
+       reg_it != registries_.end(); ++reg_it) {
+    const DomainMap& reg_domain_map = (*reg_it)->schema_map()->GetDomains();
+    for (DomainMap::const_iterator domain_it = reg_domain_map.begin();
+         domain_it != reg_domain_map.end(); ++domain_it) {
+      const ComponentMap& reg_component_map = domain_it->second;
+      for (ComponentMap::const_iterator comp_it = reg_component_map.begin();
+           comp_it != reg_component_map.end(); ++comp_it) {
+        map[domain_it->first][comp_it->first] = comp_it->second;
+      }
+    }
+  }
+  schema_map_ = new SchemaMap(map);
+  Notify(has_new_schemas);
+}
+
+ForwardingSchemaRegistry::ForwardingSchemaRegistry(SchemaRegistry* wrapped)
+    : wrapped_(wrapped) {
+  schema_map_ = wrapped_->schema_map();
+  wrapped_->AddObserver(this);
+  wrapped_->AddInternalObserver(this);
+  UpdateReadiness();
+}
+
+ForwardingSchemaRegistry::~ForwardingSchemaRegistry() {
+  if (wrapped_) {
+    wrapped_->RemoveObserver(this);
+    wrapped_->RemoveInternalObserver(this);
+  }
+}
+
+void ForwardingSchemaRegistry::RegisterComponents(
+    PolicyDomain domain,
+    const ComponentMap& components) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  // POLICY_DOMAIN_CHROME is skipped to avoid spurious updates when a new
+  // Profile is created. If the ForwardingSchemaRegistry is used outside
+  // device-level accounts then this should become configurable.
+  if (wrapped_ && domain != POLICY_DOMAIN_CHROME)
+    wrapped_->RegisterComponents(domain, components);
+  // Ignore otherwise.
+}
+
+void ForwardingSchemaRegistry::UnregisterComponent(const PolicyNamespace& ns) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (wrapped_)
+    wrapped_->UnregisterComponent(ns);
+  // Ignore otherwise.
+}
+
+void ForwardingSchemaRegistry::OnSchemaRegistryUpdated(bool has_new_schemas) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  schema_map_ = wrapped_->schema_map();
+  Notify(has_new_schemas);
+}
+
+void ForwardingSchemaRegistry::OnSchemaRegistryReady() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  UpdateReadiness();
+}
+
+void ForwardingSchemaRegistry::OnSchemaRegistryShuttingDown(
+    SchemaRegistry* registry) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_EQ(wrapped_, registry);
+  wrapped_->RemoveObserver(this);
+  wrapped_->RemoveInternalObserver(this);
+  wrapped_ = nullptr;
+  // Keep serving the same |schema_map_|.
+}
+
+void ForwardingSchemaRegistry::UpdateReadiness() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (wrapped_->IsReady())
+    SetAllDomainsReady();
+}
+
+}  // namespace policy
diff --git a/components/policy/core/common/schema_registry.h b/components/policy/core/common/schema_registry.h
new file mode 100644
index 0000000..a1d26a5
--- /dev/null
+++ b/components/policy/core/common/schema_registry.h
@@ -0,0 +1,170 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_POLICY_CORE_COMMON_SCHEMA_REGISTRY_H_
+#define COMPONENTS_POLICY_CORE_COMMON_SCHEMA_REGISTRY_H_
+
+#include <set>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/observer_list.h"
+#include "base/sequence_checker.h"
+#include "components/policy/core/common/policy_namespace.h"
+#include "components/policy/core/common/schema.h"
+#include "components/policy/core/common/schema_map.h"
+#include "components/policy/policy_export.h"
+
+namespace policy {
+
+class SchemaMap;
+
+// Holds the main reference to the current SchemaMap, and allows a list of
+// observers to get notified whenever it is updated.
+// This object is not thread safe and must be used from the owner's thread,
+// usually UI.
+class POLICY_EXPORT SchemaRegistry {
+ public:
+  class POLICY_EXPORT Observer {
+   public:
+    // Invoked whenever schemas are registered or unregistered.
+    // |has_new_schemas| is true if a new component has been registered since
+    // the last update; this allows observers to ignore updates when
+    // components are unregistered but still get a handle to the current map
+    // (e.g. for periodic reloads).
+    virtual void OnSchemaRegistryUpdated(bool has_new_schemas) = 0;
+
+    // Invoked when all policy domains become ready.
+    virtual void OnSchemaRegistryReady() {}
+
+   protected:
+    virtual ~Observer();
+  };
+
+  // This observer is only meant to be used by subclasses.
+  class POLICY_EXPORT InternalObserver {
+   public:
+    // Invoked when |registry| is about to be destroyed.
+    virtual void OnSchemaRegistryShuttingDown(SchemaRegistry* registry) = 0;
+
+   protected:
+    virtual ~InternalObserver();
+  };
+
+  SchemaRegistry();
+  virtual ~SchemaRegistry();
+
+  const scoped_refptr<SchemaMap>& schema_map() const { return schema_map_; }
+
+  // Register a single component.
+  void RegisterComponent(const PolicyNamespace& ns,
+                         const Schema& schema);
+
+  // Register a list of components for a given domain.
+  virtual void RegisterComponents(PolicyDomain domain,
+                                  const ComponentMap& components);
+
+  virtual void UnregisterComponent(const PolicyNamespace& ns);
+
+  // Returns true if all domains have registered the initial components.
+  bool IsReady() const;
+
+  // This indicates that the initial components for |domain| have all been
+  // registered. It must be invoked at least once for each policy domain;
+  // subsequent calls for the same domain are ignored.
+  void SetDomainReady(PolicyDomain domain);
+  // This is equivalent to calling |SetDomainReady| with each of the policy
+  // domains.
+  void SetAllDomainsReady();
+  // This is equivalent to calling |SetDomainReady| with each of the domains
+  // that correspond to policy for extensions.
+  void SetExtensionsDomainsReady();
+
+  void AddObserver(Observer* observer);
+  void RemoveObserver(Observer* observer);
+
+  void AddInternalObserver(InternalObserver* observer);
+  void RemoveInternalObserver(InternalObserver* observer);
+
+ protected:
+  void Notify(bool has_new_schemas);
+
+  SEQUENCE_CHECKER(sequence_checker_);
+
+  scoped_refptr<SchemaMap> schema_map_;
+
+ private:
+  base::ObserverList<Observer, true> observers_;
+  base::ObserverList<InternalObserver, true> internal_observers_;
+  bool domains_ready_[POLICY_DOMAIN_SIZE];
+
+  DISALLOW_COPY_AND_ASSIGN(SchemaRegistry);
+};
+
+// A registry that combines the maps of other registries.
+class POLICY_EXPORT CombinedSchemaRegistry
+    : public SchemaRegistry,
+      public SchemaRegistry::Observer,
+      public SchemaRegistry::InternalObserver {
+ public:
+  CombinedSchemaRegistry();
+  ~CombinedSchemaRegistry() override;
+
+  void Track(SchemaRegistry* registry);
+
+  // SchemaRegistry:
+  void RegisterComponents(PolicyDomain domain,
+                          const ComponentMap& components) override;
+  void UnregisterComponent(const PolicyNamespace& ns) override;
+
+  // SchemaRegistry::Observer:
+  void OnSchemaRegistryUpdated(bool has_new_schemas) override;
+
+  // SchemaRegistry::InternalObserver:
+  void OnSchemaRegistryShuttingDown(SchemaRegistry* registry) override;
+
+ private:
+  void Combine(bool has_new_schemas);
+
+  std::set<SchemaRegistry*> registries_;
+  scoped_refptr<SchemaMap> own_schema_map_;
+
+  DISALLOW_COPY_AND_ASSIGN(CombinedSchemaRegistry);
+};
+
+// A registry that wraps another schema registry.
+class POLICY_EXPORT ForwardingSchemaRegistry
+    : public SchemaRegistry,
+      public SchemaRegistry::Observer,
+      public SchemaRegistry::InternalObserver {
+ public:
+  // This registry will stop updating its SchemaMap when |wrapped| is
+  // destroyed.
+  explicit ForwardingSchemaRegistry(SchemaRegistry* wrapped);
+  ~ForwardingSchemaRegistry() override;
+
+  // SchemaRegistry:
+  void RegisterComponents(PolicyDomain domain,
+                          const ComponentMap& components) override;
+  void UnregisterComponent(const PolicyNamespace& ns) override;
+
+  // SchemaRegistry::Observer:
+  void OnSchemaRegistryUpdated(bool has_new_schemas) override;
+  void OnSchemaRegistryReady() override;
+
+  // SchemaRegistry::InternalObserver:
+  void OnSchemaRegistryShuttingDown(SchemaRegistry* registry) override;
+
+ private:
+  void UpdateReadiness();
+
+  SchemaRegistry* wrapped_;
+
+  DISALLOW_COPY_AND_ASSIGN(ForwardingSchemaRegistry);
+};
+
+}  // namespace policy
+
+#endif  // COMPONENTS_POLICY_CORE_COMMON_SCHEMA_REGISTRY_H_
diff --git a/components/policy/core/common/schema_registry_tracking_policy_provider.cc b/components/policy/core/common/schema_registry_tracking_policy_provider.cc
new file mode 100644
index 0000000..5f15b33
--- /dev/null
+++ b/components/policy/core/common/schema_registry_tracking_policy_provider.cc
@@ -0,0 +1,99 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/policy/core/common/schema_registry_tracking_policy_provider.h"
+
+#include <utility>
+
+#include "components/policy/core/common/schema_map.h"
+#include "components/policy/core/common/schema_registry.h"
+
+namespace policy {
+
+SchemaRegistryTrackingPolicyProvider::SchemaRegistryTrackingPolicyProvider(
+    ConfigurationPolicyProvider* delegate)
+    : delegate_(delegate), state_(WAITING_FOR_REGISTRY_READY) {
+  delegate_->AddObserver(this);
+  // Serve the initial |delegate_| policies.
+  OnUpdatePolicy(delegate_);
+}
+
+SchemaRegistryTrackingPolicyProvider::~SchemaRegistryTrackingPolicyProvider() {
+  delegate_->RemoveObserver(this);
+}
+
+void SchemaRegistryTrackingPolicyProvider::Init(SchemaRegistry* registry) {
+  ConfigurationPolicyProvider::Init(registry);
+  if (registry->IsReady())
+    OnSchemaRegistryReady();
+}
+
+bool SchemaRegistryTrackingPolicyProvider::IsInitializationComplete(
+    PolicyDomain domain) const {
+  if (domain == POLICY_DOMAIN_CHROME)
+    return delegate_->IsInitializationComplete(domain);
+  // This provider keeps its own state for all the other domains.
+  return state_ == READY;
+}
+
+void SchemaRegistryTrackingPolicyProvider::RefreshPolicies() {
+  delegate_->RefreshPolicies();
+}
+
+void SchemaRegistryTrackingPolicyProvider::OnSchemaRegistryReady() {
+  DCHECK_EQ(WAITING_FOR_REGISTRY_READY, state_);
+  // This provider's registry is ready, meaning that it has all the initial
+  // components schemas; the delegate's registry should also see them now,
+  // since it's tracking the former.
+  // Asking the delegate to RefreshPolicies now means that the next
+  // OnUpdatePolicy from the delegate will have the initial policy for
+  // components.
+  if (!schema_map()->HasComponents()) {
+    // If there are no component registered for this provider then there's no
+    // need to reload.
+    state_ = READY;
+    OnUpdatePolicy(delegate_);
+    return;
+  }
+
+  state_ = WAITING_FOR_REFRESH;
+  RefreshPolicies();
+}
+
+void SchemaRegistryTrackingPolicyProvider::OnSchemaRegistryUpdated(
+    bool has_new_schemas) {
+  if (state_ != READY)
+    return;
+  if (has_new_schemas) {
+    RefreshPolicies();
+  } else {
+    // Remove the policies that were being served for the component that have
+    // been removed. This is important so that update notifications are also
+    // sent in case those component are reinstalled during the current session.
+    OnUpdatePolicy(delegate_);
+  }
+}
+
+void SchemaRegistryTrackingPolicyProvider::OnUpdatePolicy(
+    ConfigurationPolicyProvider* provider) {
+  DCHECK_EQ(delegate_, provider);
+
+  if (state_ == WAITING_FOR_REFRESH)
+    state_ = READY;
+
+  std::unique_ptr<PolicyBundle> bundle(new PolicyBundle());
+  if (state_ == READY) {
+    bundle->CopyFrom(delegate_->policies());
+    schema_map()->FilterBundle(bundle.get());
+  } else {
+    // Always pass on the Chrome policy, even if the components are not ready
+    // yet.
+    const PolicyNamespace chrome_ns(POLICY_DOMAIN_CHROME, "");
+    bundle->Get(chrome_ns).CopyFrom(delegate_->policies().Get(chrome_ns));
+  }
+
+  UpdatePolicy(std::move(bundle));
+}
+
+}  // namespace policy
diff --git a/components/policy/core/common/schema_registry_tracking_policy_provider.h b/components/policy/core/common/schema_registry_tracking_policy_provider.h
new file mode 100644
index 0000000..1ec64c3
--- /dev/null
+++ b/components/policy/core/common/schema_registry_tracking_policy_provider.h
@@ -0,0 +1,92 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_POLICY_CORE_COMMON_SCHEMA_REGISTRY_TRACKING_POLICY_PROVIDER_H_
+#define COMPONENTS_POLICY_CORE_COMMON_SCHEMA_REGISTRY_TRACKING_POLICY_PROVIDER_H_
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "components/policy/core/common/configuration_policy_provider.h"
+#include "components/policy/core/common/policy_namespace.h"
+#include "components/policy/policy_export.h"
+
+namespace policy {
+
+// A policy provider that relies on a delegate provider to obtain policy
+// settings, but uses a different SchemaRegistry to determine which policy
+// namespaces to request from the delegate provider.
+//
+// This provider tracks the SchemaRegistry's state, and becomes ready after
+// making sure the delegate provider has refreshed its policies with an updated
+// view of the complete schema. It is expected that the delegate's
+// SchemaRegistry is a CombinedSchemaRegistry tracking the
+// SchemaRegistryTrackingPolicyProvider's registry.
+//
+// This policy provider implementation is used to wrap the platform policy
+// provider for use with individual profiles, which may have different
+// SchemaRegistries. The SchemaRegistryTrackingPolicyProvider ensures that
+// initialization completion is only signaled for non-Chrome PolicyDomains after
+// the SchemaRegistry is fully initialized. This is important to avoid flapping
+// on startup due to asynchronous SchemaRegistry initialization while the
+// underlying policy provider has already completed initialization.
+//
+// A concrete example of this is POLICY_DOMAIN_EXTENSIONS, which registers
+// the PolicyNamespaces for the different extensions it's interested in based
+// on what extensions are installed in a Profile. Before that happens, the
+// underlying policy providers will not load the corresponding policy, so at
+// startup there would be a window during which the policy appears to be not
+// present. This is avoided by only flagging POLICY_DOMAIN_EXTENSIONS ready
+// once the corresponding SchemaRegistry has been fully initialized with the
+// list of installed extensions.
+class POLICY_EXPORT SchemaRegistryTrackingPolicyProvider
+    : public ConfigurationPolicyProvider,
+      public ConfigurationPolicyProvider::Observer {
+ public:
+  // The |delegate| must outlive this provider.
+  explicit SchemaRegistryTrackingPolicyProvider(
+      ConfigurationPolicyProvider* delegate);
+  ~SchemaRegistryTrackingPolicyProvider() override;
+
+  // ConfigurationPolicyProvider:
+  //
+  // Note that Init() and Shutdown() are not forwarded to the |delegate_|, since
+  // this provider does not own it and its up to the |delegate_|'s owner to
+  // initialize it and shut it down.
+  //
+  // Note also that this provider may have a SchemaRegistry passed in Init()
+  // that doesn't match the |delegate_|'s; therefore OnSchemaRegistryUpdated()
+  // and OnSchemaRegistryReady() are not forwarded either. It is assumed that
+  // the |delegate_|'s SchemaRegistry contains a superset of this provider's
+  // SchemaRegistry though (i.e. it's a CombinedSchemaRegistry that contains
+  // this provider's SchemaRegistry).
+  //
+  // This provider manages its own initialization state for all policy domains
+  // except POLICY_DOMAIN_CHROME, whose status is always queried from the
+  // |delegate_|. RefreshPolicies() calls are also forwarded, since this
+  // provider doesn't have a "real" policy source of its own.
+  void Init(SchemaRegistry* registry) override;
+  bool IsInitializationComplete(PolicyDomain domain) const override;
+  void RefreshPolicies() override;
+  void OnSchemaRegistryReady() override;
+  void OnSchemaRegistryUpdated(bool has_new_schemas) override;
+
+  // ConfigurationPolicyProvider::Observer:
+  void OnUpdatePolicy(ConfigurationPolicyProvider* provider) override;
+
+ private:
+  enum InitializationState {
+    WAITING_FOR_REGISTRY_READY,
+    WAITING_FOR_REFRESH,
+    READY,
+  };
+
+  ConfigurationPolicyProvider* delegate_;
+  InitializationState state_;
+
+  DISALLOW_COPY_AND_ASSIGN(SchemaRegistryTrackingPolicyProvider);
+};
+
+}  // namespace policy
+
+#endif  // COMPONENTS_POLICY_CORE_COMMON_SCHEMA_REGISTRY_TRACKING_POLICY_PROVIDER_H_
diff --git a/components/policy/core/common/schema_registry_tracking_policy_provider_unittest.cc b/components/policy/core/common/schema_registry_tracking_policy_provider_unittest.cc
new file mode 100644
index 0000000..5def498
--- /dev/null
+++ b/components/policy/core/common/schema_registry_tracking_policy_provider_unittest.cc
@@ -0,0 +1,244 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/policy/core/common/schema_registry_tracking_policy_provider.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "base/values.h"
+#include "components/policy/core/common/mock_configuration_policy_provider.h"
+#include "components/policy/core/common/policy_bundle.h"
+#include "components/policy/core/common/policy_map.h"
+#include "components/policy/core/common/policy_types.h"
+#include "components/policy/core/common/schema.h"
+#include "components/policy/core/common/schema_registry.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::Mock;
+using testing::Return;
+using testing::_;
+
+namespace policy {
+
+namespace {
+
+const char kTestSchema[] =
+    "{"
+    "  \"type\": \"object\","
+    "  \"properties\": {"
+    "    \"foo\": { \"type\": \"string\" }"
+    "  }"
+    "}";
+
+}  // namespace
+
+class SchemaRegistryTrackingPolicyProviderTest : public testing::Test {
+ protected:
+  SchemaRegistryTrackingPolicyProviderTest()
+      : schema_registry_tracking_provider_(&mock_provider_) {
+    mock_provider_.Init();
+    schema_registry_tracking_provider_.Init(&schema_registry_);
+    schema_registry_tracking_provider_.AddObserver(&observer_);
+  }
+
+  ~SchemaRegistryTrackingPolicyProviderTest() override {
+    schema_registry_tracking_provider_.RemoveObserver(&observer_);
+    schema_registry_tracking_provider_.Shutdown();
+    mock_provider_.Shutdown();
+  }
+
+  Schema CreateTestSchema() {
+    std::string error;
+    Schema schema = Schema::Parse(kTestSchema, &error);
+    if (!schema.valid())
+      ADD_FAILURE() << error;
+    return schema;
+  }
+
+  SchemaRegistry schema_registry_;
+  MockConfigurationPolicyObserver observer_;
+  MockConfigurationPolicyProvider mock_provider_;
+  SchemaRegistryTrackingPolicyProvider schema_registry_tracking_provider_;
+};
+
+TEST_F(SchemaRegistryTrackingPolicyProviderTest, Empty) {
+  EXPECT_FALSE(schema_registry_.IsReady());
+  EXPECT_FALSE(schema_registry_tracking_provider_.IsInitializationComplete(
+      POLICY_DOMAIN_EXTENSIONS));
+
+  EXPECT_CALL(mock_provider_, IsInitializationComplete(POLICY_DOMAIN_CHROME))
+      .WillOnce(Return(false));
+  EXPECT_FALSE(schema_registry_tracking_provider_.IsInitializationComplete(
+      POLICY_DOMAIN_CHROME));
+  Mock::VerifyAndClearExpectations(&mock_provider_);
+
+  const PolicyBundle empty_bundle;
+  EXPECT_TRUE(
+      schema_registry_tracking_provider_.policies().Equals(empty_bundle));
+}
+
+TEST_F(SchemaRegistryTrackingPolicyProviderTest, PassOnChromePolicy) {
+  PolicyBundle bundle;
+  const PolicyNamespace chrome_ns(POLICY_DOMAIN_CHROME, "");
+  bundle.Get(chrome_ns).Set("policy", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+                            POLICY_SOURCE_CLOUD,
+                            std::make_unique<base::Value>("visible"), nullptr);
+
+  EXPECT_CALL(observer_, OnUpdatePolicy(&schema_registry_tracking_provider_));
+  std::unique_ptr<PolicyBundle> delegate_bundle(new PolicyBundle);
+  delegate_bundle->CopyFrom(bundle);
+  delegate_bundle->Get(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "xyz"))
+      .Set("foo", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+           POLICY_SOURCE_CLOUD, std::make_unique<base::Value>("not visible"),
+           nullptr);
+  mock_provider_.UpdatePolicy(std::move(delegate_bundle));
+  Mock::VerifyAndClearExpectations(&observer_);
+
+  EXPECT_FALSE(schema_registry_tracking_provider_.IsInitializationComplete(
+      POLICY_DOMAIN_EXTENSIONS));
+  EXPECT_TRUE(schema_registry_tracking_provider_.policies().Equals(bundle));
+}
+
+TEST_F(SchemaRegistryTrackingPolicyProviderTest, RefreshPolicies) {
+  EXPECT_CALL(mock_provider_, RefreshPolicies());
+  schema_registry_tracking_provider_.RefreshPolicies();
+  Mock::VerifyAndClearExpectations(&mock_provider_);
+}
+
+TEST_F(SchemaRegistryTrackingPolicyProviderTest, SchemaReady) {
+  EXPECT_CALL(observer_, OnUpdatePolicy(&schema_registry_tracking_provider_));
+  schema_registry_.SetAllDomainsReady();
+  Mock::VerifyAndClearExpectations(&observer_);
+
+  EXPECT_TRUE(schema_registry_tracking_provider_.IsInitializationComplete(
+      policy::POLICY_DOMAIN_EXTENSIONS));
+}
+
+TEST_F(SchemaRegistryTrackingPolicyProviderTest, SchemaReadyWithComponents) {
+  PolicyMap policy_map;
+  policy_map.Set("foo", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+                 POLICY_SOURCE_CLOUD, std::make_unique<base::Value>("omg"),
+                 nullptr);
+  std::unique_ptr<PolicyBundle> bundle(new PolicyBundle);
+  bundle->Get(PolicyNamespace(POLICY_DOMAIN_CHROME, "")).CopyFrom(policy_map);
+  bundle->Get(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "xyz"))
+      .CopyFrom(policy_map);
+  EXPECT_CALL(observer_, OnUpdatePolicy(&schema_registry_tracking_provider_));
+  mock_provider_.UpdatePolicy(std::move(bundle));
+  Mock::VerifyAndClearExpectations(&observer_);
+
+  EXPECT_CALL(mock_provider_, RefreshPolicies()).Times(0);
+  schema_registry_.RegisterComponent(
+      PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "xyz"), CreateTestSchema());
+  schema_registry_.SetExtensionsDomainsReady();
+  Mock::VerifyAndClearExpectations(&mock_provider_);
+
+  EXPECT_CALL(mock_provider_, RefreshPolicies());
+  schema_registry_.SetDomainReady(POLICY_DOMAIN_CHROME);
+  Mock::VerifyAndClearExpectations(&mock_provider_);
+
+  EXPECT_FALSE(schema_registry_tracking_provider_.IsInitializationComplete(
+      policy::POLICY_DOMAIN_EXTENSIONS));
+  PolicyBundle expected_bundle;
+  expected_bundle.Get(PolicyNamespace(POLICY_DOMAIN_CHROME, ""))
+      .CopyFrom(policy_map);
+  EXPECT_TRUE(
+      schema_registry_tracking_provider_.policies().Equals(expected_bundle));
+
+  EXPECT_CALL(observer_, OnUpdatePolicy(&schema_registry_tracking_provider_));
+  schema_registry_tracking_provider_.OnUpdatePolicy(&mock_provider_);
+  Mock::VerifyAndClearExpectations(&observer_);
+
+  EXPECT_TRUE(schema_registry_tracking_provider_.IsInitializationComplete(
+      policy::POLICY_DOMAIN_EXTENSIONS));
+  expected_bundle.Get(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "xyz"))
+      .CopyFrom(policy_map);
+  EXPECT_TRUE(
+      schema_registry_tracking_provider_.policies().Equals(expected_bundle));
+}
+
+TEST_F(SchemaRegistryTrackingPolicyProviderTest, DelegateUpdates) {
+  schema_registry_.RegisterComponent(
+      PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "xyz"), CreateTestSchema());
+  EXPECT_FALSE(schema_registry_.IsReady());
+  EXPECT_FALSE(schema_registry_tracking_provider_.IsInitializationComplete(
+      policy::POLICY_DOMAIN_EXTENSIONS));
+
+  PolicyMap policy_map;
+  policy_map.Set("foo", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+                 POLICY_SOURCE_CLOUD, std::make_unique<base::Value>("omg"),
+                 nullptr);
+  // Chrome policy updates are visible even if the components aren't ready.
+  EXPECT_CALL(observer_, OnUpdatePolicy(&schema_registry_tracking_provider_));
+  mock_provider_.UpdateChromePolicy(policy_map);
+  Mock::VerifyAndClearExpectations(&observer_);
+
+  EXPECT_CALL(mock_provider_, RefreshPolicies());
+  schema_registry_.SetAllDomainsReady();
+  EXPECT_TRUE(schema_registry_.IsReady());
+  Mock::VerifyAndClearExpectations(&mock_provider_);
+  EXPECT_FALSE(schema_registry_tracking_provider_.IsInitializationComplete(
+      policy::POLICY_DOMAIN_EXTENSIONS));
+
+  // The provider becomes ready after this refresh completes, and policy updates
+  // are visible after that.
+  EXPECT_CALL(observer_, OnUpdatePolicy(_));
+  mock_provider_.UpdateChromePolicy(policy_map);
+  Mock::VerifyAndClearExpectations(&observer_);
+
+  EXPECT_TRUE(schema_registry_tracking_provider_.IsInitializationComplete(
+      policy::POLICY_DOMAIN_EXTENSIONS));
+
+  // Updates continue to be visible.
+  EXPECT_CALL(observer_, OnUpdatePolicy(_));
+  mock_provider_.UpdateChromePolicy(policy_map);
+  Mock::VerifyAndClearExpectations(&observer_);
+}
+
+TEST_F(SchemaRegistryTrackingPolicyProviderTest, RemoveAndAddComponent) {
+  EXPECT_CALL(mock_provider_, RefreshPolicies());
+  const PolicyNamespace ns(POLICY_DOMAIN_EXTENSIONS, "xyz");
+  schema_registry_.RegisterComponent(ns, CreateTestSchema());
+  schema_registry_.SetAllDomainsReady();
+  Mock::VerifyAndClearExpectations(&mock_provider_);
+
+  // Serve policy for |ns|.
+  PolicyBundle platform_policy;
+  platform_policy.Get(ns).Set("foo", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+                              POLICY_SOURCE_CLOUD,
+                              std::make_unique<base::Value>("omg"), nullptr);
+  std::unique_ptr<PolicyBundle> copy(new PolicyBundle);
+  copy->CopyFrom(platform_policy);
+  EXPECT_CALL(observer_, OnUpdatePolicy(_));
+  mock_provider_.UpdatePolicy(std::move(copy));
+  Mock::VerifyAndClearExpectations(&observer_);
+  EXPECT_TRUE(
+      schema_registry_tracking_provider_.policies().Equals(platform_policy));
+
+  // Now remove that component.
+  EXPECT_CALL(observer_, OnUpdatePolicy(_));
+  schema_registry_.UnregisterComponent(ns);
+  Mock::VerifyAndClearExpectations(&observer_);
+  const PolicyBundle empty;
+  EXPECT_TRUE(schema_registry_tracking_provider_.policies().Equals(empty));
+
+  // Adding it back should serve the current policies again, even though they
+  // haven't changed on the platform provider.
+  EXPECT_CALL(mock_provider_, RefreshPolicies());
+  schema_registry_.RegisterComponent(ns, CreateTestSchema());
+  Mock::VerifyAndClearExpectations(&mock_provider_);
+
+  EXPECT_CALL(observer_, OnUpdatePolicy(_));
+  copy.reset(new PolicyBundle);
+  copy->CopyFrom(platform_policy);
+  mock_provider_.UpdatePolicy(std::move(copy));
+  Mock::VerifyAndClearExpectations(&observer_);
+  EXPECT_TRUE(
+      schema_registry_tracking_provider_.policies().Equals(platform_policy));
+}
+
+}  // namespace policy
diff --git a/components/policy/core/common/schema_registry_unittest.cc b/components/policy/core/common/schema_registry_unittest.cc
new file mode 100644
index 0000000..aea54d1
--- /dev/null
+++ b/components/policy/core/common/schema_registry_unittest.cc
@@ -0,0 +1,329 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/policy/core/common/schema_registry.h"
+
+#include <memory>
+
+#include "components/policy/core/common/policy_namespace.h"
+#include "components/policy/core/common/schema.h"
+#include "extensions/buildflags/buildflags.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::Mock;
+using ::testing::_;
+
+namespace policy {
+
+namespace {
+
+const char kTestSchema[] =
+    "{"
+    "  \"type\": \"object\","
+    "  \"properties\": {"
+    "    \"string\": { \"type\": \"string\" },"
+    "    \"integer\": { \"type\": \"integer\" },"
+    "    \"boolean\": { \"type\": \"boolean\" },"
+    "    \"null\": { \"type\": \"null\" },"
+    "    \"double\": { \"type\": \"number\" },"
+    "    \"list\": {"
+    "      \"type\": \"array\","
+    "      \"items\": { \"type\": \"string\" }"
+    "    },"
+    "    \"object\": {"
+    "      \"type\": \"object\","
+    "      \"properties\": {"
+    "        \"a\": { \"type\": \"string\" },"
+    "        \"b\": { \"type\": \"integer\" }"
+    "      }"
+    "    }"
+    "  }"
+    "}";
+
+class MockSchemaRegistryObserver : public SchemaRegistry::Observer {
+ public:
+  MockSchemaRegistryObserver() {}
+  ~MockSchemaRegistryObserver() override {}
+
+  MOCK_METHOD1(OnSchemaRegistryUpdated, void(bool));
+  MOCK_METHOD0(OnSchemaRegistryReady, void());
+};
+
+bool SchemaMapEquals(const scoped_refptr<SchemaMap>& schema_map1,
+                     const scoped_refptr<SchemaMap>& schema_map2) {
+  PolicyNamespaceList added;
+  PolicyNamespaceList removed;
+  schema_map1->GetChanges(schema_map2, &removed, &added);
+  return added.empty() && removed.empty();
+}
+
+}  // namespace
+
+TEST(SchemaRegistryTest, Notifications) {
+  std::string error;
+  Schema schema = Schema::Parse(kTestSchema, &error);
+  ASSERT_TRUE(schema.valid()) << error;
+
+  MockSchemaRegistryObserver observer;
+  SchemaRegistry registry;
+  registry.AddObserver(&observer);
+
+  ASSERT_TRUE(registry.schema_map().get());
+  EXPECT_FALSE(registry.schema_map()->GetSchema(
+      PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "abc")));
+
+  EXPECT_CALL(observer, OnSchemaRegistryUpdated(true));
+  registry.RegisterComponent(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "abc"),
+                             schema);
+  Mock::VerifyAndClearExpectations(&observer);
+
+  // Re-register also triggers notifications, because the Schema might have
+  // changed.
+  EXPECT_CALL(observer, OnSchemaRegistryUpdated(true));
+  registry.RegisterComponent(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "abc"),
+                             schema);
+  Mock::VerifyAndClearExpectations(&observer);
+
+  EXPECT_TRUE(registry.schema_map()->GetSchema(
+      PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "abc")));
+
+  EXPECT_CALL(observer, OnSchemaRegistryUpdated(false));
+  registry.UnregisterComponent(
+      PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "abc"));
+  Mock::VerifyAndClearExpectations(&observer);
+
+  EXPECT_FALSE(registry.schema_map()->GetSchema(
+      PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "abc")));
+
+  // Registering multiple components at once issues only one notification.
+  ComponentMap components;
+  components["abc"] = schema;
+  components["def"] = schema;
+  components["xyz"] = schema;
+  EXPECT_CALL(observer, OnSchemaRegistryUpdated(true));
+  registry.RegisterComponents(POLICY_DOMAIN_EXTENSIONS, components);
+  Mock::VerifyAndClearExpectations(&observer);
+
+  registry.RemoveObserver(&observer);
+}
+
+TEST(SchemaRegistryTest, IsReady) {
+  SchemaRegistry registry;
+  MockSchemaRegistryObserver observer;
+  registry.AddObserver(&observer);
+
+  EXPECT_FALSE(registry.IsReady());
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+  EXPECT_CALL(observer, OnSchemaRegistryReady()).Times(0);
+  registry.SetExtensionsDomainsReady();
+  Mock::VerifyAndClearExpectations(&observer);
+  EXPECT_FALSE(registry.IsReady());
+#endif
+  EXPECT_CALL(observer, OnSchemaRegistryReady());
+  registry.SetDomainReady(POLICY_DOMAIN_CHROME);
+  Mock::VerifyAndClearExpectations(&observer);
+  EXPECT_TRUE(registry.IsReady());
+  EXPECT_CALL(observer, OnSchemaRegistryReady()).Times(0);
+  registry.SetDomainReady(POLICY_DOMAIN_CHROME);
+  Mock::VerifyAndClearExpectations(&observer);
+  EXPECT_TRUE(registry.IsReady());
+
+  CombinedSchemaRegistry combined;
+  EXPECT_TRUE(combined.IsReady());
+
+  registry.RemoveObserver(&observer);
+}
+
+TEST(SchemaRegistryTest, Combined) {
+  std::string error;
+  Schema schema = Schema::Parse(kTestSchema, &error);
+  ASSERT_TRUE(schema.valid()) << error;
+
+  MockSchemaRegistryObserver observer;
+  std::unique_ptr<SchemaRegistry> registry1(new SchemaRegistry);
+  std::unique_ptr<SchemaRegistry> registry2(new SchemaRegistry);
+  CombinedSchemaRegistry combined;
+  combined.AddObserver(&observer);
+
+  EXPECT_CALL(observer, OnSchemaRegistryUpdated(_)).Times(0);
+  registry1->RegisterComponent(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "abc"),
+                               schema);
+  Mock::VerifyAndClearExpectations(&observer);
+
+  // Starting to track a registry issues notifications when it comes with new
+  // schemas.
+  EXPECT_CALL(observer, OnSchemaRegistryUpdated(true));
+  combined.Track(registry1.get());
+  Mock::VerifyAndClearExpectations(&observer);
+
+  // Adding a new empty registry does not trigger notifications.
+  EXPECT_CALL(observer, OnSchemaRegistryUpdated(_)).Times(0);
+  combined.Track(registry2.get());
+  Mock::VerifyAndClearExpectations(&observer);
+
+  // Adding the same component to the combined registry itself triggers
+  // notifications.
+  EXPECT_CALL(observer, OnSchemaRegistryUpdated(true));
+  combined.RegisterComponent(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "abc"),
+                             schema);
+  Mock::VerifyAndClearExpectations(&observer);
+
+  // Adding components to the sub-registries triggers notifications.
+  EXPECT_CALL(observer, OnSchemaRegistryUpdated(true));
+  registry2->RegisterComponent(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "def"),
+                               schema);
+  Mock::VerifyAndClearExpectations(&observer);
+
+  // If the same component is published in 2 sub-registries then the combined
+  // registry publishes one of them.
+  EXPECT_CALL(observer, OnSchemaRegistryUpdated(true));
+  registry1->RegisterComponent(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "def"),
+                               schema);
+  Mock::VerifyAndClearExpectations(&observer);
+
+  ASSERT_EQ(1u, combined.schema_map()->GetDomains().size());
+  ASSERT_TRUE(combined.schema_map()->GetComponents(POLICY_DOMAIN_EXTENSIONS));
+  ASSERT_EQ(
+      2u,
+      combined.schema_map()->GetComponents(POLICY_DOMAIN_EXTENSIONS)->size());
+  EXPECT_TRUE(combined.schema_map()->GetSchema(
+      PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "abc")));
+  EXPECT_TRUE(combined.schema_map()->GetSchema(
+      PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "def")));
+  EXPECT_FALSE(combined.schema_map()->GetSchema(
+      PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "xyz")));
+
+  EXPECT_CALL(observer, OnSchemaRegistryUpdated(false));
+  registry1->UnregisterComponent(
+      PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "abc"));
+  Mock::VerifyAndClearExpectations(&observer);
+  // Still registered at the combined registry.
+  EXPECT_TRUE(combined.schema_map()->GetSchema(
+      PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "abc")));
+
+  EXPECT_CALL(observer, OnSchemaRegistryUpdated(false));
+  combined.UnregisterComponent(
+      PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "abc"));
+  Mock::VerifyAndClearExpectations(&observer);
+  // Now it's gone.
+  EXPECT_FALSE(combined.schema_map()->GetSchema(
+      PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "abc")));
+
+  EXPECT_CALL(observer, OnSchemaRegistryUpdated(false));
+  registry1->UnregisterComponent(
+      PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "def"));
+  Mock::VerifyAndClearExpectations(&observer);
+  // Still registered at registry2.
+  EXPECT_TRUE(combined.schema_map()->GetSchema(
+      PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "def")));
+
+  EXPECT_CALL(observer, OnSchemaRegistryUpdated(false));
+  registry2->UnregisterComponent(
+      PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "def"));
+  Mock::VerifyAndClearExpectations(&observer);
+  // Now it's gone.
+  EXPECT_FALSE(combined.schema_map()->GetSchema(
+      PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "def")));
+
+  EXPECT_CALL(observer, OnSchemaRegistryUpdated(true)).Times(2);
+  registry1->RegisterComponent(PolicyNamespace(POLICY_DOMAIN_CHROME, ""),
+                               schema);
+  registry2->RegisterComponent(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "hij"),
+                               schema);
+  Mock::VerifyAndClearExpectations(&observer);
+
+  // Untracking |registry1| doesn't trigger an update notification, because it
+  // doesn't contain any components.
+  EXPECT_CALL(observer, OnSchemaRegistryUpdated(_)).Times(0);
+  registry1.reset();
+  Mock::VerifyAndClearExpectations(&observer);
+
+  EXPECT_CALL(observer, OnSchemaRegistryUpdated(false));
+  registry2.reset();
+  Mock::VerifyAndClearExpectations(&observer);
+
+  combined.RemoveObserver(&observer);
+}
+
+TEST(SchemaRegistryTest, ForwardingSchemaRegistry) {
+  std::unique_ptr<SchemaRegistry> registry(new SchemaRegistry);
+  ForwardingSchemaRegistry forwarding(registry.get());
+  MockSchemaRegistryObserver observer;
+  forwarding.AddObserver(&observer);
+
+  EXPECT_FALSE(registry->IsReady());
+  EXPECT_FALSE(forwarding.IsReady());
+  // They always have the same SchemaMap.
+  EXPECT_TRUE(SchemaMapEquals(registry->schema_map(), forwarding.schema_map()));
+
+  EXPECT_CALL(observer, OnSchemaRegistryUpdated(true));
+  registry->RegisterComponent(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "abc"),
+                              Schema());
+  Mock::VerifyAndClearExpectations(&observer);
+  EXPECT_TRUE(SchemaMapEquals(registry->schema_map(), forwarding.schema_map()));
+
+  EXPECT_CALL(observer, OnSchemaRegistryUpdated(false));
+  registry->UnregisterComponent(
+      PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "abc"));
+  Mock::VerifyAndClearExpectations(&observer);
+  EXPECT_TRUE(SchemaMapEquals(registry->schema_map(), forwarding.schema_map()));
+
+  // No notifications expected for these calls.
+  EXPECT_FALSE(registry->IsReady());
+  EXPECT_FALSE(forwarding.IsReady());
+
+  registry->SetExtensionsDomainsReady();
+  EXPECT_FALSE(registry->IsReady());
+  EXPECT_FALSE(forwarding.IsReady());
+
+  EXPECT_CALL(observer, OnSchemaRegistryReady());
+  registry->SetDomainReady(POLICY_DOMAIN_CHROME);
+  EXPECT_TRUE(registry->IsReady());
+  EXPECT_TRUE(forwarding.IsReady());
+  Mock::VerifyAndClearExpectations(&observer);
+
+  EXPECT_TRUE(SchemaMapEquals(registry->schema_map(), forwarding.schema_map()));
+  Mock::VerifyAndClearExpectations(&observer);
+
+  forwarding.SetExtensionsDomainsReady();
+  forwarding.SetDomainReady(POLICY_DOMAIN_CHROME);
+  EXPECT_TRUE(forwarding.IsReady());
+
+  // Keep the same SchemaMap when the original registry is gone.
+  // No notifications are expected in this case either.
+  scoped_refptr<SchemaMap> schema_map = registry->schema_map();
+  registry.reset();
+  EXPECT_TRUE(SchemaMapEquals(schema_map, forwarding.schema_map()));
+  Mock::VerifyAndClearExpectations(&observer);
+
+  forwarding.RemoveObserver(&observer);
+}
+
+TEST(SchemaRegistryTest, ForwardingSchemaRegistryReadiness) {
+  std::unique_ptr<SchemaRegistry> registry(new SchemaRegistry);
+
+  ForwardingSchemaRegistry forwarding_1(registry.get());
+  EXPECT_FALSE(registry->IsReady());
+  EXPECT_FALSE(forwarding_1.IsReady());
+
+  // Once the wrapped registry gets ready, the forwarding schema registry
+  // becomes ready too.
+  registry->SetAllDomainsReady();
+  EXPECT_TRUE(registry->IsReady());
+  EXPECT_TRUE(forwarding_1.IsReady());
+
+  // The wrapped registry was ready at the time when the forwarding registry was
+  // constructed, so the forwarding registry is immediately ready too.
+  ForwardingSchemaRegistry forwarding_2(registry.get());
+  EXPECT_TRUE(forwarding_2.IsReady());
+
+  // Destruction of the wrapped registry doesn't change the readiness of the
+  // forwarding registry.
+  registry.reset();
+  EXPECT_TRUE(forwarding_1.IsReady());
+  EXPECT_TRUE(forwarding_2.IsReady());
+}
+
+}  // namespace policy
diff --git a/components/policy/core/common/schema_unittest.cc b/components/policy/core/common/schema_unittest.cc
new file mode 100644
index 0000000..4e861d1
--- /dev/null
+++ b/components/policy/core/common/schema_unittest.cc
@@ -0,0 +1,1286 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/policy/core/common/schema.h"
+
+#include <stddef.h>
+
+#include <memory>
+#include <utility>
+
+#include "base/macros.h"
+#include "base/strings/stringprintf.h"
+#include "base/values.h"
+#include "components/policy/core/common/schema_internal.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace policy {
+
+namespace {
+
+#define TestSchemaValidation(a, b, c, d) \
+    TestSchemaValidationHelper(          \
+        base::StringPrintf("%s:%i", __FILE__, __LINE__), a, b, c, d)
+
+const char kTestSchema[] = R"({
+  "type": "object",
+  "properties": {
+    "Boolean": { "type": "boolean" },
+    "Integer": { "type": "integer" },
+    "Null": { "type": "null" },
+    "Number": { "type": "number" },
+    "String": { "type": "string" },
+    "Array": {
+      "type": "array",
+      "items": { "type": "string" }
+    },
+    "ArrayOfObjects": {
+      "type": "array",
+      "items": {
+        "type": "object",
+        "properties": {
+          "one": { "type": "string" },
+          "two": { "type": "integer" }
+        }
+      }
+    },
+    "ArrayOfArray": {
+      "type": "array",
+      "items": {
+        "type": "array",
+        "items": { "type": "string" }
+      }
+    },
+    "Object": {
+      "type": "object",
+      "properties": {
+        "one": { "type": "boolean" },
+        "two": { "type": "integer" }
+      },
+      "additionalProperties": { "type": "string" }
+    },
+    "ObjectOfObject": {
+      "type": "object",
+      "properties": {
+        "Object": {
+          "type": "object",
+          "properties": {
+            "one": { "type": "string" },
+            "two": { "type": "integer" }
+          }
+        }
+      }
+    },
+    "IntegerWithEnums": {
+      "type": "integer",
+      "enum": [1, 2, 3]
+    },
+    "IntegerWithEnumsGaps": {
+      "type": "integer",
+      "enum": [10, 20, 30]
+    },
+    "StringWithEnums": {
+      "type": "string",
+      "enum": ["one", "two", "three"]
+    },
+    "IntegerWithRange": {
+      "type": "integer",
+      "minimum": 1,
+      "maximum": 3
+    },
+    "ObjectOfArray": {
+      "type": "object",
+      "properties": {
+        "List": {
+          "type": "array",
+          "items": { "type": "integer" }
+        }
+      }
+    },
+    "ArrayOfObjectOfArray": {
+      "type": "array",
+      "items": {
+        "type": "object",
+        "properties": {
+          "List": {
+            "type": "array",
+            "items": { "type": "string" }
+          }
+        }
+      }
+    },
+    "StringWithPattern": {
+      "type": "string",
+      "pattern": "^foo+$"
+    },
+    "ObjectWithPatternProperties": {
+      "type": "object",
+      "patternProperties": {
+        "^foo+$": { "type": "integer" },
+        "^bar+$": {
+          "type": "string",
+          "enum": ["one", "two"]
+        }
+      },
+      "properties": {
+        "bar": {
+          "type": "string",
+          "enum": ["one", "three"]
+        }
+      }
+    },
+    "ObjectWithRequiredProperties": {
+      "type": "object",
+      "properties": {
+        "Integer": {
+          "type": "integer",
+          "enum": [1, 2]
+        },
+        "String": { "type": "string" },
+        "Number": { "type": "number" }
+      },
+      "patternProperties": {
+        "^Integer": {
+          "type": "integer",
+          "enum": [1, 3]
+        }
+      },
+      "required": [ "Integer", "String" ]
+    }
+  }
+})";
+
+bool ParseFails(const std::string& content) {
+  std::string error;
+  Schema schema = Schema::Parse(content, &error);
+  if (schema.valid())
+    return false;
+  EXPECT_FALSE(error.empty());
+  return true;
+}
+
+void TestSchemaValidationHelper(const std::string& source,
+                                Schema schema,
+                                const base::Value& value,
+                                SchemaOnErrorStrategy strategy,
+                                bool expected_return_value) {
+  std::string error;
+  static const char kNoErrorReturned[] = "No error returned.";
+
+  // Test that Schema::Validate() works as expected.
+  error = kNoErrorReturned;
+  bool returned = schema.Validate(value, strategy, nullptr, &error);
+  ASSERT_EQ(expected_return_value, returned) << source << ": " << error;
+
+  // Test that Schema::Normalize() will return the same value as
+  // Schema::Validate().
+  error = kNoErrorReturned;
+  std::unique_ptr<base::Value> cloned_value(value.DeepCopy());
+  bool touched = false;
+  returned =
+      schema.Normalize(cloned_value.get(), strategy, nullptr, &error, &touched);
+  EXPECT_EQ(expected_return_value, returned) << source << ": " << error;
+
+  bool strictly_valid = schema.Validate(value, SCHEMA_STRICT, nullptr, &error);
+  EXPECT_EQ(touched, !strictly_valid && returned) << source;
+
+  // Test that Schema::Normalize() have actually dropped invalid and unknown
+  // properties.
+  if (expected_return_value) {
+    EXPECT_TRUE(schema.Validate(*cloned_value, SCHEMA_STRICT, nullptr, &error))
+        << source;
+    EXPECT_TRUE(schema.Normalize(cloned_value.get(), SCHEMA_STRICT, nullptr,
+                                 &error, nullptr))
+        << source;
+  }
+}
+
+void TestSchemaValidationWithPath(Schema schema,
+                                  const base::Value& value,
+                                  const std::string& expected_failure_path) {
+  std::string error_path = "NOT_SET";
+  std::string error;
+
+  bool returned = schema.Validate(value, SCHEMA_STRICT, &error_path, &error);
+  ASSERT_FALSE(returned) << error_path;
+  EXPECT_EQ(error_path, expected_failure_path);
+}
+
+std::string SchemaObjectWrapper(const std::string& subschema) {
+  return "{"
+         "  \"type\": \"object\","
+         "  \"properties\": {"
+         "    \"SomePropertyName\":" + subschema +
+         "  }"
+         "}";
+}
+
+}  // namespace
+
+TEST(SchemaTest, MinimalSchema) {
+  EXPECT_FALSE(ParseFails(R"({ "type": "object" })"));
+}
+
+TEST(SchemaTest, InvalidSchemas) {
+  EXPECT_TRUE(ParseFails(""));
+  EXPECT_TRUE(ParseFails("omg"));
+  EXPECT_TRUE(ParseFails("\"omg\""));
+  EXPECT_TRUE(ParseFails("123"));
+  EXPECT_TRUE(ParseFails("[]"));
+  EXPECT_TRUE(ParseFails("null"));
+  EXPECT_TRUE(ParseFails("{}"));
+
+  EXPECT_TRUE(ParseFails(R"({
+    "type": "object",
+    "additionalProperties": { "type":"object" }
+  })"));
+
+  EXPECT_TRUE(ParseFails(R"({
+    "type": "object",
+    "patternProperties": { "a+b*": { "type": "object" } }
+  })"));
+
+  EXPECT_TRUE(ParseFails(R"({
+    "type": "object",
+    "properties": { "Policy": { "type": "bogus" } }
+  })"));
+
+  EXPECT_TRUE(ParseFails(R"({
+    "type": "object",
+    "properties": { "Policy": { "type": ["string", "number"] } }
+  })"));
+
+  EXPECT_TRUE(ParseFails(R"({
+    "type": "object",
+    "properties": { "Policy": { "type": "any" } }
+  })"));
+
+  EXPECT_TRUE(ParseFails(R"({
+    "type": "object",
+    "properties": { "Policy": 123 }
+  })"));
+
+  EXPECT_FALSE(ParseFails(R"({
+    "type": "object",
+    "unknown attribute": "is ignored"
+  })"));
+}
+
+TEST(SchemaTest, Ownership) {
+  std::string error;
+  Schema schema = Schema::Parse(R"({
+    "type": "object",
+    "properties": {
+      "sub": {
+        "type": "object",
+        "properties": {
+          "subsub": { "type": "string" }
+        }
+      }
+    }
+  })",
+                                &error);
+  ASSERT_TRUE(schema.valid()) << error;
+  ASSERT_EQ(base::Value::Type::DICTIONARY, schema.type());
+
+  schema = schema.GetKnownProperty("sub");
+  ASSERT_TRUE(schema.valid());
+  ASSERT_EQ(base::Value::Type::DICTIONARY, schema.type());
+
+  {
+    Schema::Iterator it = schema.GetPropertiesIterator();
+    ASSERT_FALSE(it.IsAtEnd());
+    EXPECT_STREQ("subsub", it.key());
+
+    schema = it.schema();
+    it.Advance();
+    EXPECT_TRUE(it.IsAtEnd());
+  }
+
+  ASSERT_TRUE(schema.valid());
+  EXPECT_EQ(base::Value::Type::STRING, schema.type());
+
+  // This test shouldn't leak nor use invalid memory.
+}
+
+TEST(SchemaTest, ValidSchema) {
+  std::string error;
+  Schema schema = Schema::Parse(kTestSchema, &error);
+  ASSERT_TRUE(schema.valid()) << error;
+
+  ASSERT_EQ(base::Value::Type::DICTIONARY, schema.type());
+  EXPECT_FALSE(schema.GetProperty("invalid").valid());
+
+  Schema sub = schema.GetProperty("Boolean");
+  ASSERT_TRUE(sub.valid());
+  EXPECT_EQ(base::Value::Type::BOOLEAN, sub.type());
+
+  sub = schema.GetProperty("Integer");
+  ASSERT_TRUE(sub.valid());
+  EXPECT_EQ(base::Value::Type::INTEGER, sub.type());
+
+  sub = schema.GetProperty("Null");
+  ASSERT_TRUE(sub.valid());
+  EXPECT_EQ(base::Value::Type::NONE, sub.type());
+
+  sub = schema.GetProperty("Number");
+  ASSERT_TRUE(sub.valid());
+  EXPECT_EQ(base::Value::Type::DOUBLE, sub.type());
+
+  sub = schema.GetProperty("String");
+  ASSERT_TRUE(sub.valid());
+  EXPECT_EQ(base::Value::Type::STRING, sub.type());
+
+  sub = schema.GetProperty("Array");
+  ASSERT_TRUE(sub.valid());
+  ASSERT_EQ(base::Value::Type::LIST, sub.type());
+  sub = sub.GetItems();
+  ASSERT_TRUE(sub.valid());
+  EXPECT_EQ(base::Value::Type::STRING, sub.type());
+
+  sub = schema.GetProperty("ArrayOfObjects");
+  ASSERT_TRUE(sub.valid());
+  ASSERT_EQ(base::Value::Type::LIST, sub.type());
+  sub = sub.GetItems();
+  ASSERT_TRUE(sub.valid());
+  EXPECT_EQ(base::Value::Type::DICTIONARY, sub.type());
+  Schema subsub = sub.GetProperty("one");
+  ASSERT_TRUE(subsub.valid());
+  EXPECT_EQ(base::Value::Type::STRING, subsub.type());
+  subsub = sub.GetProperty("two");
+  ASSERT_TRUE(subsub.valid());
+  EXPECT_EQ(base::Value::Type::INTEGER, subsub.type());
+  subsub = sub.GetProperty("invalid");
+  EXPECT_FALSE(subsub.valid());
+
+  sub = schema.GetProperty("ArrayOfArray");
+  ASSERT_TRUE(sub.valid());
+  ASSERT_EQ(base::Value::Type::LIST, sub.type());
+  sub = sub.GetItems();
+  ASSERT_TRUE(sub.valid());
+  ASSERT_EQ(base::Value::Type::LIST, sub.type());
+  sub = sub.GetItems();
+  ASSERT_TRUE(sub.valid());
+  EXPECT_EQ(base::Value::Type::STRING, sub.type());
+
+  sub = schema.GetProperty("Object");
+  ASSERT_TRUE(sub.valid());
+  ASSERT_EQ(base::Value::Type::DICTIONARY, sub.type());
+  subsub = sub.GetProperty("one");
+  ASSERT_TRUE(subsub.valid());
+  EXPECT_EQ(base::Value::Type::BOOLEAN, subsub.type());
+  subsub = sub.GetProperty("two");
+  ASSERT_TRUE(subsub.valid());
+  EXPECT_EQ(base::Value::Type::INTEGER, subsub.type());
+  subsub = sub.GetProperty("undeclared");
+  ASSERT_TRUE(subsub.valid());
+  EXPECT_EQ(base::Value::Type::STRING, subsub.type());
+
+  sub = schema.GetProperty("IntegerWithEnums");
+  ASSERT_TRUE(sub.valid());
+  ASSERT_EQ(base::Value::Type::INTEGER, sub.type());
+
+  sub = schema.GetProperty("IntegerWithEnumsGaps");
+  ASSERT_TRUE(sub.valid());
+  ASSERT_EQ(base::Value::Type::INTEGER, sub.type());
+
+  sub = schema.GetProperty("StringWithEnums");
+  ASSERT_TRUE(sub.valid());
+  ASSERT_EQ(base::Value::Type::STRING, sub.type());
+
+  sub = schema.GetProperty("IntegerWithRange");
+  ASSERT_TRUE(sub.valid());
+  ASSERT_EQ(base::Value::Type::INTEGER, sub.type());
+
+  sub = schema.GetProperty("StringWithPattern");
+  ASSERT_TRUE(sub.valid());
+  ASSERT_EQ(base::Value::Type::STRING, sub.type());
+
+  sub = schema.GetProperty("ObjectWithPatternProperties");
+  ASSERT_TRUE(sub.valid());
+  ASSERT_EQ(base::Value::Type::DICTIONARY, sub.type());
+
+  sub = schema.GetProperty("ObjectWithRequiredProperties");
+  ASSERT_TRUE(sub.valid());
+  ASSERT_EQ(base::Value::Type::DICTIONARY, sub.type());
+
+  struct {
+    const char* expected_key;
+    base::Value::Type expected_type;
+  } kExpectedProperties[] = {
+    { "Array",                        base::Value::Type::LIST },
+    { "ArrayOfArray",                 base::Value::Type::LIST },
+    { "ArrayOfObjectOfArray",         base::Value::Type::LIST },
+    { "ArrayOfObjects",               base::Value::Type::LIST },
+    { "Boolean",                      base::Value::Type::BOOLEAN },
+    { "Integer",                      base::Value::Type::INTEGER },
+    { "IntegerWithEnums",             base::Value::Type::INTEGER },
+    { "IntegerWithEnumsGaps",         base::Value::Type::INTEGER },
+    { "IntegerWithRange",             base::Value::Type::INTEGER },
+    { "Null",                         base::Value::Type::NONE },
+    { "Number",                       base::Value::Type::DOUBLE },
+    { "Object",                       base::Value::Type::DICTIONARY },
+    { "ObjectOfArray",                base::Value::Type::DICTIONARY },
+    { "ObjectOfObject",               base::Value::Type::DICTIONARY },
+    { "ObjectWithPatternProperties",  base::Value::Type::DICTIONARY },
+    { "ObjectWithRequiredProperties", base::Value::Type::DICTIONARY },
+    { "String",                       base::Value::Type::STRING },
+    { "StringWithEnums",              base::Value::Type::STRING },
+    { "StringWithPattern",            base::Value::Type::STRING },
+  };
+  Schema::Iterator it = schema.GetPropertiesIterator();
+  for (size_t i = 0; i < arraysize(kExpectedProperties); ++i) {
+    ASSERT_FALSE(it.IsAtEnd());
+    EXPECT_STREQ(kExpectedProperties[i].expected_key, it.key());
+    ASSERT_TRUE(it.schema().valid());
+    EXPECT_EQ(kExpectedProperties[i].expected_type, it.schema().type());
+    it.Advance();
+  }
+  EXPECT_TRUE(it.IsAtEnd());
+}
+
+TEST(SchemaTest, Lookups) {
+  std::string error;
+
+  Schema schema = Schema::Parse(R"({ "type": "object" })", &error);
+  ASSERT_TRUE(schema.valid()) << error;
+  ASSERT_EQ(base::Value::Type::DICTIONARY, schema.type());
+
+  // This empty schema should never find named properties.
+  EXPECT_FALSE(schema.GetKnownProperty("").valid());
+  EXPECT_FALSE(schema.GetKnownProperty("xyz").valid());
+  EXPECT_TRUE(schema.GetPropertiesIterator().IsAtEnd());
+
+  schema = Schema::Parse(R"({
+    "type": "object",
+    "properties": {
+      "Boolean": { "type": "boolean" }
+    }
+  })",
+                         &error);
+  ASSERT_TRUE(schema.valid()) << error;
+  ASSERT_EQ(base::Value::Type::DICTIONARY, schema.type());
+
+  EXPECT_FALSE(schema.GetKnownProperty("").valid());
+  EXPECT_FALSE(schema.GetKnownProperty("xyz").valid());
+  EXPECT_TRUE(schema.GetKnownProperty("Boolean").valid());
+
+  schema = Schema::Parse(R"({
+    "type": "object",
+    "properties": {
+      "bb" : { "type": "null" },
+      "aa" : { "type": "boolean" },
+      "abab" : { "type": "string" },
+      "ab" : { "type": "number" },
+      "aba" : { "type": "integer" }
+    }
+  })",
+                         &error);
+  ASSERT_TRUE(schema.valid()) << error;
+  ASSERT_EQ(base::Value::Type::DICTIONARY, schema.type());
+
+  EXPECT_FALSE(schema.GetKnownProperty("").valid());
+  EXPECT_FALSE(schema.GetKnownProperty("xyz").valid());
+
+  struct {
+    const char* expected_key;
+    base::Value::Type expected_type;
+  } kExpectedKeys[] = {
+    { "aa",     base::Value::Type::BOOLEAN },
+    { "ab",     base::Value::Type::DOUBLE },
+    { "aba",    base::Value::Type::INTEGER },
+    { "abab",   base::Value::Type::STRING },
+    { "bb",     base::Value::Type::NONE },
+  };
+  for (size_t i = 0; i < arraysize(kExpectedKeys); ++i) {
+    Schema sub = schema.GetKnownProperty(kExpectedKeys[i].expected_key);
+    ASSERT_TRUE(sub.valid());
+    EXPECT_EQ(kExpectedKeys[i].expected_type, sub.type());
+  }
+
+  schema = Schema::Parse(R"(
+    {
+      "type": "object",
+      "properties": {
+        "String": { "type": "string" },
+        "Object": {
+          "type": "object",
+          "properties": {"Integer": {"type": "integer"}},
+          "required": [ "Integer" ]
+        },
+        "Number": { "type": "number" }
+      },
+      "required": [ "String", "Object"]
+    })",
+                         &error);
+  ASSERT_TRUE(schema.valid()) << error;
+  ASSERT_EQ(base::Value::Type::DICTIONARY, schema.type());
+
+  EXPECT_EQ(std::vector<std::string>({"String", "Object"}),
+            schema.GetRequiredProperties());
+
+  schema = schema.GetKnownProperty("Object");
+  ASSERT_TRUE(schema.valid()) << error;
+  ASSERT_EQ(base::Value::Type::DICTIONARY, schema.type());
+
+  EXPECT_EQ(std::vector<std::string>({"Integer"}),
+            schema.GetRequiredProperties());
+}
+
+TEST(SchemaTest, Wrap) {
+  const internal::SchemaNode kSchemas[] = {
+    { base::Value::Type::DICTIONARY,   0 },    //  0: root node
+    { base::Value::Type::BOOLEAN,      -1 },   //  1
+    { base::Value::Type::INTEGER,      -1 },   //  2
+    { base::Value::Type::DOUBLE,       -1 },   //  3
+    { base::Value::Type::STRING,       -1 },   //  4
+    { base::Value::Type::LIST,         4 },    //  5: list of strings.
+    { base::Value::Type::LIST,         5 },    //  6: list of lists of strings.
+    { base::Value::Type::INTEGER,      0 },    //  7: integer enumerations.
+    { base::Value::Type::INTEGER,      1 },    //  8: ranged integers.
+    { base::Value::Type::STRING,       2 },    //  9: string enumerations.
+    { base::Value::Type::STRING,       3 },    // 10: string with pattern.
+    { base::Value::Type::DICTIONARY,   1 },    // 11: dictionary with required
+                                             //     properties
+  };
+
+  const internal::PropertyNode kPropertyNodes[] = {
+    { "Boolean",       1},  //  0
+    { "DictRequired", 11},  //  1
+    { "Integer",       2},  //  2
+    { "List",          5},  //  3
+    { "Number",        3},  //  4
+    { "String",        4},  //  5
+    { "IntEnum",       7},  //  6
+    { "RangedInt",     8},  //  7
+    { "StrEnum",       9},  //  8
+    { "StrPat",       10},  //  9
+    { "bar+$",         4},  // 10
+    { "String",        4},  // 11
+    { "Number",        3},  // 12
+  };
+
+  const internal::PropertiesNode kProperties[] = {
+    // 0 to 10 (exclusive) are the known properties in kPropertyNodes, 9 is
+    // patternProperties and 6 is the additionalProperties node.
+    { 0, 10, 11, 0, 0, 6 },
+    // 11 to 13 (exclusive) are the known properties in kPropertyNodes. 0 to
+    // 1 (exclusive) are the required properties in kRequired. -1 indicates
+    // no additionalProperties.
+    { 11, 13, 13, 0, 1, -1 },
+  };
+
+  const internal::RestrictionNode kRestriction[] = {
+    {{0, 3}},  // 0: [1, 2, 3]
+    {{5, 1}},  // 1: minimum = 1, maximum = 5
+    {{0, 3}},  // 2: ["one", "two", "three"]
+    {{3, 3}},  // 3: pattern "foo+"
+  };
+
+  const char* kRequired[] = {"String"};
+
+  const int kIntEnums[] = {1, 2, 3};
+
+  const char* kStringEnums[] = {
+    "one",    // 0
+    "two",    // 1
+    "three",  // 2
+    "foo+",   // 3
+  };
+
+  const internal::SchemaData kData = {
+    kSchemas,
+    kPropertyNodes,
+    kProperties,
+    kRestriction,
+    kRequired,
+    kIntEnums,
+    kStringEnums,
+  };
+
+  Schema schema = Schema::Wrap(&kData);
+  ASSERT_TRUE(schema.valid());
+  EXPECT_EQ(base::Value::Type::DICTIONARY, schema.type());
+
+  struct {
+    const char* key;
+    base::Value::Type type;
+  } kExpectedProperties[] = {
+    { "Boolean", base::Value::Type::BOOLEAN },
+    { "DictRequired", base::Value::Type::DICTIONARY },
+    { "Integer", base::Value::Type::INTEGER },
+    { "List", base::Value::Type::LIST },
+    { "Number", base::Value::Type::DOUBLE },
+    { "String", base::Value::Type::STRING },
+    { "IntEnum", base::Value::Type::INTEGER },
+    { "RangedInt", base::Value::Type::INTEGER },
+    { "StrEnum", base::Value::Type::STRING },
+    { "StrPat", base::Value::Type::STRING },
+  };
+
+  Schema::Iterator it = schema.GetPropertiesIterator();
+  for (size_t i = 0; i < arraysize(kExpectedProperties); ++i) {
+    ASSERT_FALSE(it.IsAtEnd());
+    EXPECT_STREQ(kExpectedProperties[i].key, it.key());
+    Schema sub = it.schema();
+    ASSERT_TRUE(sub.valid());
+    EXPECT_EQ(kExpectedProperties[i].type, sub.type());
+
+    if (sub.type() == base::Value::Type::LIST) {
+      Schema items = sub.GetItems();
+      ASSERT_TRUE(items.valid());
+      EXPECT_EQ(base::Value::Type::STRING, items.type());
+    }
+
+    it.Advance();
+  }
+  EXPECT_TRUE(it.IsAtEnd());
+
+  Schema sub = schema.GetAdditionalProperties();
+  ASSERT_TRUE(sub.valid());
+  ASSERT_EQ(base::Value::Type::LIST, sub.type());
+  Schema subsub = sub.GetItems();
+  ASSERT_TRUE(subsub.valid());
+  ASSERT_EQ(base::Value::Type::LIST, subsub.type());
+  Schema subsubsub = subsub.GetItems();
+  ASSERT_TRUE(subsubsub.valid());
+  ASSERT_EQ(base::Value::Type::STRING, subsubsub.type());
+
+  SchemaList schema_list = schema.GetPatternProperties("barr");
+  ASSERT_EQ(1u, schema_list.size());
+  sub = schema_list[0];
+  ASSERT_TRUE(sub.valid());
+  EXPECT_EQ(base::Value::Type::STRING, sub.type());
+
+  EXPECT_TRUE(schema.GetPatternProperties("ba").empty());
+  EXPECT_TRUE(schema.GetPatternProperties("bar+$").empty());
+
+  Schema dict = schema.GetKnownProperty("DictRequired");
+  ASSERT_TRUE(dict.valid());
+  ASSERT_EQ(base::Value::Type::DICTIONARY, dict.type());
+
+  EXPECT_EQ(std::vector<std::string>({"String"}), dict.GetRequiredProperties());
+}
+
+TEST(SchemaTest, Validate) {
+  std::string error;
+  Schema schema = Schema::Parse(kTestSchema, &error);
+  ASSERT_TRUE(schema.valid()) << error;
+
+  base::DictionaryValue bundle;
+  TestSchemaValidation(schema, bundle, SCHEMA_STRICT, true);
+
+  // Wrong type, expected integer.
+  bundle.SetBoolean("Integer", true);
+  TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
+
+  // Wrong type, expected list of strings.
+  {
+    bundle.Clear();
+    base::ListValue list;
+    list.AppendInteger(1);
+    bundle.SetKey("Array", std::move(list));
+    TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
+  }
+
+  // Wrong type in a sub-object.
+  {
+    bundle.Clear();
+    base::DictionaryValue dict;
+    dict.SetString("one", "one");
+    bundle.SetKey("Object", std::move(dict));
+    TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
+  }
+
+  // Unknown name.
+  bundle.Clear();
+  bundle.SetBoolean("Unknown", true);
+  TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
+
+  // All of these will be valid.
+  bundle.Clear();
+  bundle.SetBoolean("Boolean", true);
+  bundle.SetInteger("Integer", 123);
+  bundle.Set("Null", std::make_unique<base::Value>());
+  bundle.SetDouble("Number", 3.14);
+  bundle.SetString("String", "omg");
+
+  {
+    base::ListValue list;
+    list.AppendString("a string");
+    list.AppendString("another string");
+    bundle.SetKey("Array", std::move(list));
+  }
+
+  {
+    base::DictionaryValue dict;
+    dict.SetString("one", "string");
+    dict.SetInteger("two", 2);
+    base::ListValue list;
+    list.GetList().push_back(dict.Clone());
+    list.GetList().push_back(std::move(dict));
+    bundle.SetKey("ArrayOfObjects", std::move(list));
+  }
+
+  {
+    base::ListValue list;
+    list.AppendString("a string");
+    list.AppendString("another string");
+    base::ListValue listlist;
+    listlist.GetList().push_back(list.Clone());
+    listlist.GetList().push_back(std::move(list));
+    bundle.SetKey("ArrayOfArray", std::move(listlist));
+  }
+
+  {
+    base::DictionaryValue dict;
+    dict.SetBoolean("one", true);
+    dict.SetInteger("two", 2);
+    dict.SetString("additionally", "a string");
+    dict.SetString("and also", "another string");
+    bundle.SetKey("Object", std::move(dict));
+  }
+
+  {
+    base::DictionaryValue dict;
+    dict.SetInteger("Integer", 1);
+    dict.SetString("String", "a string");
+    dict.SetDouble("Number", 3.14);
+    bundle.SetKey("ObjectWithRequiredProperties", std::move(dict));
+  }
+
+  bundle.SetInteger("IntegerWithEnums", 1);
+  bundle.SetInteger("IntegerWithEnumsGaps", 20);
+  bundle.SetString("StringWithEnums", "two");
+  bundle.SetInteger("IntegerWithRange", 3);
+
+  TestSchemaValidation(schema, bundle, SCHEMA_STRICT, true);
+
+  bundle.SetInteger("IntegerWithEnums", 0);
+  TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
+  bundle.SetInteger("IntegerWithEnums", 1);
+
+  bundle.SetInteger("IntegerWithEnumsGaps", 0);
+  TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
+  bundle.SetInteger("IntegerWithEnumsGaps", 9);
+  TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
+  bundle.SetInteger("IntegerWithEnumsGaps", 10);
+  TestSchemaValidation(schema, bundle, SCHEMA_STRICT, true);
+  bundle.SetInteger("IntegerWithEnumsGaps", 11);
+  TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
+  bundle.SetInteger("IntegerWithEnumsGaps", 19);
+  TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
+  bundle.SetInteger("IntegerWithEnumsGaps", 21);
+  TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
+  bundle.SetInteger("IntegerWithEnumsGaps", 29);
+  TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
+  bundle.SetInteger("IntegerWithEnumsGaps", 30);
+  TestSchemaValidation(schema, bundle, SCHEMA_STRICT, true);
+  bundle.SetInteger("IntegerWithEnumsGaps", 31);
+  TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
+  bundle.SetInteger("IntegerWithEnumsGaps", 100);
+  TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
+  bundle.SetInteger("IntegerWithEnumsGaps", 20);
+
+  bundle.SetString("StringWithEnums", "FOUR");
+  TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
+  bundle.SetString("StringWithEnums", "two");
+
+  bundle.SetInteger("IntegerWithRange", 4);
+  TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
+  bundle.SetInteger("IntegerWithRange", 3);
+
+  // Unknown top level property.
+  bundle.SetString("boom", "bang");
+  TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
+  TestSchemaValidation(schema, bundle, SCHEMA_ALLOW_UNKNOWN_TOPLEVEL, true);
+  TestSchemaValidation(schema, bundle, SCHEMA_ALLOW_UNKNOWN, true);
+  TestSchemaValidationWithPath(schema, bundle, "");
+  bundle.Remove("boom", nullptr);
+
+  // Invalid top level property.
+  bundle.SetInteger("Boolean", 12345);
+  TestSchemaValidation(schema, bundle, SCHEMA_STRICT, false);
+  TestSchemaValidation(schema, bundle, SCHEMA_ALLOW_INVALID_TOPLEVEL, true);
+  TestSchemaValidation(schema, bundle, SCHEMA_ALLOW_INVALID, true);
+  TestSchemaValidationWithPath(schema, bundle, "Boolean");
+  bundle.SetBoolean("Boolean", true);
+
+  // Tests on ObjectOfObject.
+  {
+    Schema subschema = schema.GetProperty("ObjectOfObject");
+    ASSERT_TRUE(subschema.valid());
+    base::DictionaryValue root;
+
+    // Unknown property.
+    root.SetBoolean("Object.three", false);
+    TestSchemaValidation(subschema, root, SCHEMA_STRICT, false);
+    TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN_TOPLEVEL, false);
+    TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN, true);
+    TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID_TOPLEVEL, true);
+    TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID, true);
+    TestSchemaValidationWithPath(subschema, root, "Object");
+    root.Remove("Object.three", nullptr);
+
+    // Invalid property.
+    root.SetInteger("Object.one", 12345);
+    TestSchemaValidation(subschema, root, SCHEMA_STRICT, false);
+    TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN_TOPLEVEL, false);
+    TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN, false);
+    TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID_TOPLEVEL, true);
+    TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID, true);
+    TestSchemaValidationWithPath(subschema, root, "Object.one");
+    root.Remove("Object.one", nullptr);
+  }
+
+  // Tests on ArrayOfObjects.
+  {
+    Schema subschema = schema.GetProperty("ArrayOfObjects");
+    ASSERT_TRUE(subschema.valid());
+    base::ListValue root;
+
+    // Unknown property.
+    std::unique_ptr<base::DictionaryValue> dict_value(
+        new base::DictionaryValue());
+    dict_value->SetBoolean("three", true);
+    root.Append(std::move(dict_value));
+    TestSchemaValidation(subschema, root, SCHEMA_STRICT, false);
+    TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN_TOPLEVEL, false);
+    TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN, true);
+    TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID_TOPLEVEL, true);
+    TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID, true);
+    TestSchemaValidationWithPath(subschema, root, "items[0]");
+    root.Remove(root.GetSize() - 1, nullptr);
+
+    // Invalid property.
+    dict_value.reset(new base::DictionaryValue());
+    dict_value->SetBoolean("two", true);
+    root.Append(std::move(dict_value));
+    TestSchemaValidation(subschema, root, SCHEMA_STRICT, false);
+    TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN_TOPLEVEL, false);
+    TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN, false);
+    TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID_TOPLEVEL, true);
+    TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID, true);
+    TestSchemaValidationWithPath(subschema, root, "items[0].two");
+    root.Remove(root.GetSize() - 1, nullptr);
+  }
+
+  // Tests on ObjectOfArray.
+  {
+    Schema subschema = schema.GetProperty("ObjectOfArray");
+    ASSERT_TRUE(subschema.valid());
+    base::DictionaryValue root;
+
+    base::ListValue* list_value =
+        root.SetList("List", std::make_unique<base::ListValue>());
+
+    // Test that there are not errors here.
+    list_value->AppendInteger(12345);
+    TestSchemaValidation(subschema, root, SCHEMA_STRICT, true);
+    TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN_TOPLEVEL, true);
+    TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN, true);
+    TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID_TOPLEVEL, true);
+    TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID, true);
+
+    // Invalid list item.
+    list_value->AppendString("blabla");
+    TestSchemaValidation(subschema, root, SCHEMA_STRICT, false);
+    TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN_TOPLEVEL, false);
+    TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN, false);
+    TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID_TOPLEVEL, true);
+    TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID, true);
+    TestSchemaValidationWithPath(subschema, root, "List.items[1]");
+  }
+
+  // Tests on ArrayOfObjectOfArray.
+  {
+    Schema subschema = schema.GetProperty("ArrayOfObjectOfArray");
+    ASSERT_TRUE(subschema.valid());
+    base::ListValue root;
+
+    auto dict_value = std::make_unique<base::DictionaryValue>();
+    base::ListValue* list_value =
+        dict_value->SetList("List", std::make_unique<base::ListValue>());
+    root.Append(std::move(dict_value));
+
+    // Test that there are not errors here.
+    list_value->AppendString("blabla");
+    TestSchemaValidation(subschema, root, SCHEMA_STRICT, true);
+    TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN_TOPLEVEL, true);
+    TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN, true);
+    TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID_TOPLEVEL, true);
+    TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID, true);
+
+    // Invalid list item.
+    list_value->AppendInteger(12345);
+    TestSchemaValidation(subschema, root, SCHEMA_STRICT, false);
+    TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN_TOPLEVEL, false);
+    TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN, false);
+    TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID_TOPLEVEL, true);
+    TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID, true);
+    TestSchemaValidationWithPath(subschema, root, "items[0].List.items[1]");
+  }
+
+  // Tests on StringWithPattern.
+  {
+    Schema subschema = schema.GetProperty("StringWithPattern");
+    ASSERT_TRUE(subschema.valid());
+
+    TestSchemaValidation(subschema, base::Value("foobar"), SCHEMA_STRICT,
+                         false);
+    TestSchemaValidation(subschema, base::Value("foo"), SCHEMA_STRICT, true);
+    TestSchemaValidation(subschema, base::Value("fo"), SCHEMA_STRICT, false);
+    TestSchemaValidation(subschema, base::Value("fooo"), SCHEMA_STRICT, true);
+    TestSchemaValidation(subschema, base::Value("^foo+$"), SCHEMA_STRICT,
+                         false);
+  }
+
+  // Tests on ObjectWithPatternProperties.
+  {
+    Schema subschema = schema.GetProperty("ObjectWithPatternProperties");
+    ASSERT_TRUE(subschema.valid());
+    base::DictionaryValue root;
+
+    ASSERT_EQ(1u, subschema.GetPatternProperties("fooo").size());
+    ASSERT_EQ(1u, subschema.GetPatternProperties("foo").size());
+    ASSERT_EQ(1u, subschema.GetPatternProperties("barr").size());
+    ASSERT_EQ(1u, subschema.GetPatternProperties("bar").size());
+    ASSERT_EQ(1u, subschema.GetMatchingProperties("fooo").size());
+    ASSERT_EQ(1u, subschema.GetMatchingProperties("foo").size());
+    ASSERT_EQ(1u, subschema.GetMatchingProperties("barr").size());
+    ASSERT_EQ(2u, subschema.GetMatchingProperties("bar").size());
+    ASSERT_TRUE(subschema.GetPatternProperties("foobar").empty());
+
+    root.SetInteger("fooo", 123);
+    TestSchemaValidation(subschema, root, SCHEMA_STRICT, true);
+    root.SetBoolean("fooo", false);
+    TestSchemaValidation(subschema, root, SCHEMA_STRICT, false);
+    root.Remove("fooo", nullptr);
+
+    root.SetInteger("foo", 123);
+    TestSchemaValidation(subschema, root, SCHEMA_STRICT, true);
+    root.SetBoolean("foo", false);
+    TestSchemaValidation(subschema, root, SCHEMA_STRICT, false);
+    root.Remove("foo", nullptr);
+
+    root.SetString("barr", "one");
+    TestSchemaValidation(subschema, root, SCHEMA_STRICT, true);
+    root.SetString("barr", "three");
+    TestSchemaValidation(subschema, root, SCHEMA_STRICT, false);
+    root.SetBoolean("barr", false);
+    TestSchemaValidation(subschema, root, SCHEMA_STRICT, false);
+    root.Remove("barr", nullptr);
+
+    root.SetString("bar", "one");
+    TestSchemaValidation(subschema, root, SCHEMA_STRICT, true);
+    root.SetString("bar", "two");
+    TestSchemaValidation(subschema, root, SCHEMA_STRICT, false);
+    root.SetString("bar", "three");
+    TestSchemaValidation(subschema, root, SCHEMA_STRICT, false);
+    root.Remove("bar", nullptr);
+
+    root.SetInteger("foobar", 123);
+    TestSchemaValidation(subschema, root, SCHEMA_STRICT, false);
+    TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN, true);
+    root.Remove("foobar", nullptr);
+  }
+
+  // Tests on ObjectWithRequiredProperties
+  {
+    Schema subschema = schema.GetProperty("ObjectWithRequiredProperties");
+    ASSERT_TRUE(subschema.valid());
+    base::DictionaryValue root;
+
+    // Required property missing.
+    root.SetInteger("Integer", 1);
+    root.SetDouble("Number", 3.14);
+    TestSchemaValidation(subschema, root, SCHEMA_STRICT, false);
+    TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN_TOPLEVEL, false);
+    TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN, false);
+    TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID_TOPLEVEL, false);
+    TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID, false);
+
+    // Invalid required property.
+    root.SetInteger("String", 123);
+    TestSchemaValidation(subschema, root, SCHEMA_STRICT, false);
+    TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN_TOPLEVEL, false);
+    TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN, false);
+    TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID_TOPLEVEL, false);
+    TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID, false);
+    root.SetString("String", "a string");
+
+    // Invalid subschema of required property with multiple subschemas.
+    //
+    // The "Integer" property has two subschemas, one in "properties" and one
+    // in "patternProperties". The first test generates a valid schema for the
+    // first subschema and the second test generates a valid schema for the
+    // second subschema. In both cases validation should fail because one of the
+    // required properties is invalid.
+    root.SetInteger("Integer", 2);
+    TestSchemaValidation(subschema, root, SCHEMA_STRICT, false);
+    TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN_TOPLEVEL, false);
+    TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN, false);
+    TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID_TOPLEVEL, false);
+    TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID, false);
+
+    root.SetInteger("Integer", 3);
+    TestSchemaValidation(subschema, root, SCHEMA_STRICT, false);
+    TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN_TOPLEVEL, false);
+    TestSchemaValidation(subschema, root, SCHEMA_ALLOW_UNKNOWN, false);
+    TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID_TOPLEVEL, false);
+    TestSchemaValidation(subschema, root, SCHEMA_ALLOW_INVALID, false);
+  }
+
+  // Test that integer to double promotion is allowed.
+  bundle.SetInteger("Number", 31415);
+  TestSchemaValidation(schema, bundle, SCHEMA_STRICT, true);
+}
+
+TEST(SchemaTest, InvalidReferences) {
+  // References to undeclared schemas fail.
+  EXPECT_TRUE(ParseFails(R"({
+    "type": "object",
+    "properties": {
+      "name": { "$ref": "undeclared" }
+    }
+  })"));
+
+  // Can't refer to self.
+  EXPECT_TRUE(ParseFails(R"({
+    "type": "object",
+    "properties": {
+      "name": {
+        "id": "self",
+        "$ref": "self"
+      }
+    }
+  })"));
+
+  // Duplicated IDs are invalid.
+  EXPECT_TRUE(ParseFails(R"({
+    "type": "object",
+    "properties": {
+      "name": {
+        "id": "x",
+        "type": "string"
+      },
+      "another": {
+        "id": "x",
+        "type": "string"
+      }
+    }
+  })"));
+
+  // Main object can't be a reference.
+  EXPECT_TRUE(ParseFails(R"({
+    "type": "object",
+    "id": "main",
+    "$ref": "main"
+  })"));
+
+  EXPECT_TRUE(ParseFails(R"({
+    "type": "object",
+    "$ref": "main"
+  })"));
+}
+
+TEST(SchemaTest, RecursiveReferences) {
+  // Verifies that references can go to a parent schema, to define a
+  // recursive type.
+  std::string error;
+  Schema schema = Schema::Parse(R"({
+    "type": "object",
+    "properties": {
+      "bookmarks": {
+        "type": "array",
+        "id": "ListOfBookmarks",
+        "items": {
+          "type": "object",
+          "properties": {
+            "name": { "type": "string" },
+            "url": { "type": "string" },
+            "children": { "$ref": "ListOfBookmarks" }
+          }
+        }
+      }
+    }
+  })",
+                                &error);
+  ASSERT_TRUE(schema.valid()) << error;
+  ASSERT_EQ(base::Value::Type::DICTIONARY, schema.type());
+
+  Schema parent = schema.GetKnownProperty("bookmarks");
+  ASSERT_TRUE(parent.valid());
+  ASSERT_EQ(base::Value::Type::LIST, parent.type());
+
+  // Check the recursive type a number of times.
+  for (int i = 0; i < 10; ++i) {
+    Schema items = parent.GetItems();
+    ASSERT_TRUE(items.valid());
+    ASSERT_EQ(base::Value::Type::DICTIONARY, items.type());
+
+    Schema prop = items.GetKnownProperty("name");
+    ASSERT_TRUE(prop.valid());
+    ASSERT_EQ(base::Value::Type::STRING, prop.type());
+
+    prop = items.GetKnownProperty("url");
+    ASSERT_TRUE(prop.valid());
+    ASSERT_EQ(base::Value::Type::STRING, prop.type());
+
+    prop = items.GetKnownProperty("children");
+    ASSERT_TRUE(prop.valid());
+    ASSERT_EQ(base::Value::Type::LIST, prop.type());
+
+    parent = prop;
+  }
+}
+
+TEST(SchemaTest, UnorderedReferences) {
+  // Verifies that references and IDs can come in any order.
+  std::string error;
+  Schema schema = Schema::Parse(R"({
+    "type": "object",
+    "properties": {
+      "a": { "$ref": "shared" },
+      "b": { "$ref": "shared" },
+      "c": { "$ref": "shared" },
+      "d": { "$ref": "shared" },
+      "e": {
+        "type": "boolean",
+        "id": "shared"
+      },
+      "f": { "$ref": "shared" },
+      "g": { "$ref": "shared" },
+      "h": { "$ref": "shared" },
+      "i": { "$ref": "shared" }
+    }
+  })",
+                                &error);
+  ASSERT_TRUE(schema.valid()) << error;
+  ASSERT_EQ(base::Value::Type::DICTIONARY, schema.type());
+
+  for (char c = 'a'; c <= 'i'; ++c) {
+    Schema sub = schema.GetKnownProperty(std::string(1, c));
+    ASSERT_TRUE(sub.valid()) << c;
+    ASSERT_EQ(base::Value::Type::BOOLEAN, sub.type()) << c;
+  }
+}
+
+TEST(SchemaTest, AdditionalPropertiesReference) {
+  // Verifies that "additionalProperties" can be a reference.
+  std::string error;
+  Schema schema = Schema::Parse(R"({
+    "type": "object",
+    "properties": {
+      "policy": {
+        "type": "object",
+        "properties": {
+          "foo": {
+            "type": "boolean",
+            "id": "FooId"
+          }
+        },
+        "additionalProperties": { "$ref": "FooId" }
+      }
+    }
+  })",
+                                &error);
+  ASSERT_TRUE(schema.valid()) << error;
+  ASSERT_EQ(base::Value::Type::DICTIONARY, schema.type());
+
+  Schema policy = schema.GetKnownProperty("policy");
+  ASSERT_TRUE(policy.valid());
+  ASSERT_EQ(base::Value::Type::DICTIONARY, policy.type());
+
+  Schema foo = policy.GetKnownProperty("foo");
+  ASSERT_TRUE(foo.valid());
+  EXPECT_EQ(base::Value::Type::BOOLEAN, foo.type());
+
+  Schema additional = policy.GetAdditionalProperties();
+  ASSERT_TRUE(additional.valid());
+  EXPECT_EQ(base::Value::Type::BOOLEAN, additional.type());
+
+  Schema x = policy.GetProperty("x");
+  ASSERT_TRUE(x.valid());
+  EXPECT_EQ(base::Value::Type::BOOLEAN, x.type());
+}
+
+TEST(SchemaTest, ItemsReference) {
+  // Verifies that "items" can be a reference.
+  std::string error;
+  Schema schema = Schema::Parse(R"({
+    "type": "object",
+    "properties": {
+      "foo": {
+        "type": "boolean",
+        "id": "FooId"
+      },
+      "list": {
+        "type": "array",
+        "items": { "$ref": "FooId" }
+      }
+    }
+  })",
+                                &error);
+  ASSERT_TRUE(schema.valid()) << error;
+  ASSERT_EQ(base::Value::Type::DICTIONARY, schema.type());
+
+  Schema foo = schema.GetKnownProperty("foo");
+  ASSERT_TRUE(foo.valid());
+  EXPECT_EQ(base::Value::Type::BOOLEAN, foo.type());
+
+  Schema list = schema.GetKnownProperty("list");
+  ASSERT_TRUE(list.valid());
+  ASSERT_EQ(base::Value::Type::LIST, list.type());
+
+  Schema items = list.GetItems();
+  ASSERT_TRUE(items.valid());
+  ASSERT_EQ(base::Value::Type::BOOLEAN, items.type());
+}
+
+TEST(SchemaTest, EnumerationRestriction) {
+  // Enum attribute is a list.
+  EXPECT_TRUE(ParseFails(SchemaObjectWrapper(R"({
+    "type": "string",
+    "enum": 12
+  })")));
+
+  // Empty enum attributes is not allowed.
+  EXPECT_TRUE(ParseFails(SchemaObjectWrapper(R"({
+    "type": "integer",
+    "enum": []
+  })")));
+
+  // Enum elements type should be same as stated.
+  EXPECT_TRUE(ParseFails(SchemaObjectWrapper(R"({
+    "type": "string",
+    "enum": [1, 2, 3]
+  })")));
+
+  EXPECT_FALSE(ParseFails(SchemaObjectWrapper(R"({
+    "type": "integer",
+    "enum": [1, 2, 3]
+  })")));
+
+  EXPECT_FALSE(ParseFails(SchemaObjectWrapper(R"({
+    "type": "string",
+    "enum": ["1", "2", "3"]
+  })")));
+}
+
+TEST(SchemaTest, RangedRestriction) {
+  EXPECT_TRUE(ParseFails(SchemaObjectWrapper(R"({
+    "type": "integer",
+    "minimum": 10,
+    "maximum": 5
+  })")));
+
+  EXPECT_FALSE(ParseFails(SchemaObjectWrapper(R"({
+    "type": "integer",
+    "minimum": 10,
+    "maximum": 20
+  })")));
+}
+
+}  // namespace policy
diff --git a/components/timers/alarm_timer_unittest.cc b/components/timers/alarm_timer_unittest.cc
new file mode 100644
index 0000000..868eb78
--- /dev/null
+++ b/components/timers/alarm_timer_unittest.cc
@@ -0,0 +1,372 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <sys/timerfd.h>
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/files/file_descriptor_watcher_posix.h"
+#include "base/location.h"
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "base/threading/platform_thread.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/time/time.h"
+#include "components/timers/alarm_timer_chromeos.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// Most of these tests have been lifted right out of timer_unittest.cc with only
+// cosmetic changes. We want the AlarmTimer to be a drop-in replacement for the
+// regular Timer so it should pass the same tests as the Timer class.
+namespace timers {
+namespace {
+const base::TimeDelta kTenMilliseconds = base::TimeDelta::FromMilliseconds(10);
+
+class AlarmTimerTester {
+ public:
+  AlarmTimerTester(bool* did_run,
+                   base::TimeDelta delay,
+                   base::OnceClosure quit_closure)
+      : did_run_(did_run),
+        quit_closure_(std::move(quit_closure)),
+        delay_(delay),
+        timer_(new timers::SimpleAlarmTimer()) {}
+  void Start() {
+    timer_->Start(
+        FROM_HERE, delay_,
+        base::BindRepeating(&AlarmTimerTester::Run, base::Unretained(this)));
+  }
+
+ private:
+  void Run() {
+    *did_run_ = true;
+    if (quit_closure_)
+      std::move(quit_closure_).Run();
+  }
+
+  bool* did_run_;
+  base::OnceClosure quit_closure_;
+  const base::TimeDelta delay_;
+  std::unique_ptr<timers::SimpleAlarmTimer> timer_;
+
+  DISALLOW_COPY_AND_ASSIGN(AlarmTimerTester);
+};
+
+class SelfDeletingAlarmTimerTester {
+ public:
+  SelfDeletingAlarmTimerTester(bool* did_run,
+                               base::TimeDelta delay,
+                               base::OnceClosure quit_closure)
+      : did_run_(did_run),
+        quit_closure_(std::move(quit_closure)),
+        delay_(delay),
+        timer_(new timers::SimpleAlarmTimer()) {}
+  void Start() {
+    timer_->Start(FROM_HERE, delay_,
+                  base::BindRepeating(&SelfDeletingAlarmTimerTester::Run,
+                                      base::Unretained(this)));
+  }
+
+ private:
+  void Run() {
+    *did_run_ = true;
+    timer_.reset();
+
+    if (quit_closure_)
+      std::move(quit_closure_).Run();
+  }
+
+  bool* did_run_;
+  base::OnceClosure quit_closure_;
+  const base::TimeDelta delay_;
+  std::unique_ptr<timers::SimpleAlarmTimer> timer_;
+
+  DISALLOW_COPY_AND_ASSIGN(SelfDeletingAlarmTimerTester);
+};
+
+}  // namespace
+
+//-----------------------------------------------------------------------------
+// Each test is run against each type of MessageLoop.  That way we are sure
+// that timers work properly in all configurations.
+
+TEST(AlarmTimerTest, SimpleAlarmTimer) {
+  base::MessageLoopForIO loop;
+  base::FileDescriptorWatcher file_descriptor_watcher(&loop);
+
+  base::RunLoop run_loop;
+  bool did_run = false;
+  AlarmTimerTester f(&did_run, kTenMilliseconds,
+                     run_loop.QuitWhenIdleClosure());
+  f.Start();
+
+  run_loop.Run();
+
+  EXPECT_TRUE(did_run);
+}
+
+TEST(AlarmTimerTest, SimpleAlarmTimer_Cancel) {
+  base::MessageLoopForIO loop;
+  base::FileDescriptorWatcher file_descriptor_watcher(&loop);
+
+  bool did_run_a = false;
+  AlarmTimerTester* a =
+      new AlarmTimerTester(&did_run_a, kTenMilliseconds, base::OnceClosure());
+
+  // This should run before the timer expires.
+  base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, a);
+
+  // Now start the timer.
+  a->Start();
+
+  base::RunLoop run_loop;
+  bool did_run_b = false;
+  AlarmTimerTester b(&did_run_b, kTenMilliseconds,
+                     run_loop.QuitWhenIdleClosure());
+  b.Start();
+
+  run_loop.Run();
+
+  EXPECT_FALSE(did_run_a);
+  EXPECT_TRUE(did_run_b);
+}
+
+// If underlying timer does not handle this properly, we will crash or fail
+// in full page heap environment.
+TEST(AlarmTimerTest, SelfDeletingAlarmTimer) {
+  base::MessageLoopForIO loop;
+  base::FileDescriptorWatcher file_descriptor_watcher(&loop);
+
+  base::RunLoop run_loop;
+  bool did_run = false;
+  SelfDeletingAlarmTimerTester f(&did_run, kTenMilliseconds,
+                                 run_loop.QuitWhenIdleClosure());
+  f.Start();
+
+  run_loop.Run();
+
+  EXPECT_TRUE(did_run);
+}
+
+TEST(AlarmTimerTest, AlarmTimerZeroDelay) {
+  base::MessageLoopForIO loop;
+  base::FileDescriptorWatcher file_descriptor_watcher(&loop);
+
+  base::RunLoop run_loop;
+  bool did_run = false;
+  AlarmTimerTester f(&did_run, base::TimeDelta(),
+                     run_loop.QuitWhenIdleClosure());
+  f.Start();
+
+  run_loop.Run();
+
+  EXPECT_TRUE(did_run);
+}
+
+TEST(AlarmTimerTest, AlarmTimerZeroDelay_Cancel) {
+  base::MessageLoopForIO loop;
+  base::FileDescriptorWatcher file_descriptor_watcher(&loop);
+
+  bool did_run_a = false;
+  AlarmTimerTester* a =
+      new AlarmTimerTester(&did_run_a, base::TimeDelta(), base::OnceClosure());
+
+  // This should run before the timer expires.
+  base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, a);
+
+  // Now start the timer.
+  a->Start();
+
+  base::RunLoop run_loop;
+  bool did_run_b = false;
+  AlarmTimerTester b(&did_run_b, base::TimeDelta(),
+                     run_loop.QuitWhenIdleClosure());
+  b.Start();
+
+  run_loop.Run();
+
+  EXPECT_FALSE(did_run_a);
+  EXPECT_TRUE(did_run_b);
+}
+
+TEST(AlarmTimerTest, MessageLoopShutdown) {
+  // This test is designed to verify that shutdown of the
+  // message loop does not cause crashes if there were pending
+  // timers not yet fired.  It may only trigger exceptions
+  // if debug heap checking is enabled.
+  bool did_run = false;
+  {
+    auto loop = std::make_unique<base::MessageLoopForIO>();
+    auto file_descriptor_watcher =
+        std::make_unique<base::FileDescriptorWatcher>(loop.get());
+    AlarmTimerTester a(&did_run, kTenMilliseconds, base::OnceClosure());
+    AlarmTimerTester b(&did_run, kTenMilliseconds, base::OnceClosure());
+    AlarmTimerTester c(&did_run, kTenMilliseconds, base::OnceClosure());
+    AlarmTimerTester d(&did_run, kTenMilliseconds, base::OnceClosure());
+
+    a.Start();
+    b.Start();
+
+    // Allow FileDescriptorWatcher to start watching the timers. Without this,
+    // tasks posted by FileDescriptorWatcher::WatchReadable() are leaked.
+    base::RunLoop().RunUntilIdle();
+
+    // MessageLoop and FileDescriptorWatcher destruct.
+    file_descriptor_watcher.reset();
+    loop.reset();
+  }  // SimpleAlarmTimers destruct. SHOULD NOT CRASH, of course.
+
+  EXPECT_FALSE(did_run);
+}
+
+TEST(AlarmTimerTest, NonRepeatIsRunning) {
+  {
+    base::MessageLoopForIO loop;
+    base::FileDescriptorWatcher file_descriptor_watcher(&loop);
+    timers::SimpleAlarmTimer timer;
+    EXPECT_FALSE(timer.IsRunning());
+    timer.Start(FROM_HERE, base::TimeDelta::FromDays(1), base::DoNothing());
+
+    // Allow FileDescriptorWatcher to start watching the timer. Without this, a
+    // task posted by FileDescriptorWatcher::WatchReadable() is leaked.
+    base::RunLoop().RunUntilIdle();
+
+    EXPECT_TRUE(timer.IsRunning());
+    timer.Stop();
+    EXPECT_FALSE(timer.IsRunning());
+    ASSERT_FALSE(timer.user_task().is_null());
+    timer.Reset();
+    base::RunLoop().RunUntilIdle();
+    EXPECT_TRUE(timer.IsRunning());
+  }
+}
+
+TEST(AlarmTimerTest, RetainNonRepeatIsRunning) {
+  base::MessageLoopForIO loop;
+  base::FileDescriptorWatcher file_descriptor_watcher(&loop);
+  timers::SimpleAlarmTimer timer;
+  EXPECT_FALSE(timer.IsRunning());
+  timer.Start(FROM_HERE, base::TimeDelta::FromDays(1), base::DoNothing());
+
+  // Allow FileDescriptorWatcher to start watching the timer. Without this, a
+  // task posted by FileDescriptorWatcher::WatchReadable() is leaked.
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_TRUE(timer.IsRunning());
+  timer.Reset();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(timer.IsRunning());
+  timer.Stop();
+  EXPECT_FALSE(timer.IsRunning());
+  timer.Reset();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(timer.IsRunning());
+}
+
+namespace {
+
+bool g_callback_happened1 = false;
+bool g_callback_happened2 = false;
+
+void ClearAllCallbackHappened() {
+  g_callback_happened1 = false;
+  g_callback_happened2 = false;
+}
+
+void SetCallbackHappened1(base::OnceClosure quit_closure) {
+  g_callback_happened1 = true;
+  if (quit_closure)
+    std::move(quit_closure).Run();
+}
+
+void SetCallbackHappened2(base::OnceClosure quit_closure) {
+  g_callback_happened2 = true;
+  if (quit_closure)
+    std::move(quit_closure).Run();
+}
+
+TEST(AlarmTimerTest, ContinuationStopStart) {
+  ClearAllCallbackHappened();
+  base::MessageLoopForIO loop;
+  base::FileDescriptorWatcher file_descriptor_watcher(&loop);
+  timers::SimpleAlarmTimer timer;
+  timer.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(10),
+              base::BindRepeating(&SetCallbackHappened1,
+                                  base::DoNothing().Repeatedly()));
+  timer.Stop();
+
+  base::RunLoop run_loop;
+  timer.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(40),
+              base::BindRepeating(&SetCallbackHappened2,
+                                  run_loop.QuitWhenIdleClosure()));
+  run_loop.Run();
+
+  EXPECT_FALSE(g_callback_happened1);
+  EXPECT_TRUE(g_callback_happened2);
+}
+
+TEST(AlarmTimerTest, ContinuationReset) {
+  ClearAllCallbackHappened();
+  base::MessageLoopForIO loop;
+  base::FileDescriptorWatcher file_descriptor_watcher(&loop);
+
+  base::RunLoop run_loop;
+  timers::SimpleAlarmTimer timer;
+  timer.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(10),
+              base::BindRepeating(&SetCallbackHappened1,
+                                  run_loop.QuitWhenIdleClosure()));
+  timer.Reset();
+  ASSERT_FALSE(timer.user_task().is_null());
+  run_loop.Run();
+  EXPECT_TRUE(g_callback_happened1);
+}
+
+// Verify that no crash occurs if a timer is deleted while its callback is
+// running.
+TEST(AlarmTimerTest, DeleteTimerWhileCallbackIsRunning) {
+  base::MessageLoopForIO loop;
+  base::FileDescriptorWatcher file_descriptor_watcher(&loop);
+  base::RunLoop run_loop;
+
+  // Will be deleted by the callback.
+  timers::SimpleAlarmTimer* timer = new timers::SimpleAlarmTimer;
+
+  timer->Start(
+      FROM_HERE, base::TimeDelta::FromMilliseconds(10),
+      base::BindRepeating(
+          [](timers::SimpleAlarmTimer* timer, base::RunLoop* run_loop) {
+            delete timer;
+            run_loop->Quit();
+          },
+          timer, &run_loop));
+  run_loop.Run();
+}
+
+// Verify that no crash occurs if a zero-delay timer is deleted while its
+// callback is running.
+TEST(AlarmTimerTest, DeleteTimerWhileCallbackIsRunningZeroDelay) {
+  base::MessageLoopForIO loop;
+  base::FileDescriptorWatcher file_descriptor_watcher(&loop);
+  base::RunLoop run_loop;
+
+  // Will be deleted by the callback.
+  timers::SimpleAlarmTimer* timer = new timers::SimpleAlarmTimer;
+
+  timer->Start(
+      FROM_HERE, base::TimeDelta(),
+      base::BindRepeating(
+          [](timers::SimpleAlarmTimer* timer, base::RunLoop* run_loop) {
+            delete timer;
+            run_loop->Quit();
+          },
+          timer, &run_loop));
+  run_loop.Run();
+}
+
+}  // namespace
+}  // namespace timers
diff --git a/dbus/bus_unittest.cc b/dbus/bus_unittest.cc
new file mode 100644
index 0000000..f00d1e9
--- /dev/null
+++ b/dbus/bus_unittest.cc
@@ -0,0 +1,420 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "dbus/bus.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/files/file_descriptor_watcher_posix.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/threading/thread.h"
+#include "dbus/exported_object.h"
+#include "dbus/object_path.h"
+#include "dbus/object_proxy.h"
+#include "dbus/scoped_dbus_error.h"
+#include "dbus/test_service.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace dbus {
+
+namespace {
+
+// Test helper for BusTest.ListenForServiceOwnerChange that wraps a
+// base::RunLoop. At Run() time, the caller pass in the expected number of
+// quit calls, and at QuitIfConditionIsSatisified() time, only quit the RunLoop
+// if the expected number of quit calls have been reached.
+class RunLoopWithExpectedCount {
+ public:
+  RunLoopWithExpectedCount() : expected_quit_calls_(0), actual_quit_calls_(0) {}
+  ~RunLoopWithExpectedCount() = default;
+
+  void Run(int expected_quit_calls) {
+    DCHECK_EQ(0, expected_quit_calls_);
+    DCHECK_EQ(0, actual_quit_calls_);
+    expected_quit_calls_ = expected_quit_calls;
+    run_loop_.reset(new base::RunLoop());
+    run_loop_->Run();
+  }
+
+  void QuitIfConditionIsSatisified() {
+    if (++actual_quit_calls_ != expected_quit_calls_)
+      return;
+    run_loop_->Quit();
+    expected_quit_calls_ = 0;
+    actual_quit_calls_ = 0;
+  }
+
+ private:
+  std::unique_ptr<base::RunLoop> run_loop_;
+  int expected_quit_calls_;
+  int actual_quit_calls_;
+
+  DISALLOW_COPY_AND_ASSIGN(RunLoopWithExpectedCount);
+};
+
+// Test helper for BusTest.ListenForServiceOwnerChange.
+void OnServiceOwnerChanged(RunLoopWithExpectedCount* run_loop_state,
+                           std::string* service_owner,
+                           int* num_of_owner_changes,
+                           const std::string& new_service_owner) {
+  *service_owner = new_service_owner;
+  ++(*num_of_owner_changes);
+  run_loop_state->QuitIfConditionIsSatisified();
+}
+
+}  // namespace
+
+TEST(BusTest, GetObjectProxy) {
+  Bus::Options options;
+  scoped_refptr<Bus> bus = new Bus(options);
+
+  ObjectProxy* object_proxy1 =
+      bus->GetObjectProxy("org.chromium.TestService",
+                          ObjectPath("/org/chromium/TestObject"));
+  ASSERT_TRUE(object_proxy1);
+
+  // This should return the same object.
+  ObjectProxy* object_proxy2 =
+      bus->GetObjectProxy("org.chromium.TestService",
+                          ObjectPath("/org/chromium/TestObject"));
+  ASSERT_TRUE(object_proxy2);
+  EXPECT_EQ(object_proxy1, object_proxy2);
+
+  // This should not.
+  ObjectProxy* object_proxy3 =
+      bus->GetObjectProxy(
+          "org.chromium.TestService",
+          ObjectPath("/org/chromium/DifferentTestObject"));
+  ASSERT_TRUE(object_proxy3);
+  EXPECT_NE(object_proxy1, object_proxy3);
+
+  bus->ShutdownAndBlock();
+}
+
+TEST(BusTest, GetObjectProxyIgnoreUnknownService) {
+  Bus::Options options;
+  scoped_refptr<Bus> bus = new Bus(options);
+
+  ObjectProxy* object_proxy1 =
+      bus->GetObjectProxyWithOptions(
+          "org.chromium.TestService",
+          ObjectPath("/org/chromium/TestObject"),
+          ObjectProxy::IGNORE_SERVICE_UNKNOWN_ERRORS);
+  ASSERT_TRUE(object_proxy1);
+
+  // This should return the same object.
+  ObjectProxy* object_proxy2 =
+      bus->GetObjectProxyWithOptions(
+          "org.chromium.TestService",
+          ObjectPath("/org/chromium/TestObject"),
+          ObjectProxy::IGNORE_SERVICE_UNKNOWN_ERRORS);
+  ASSERT_TRUE(object_proxy2);
+  EXPECT_EQ(object_proxy1, object_proxy2);
+
+  // This should not.
+  ObjectProxy* object_proxy3 =
+      bus->GetObjectProxyWithOptions(
+          "org.chromium.TestService",
+          ObjectPath("/org/chromium/DifferentTestObject"),
+          ObjectProxy::IGNORE_SERVICE_UNKNOWN_ERRORS);
+  ASSERT_TRUE(object_proxy3);
+  EXPECT_NE(object_proxy1, object_proxy3);
+
+  bus->ShutdownAndBlock();
+}
+
+TEST(BusTest, RemoveObjectProxy) {
+  // Setup the current thread's MessageLoop.
+  base::MessageLoop message_loop;
+
+  // Start the D-Bus thread.
+  base::Thread::Options thread_options;
+  thread_options.message_loop_type = base::MessageLoop::TYPE_IO;
+  base::Thread dbus_thread("D-Bus thread");
+  dbus_thread.StartWithOptions(thread_options);
+
+  // Create the bus.
+  Bus::Options options;
+  options.dbus_task_runner = dbus_thread.task_runner();
+  scoped_refptr<Bus> bus = new Bus(options);
+  ASSERT_FALSE(bus->shutdown_completed());
+
+  // Try to remove a non existant object proxy should return false.
+  ASSERT_FALSE(bus->RemoveObjectProxy("org.chromium.TestService",
+                                      ObjectPath("/org/chromium/TestObject"),
+                                      base::DoNothing()));
+
+  ObjectProxy* object_proxy1 =
+      bus->GetObjectProxy("org.chromium.TestService",
+                          ObjectPath("/org/chromium/TestObject"));
+  ASSERT_TRUE(object_proxy1);
+
+  // Increment the reference count to the object proxy to avoid destroying it
+  // while removing the object.
+  object_proxy1->AddRef();
+
+  // Remove the object from the bus. This will invalidate any other usage of
+  // object_proxy1 other than destroy it. We keep this object for a comparison
+  // at a later time.
+  ASSERT_TRUE(bus->RemoveObjectProxy("org.chromium.TestService",
+                                     ObjectPath("/org/chromium/TestObject"),
+                                     base::DoNothing()));
+
+  // This should return a different object because the first object was removed
+  // from the bus, but not deleted from memory.
+  ObjectProxy* object_proxy2 =
+      bus->GetObjectProxy("org.chromium.TestService",
+                          ObjectPath("/org/chromium/TestObject"));
+  ASSERT_TRUE(object_proxy2);
+
+  // Compare the new object with the first object. The first object still exists
+  // thanks to the increased reference.
+  EXPECT_NE(object_proxy1, object_proxy2);
+
+  // Release object_proxy1.
+  object_proxy1->Release();
+
+  // Shut down synchronously.
+  bus->ShutdownOnDBusThreadAndBlock();
+  EXPECT_TRUE(bus->shutdown_completed());
+  dbus_thread.Stop();
+}
+
+TEST(BusTest, GetExportedObject) {
+  Bus::Options options;
+  scoped_refptr<Bus> bus = new Bus(options);
+
+  ExportedObject* object_proxy1 =
+      bus->GetExportedObject(ObjectPath("/org/chromium/TestObject"));
+  ASSERT_TRUE(object_proxy1);
+
+  // This should return the same object.
+  ExportedObject* object_proxy2 =
+      bus->GetExportedObject(ObjectPath("/org/chromium/TestObject"));
+  ASSERT_TRUE(object_proxy2);
+  EXPECT_EQ(object_proxy1, object_proxy2);
+
+  // This should not.
+  ExportedObject* object_proxy3 =
+      bus->GetExportedObject(
+          ObjectPath("/org/chromium/DifferentTestObject"));
+  ASSERT_TRUE(object_proxy3);
+  EXPECT_NE(object_proxy1, object_proxy3);
+
+  bus->ShutdownAndBlock();
+}
+
+TEST(BusTest, UnregisterExportedObject) {
+  // Start the D-Bus thread.
+  base::Thread::Options thread_options;
+  thread_options.message_loop_type = base::MessageLoop::TYPE_IO;
+  base::Thread dbus_thread("D-Bus thread");
+  dbus_thread.StartWithOptions(thread_options);
+
+  // Create the bus.
+  Bus::Options options;
+  options.dbus_task_runner = dbus_thread.task_runner();
+  scoped_refptr<Bus> bus = new Bus(options);
+  ASSERT_FALSE(bus->shutdown_completed());
+
+  ExportedObject* object_proxy1 =
+      bus->GetExportedObject(ObjectPath("/org/chromium/TestObject"));
+  ASSERT_TRUE(object_proxy1);
+
+  // Increment the reference count to the object proxy to avoid destroying it
+  // calling UnregisterExportedObject. This ensures the dbus::ExportedObject is
+  // not freed from memory. See http://crbug.com/137846 for details.
+  object_proxy1->AddRef();
+
+  bus->UnregisterExportedObject(ObjectPath("/org/chromium/TestObject"));
+
+  // This should return a new object because the object_proxy1 is still in
+  // alloc'ed memory.
+  ExportedObject* object_proxy2 =
+      bus->GetExportedObject(ObjectPath("/org/chromium/TestObject"));
+  ASSERT_TRUE(object_proxy2);
+  EXPECT_NE(object_proxy1, object_proxy2);
+
+  // Release the incremented reference.
+  object_proxy1->Release();
+
+  // Shut down synchronously.
+  bus->ShutdownOnDBusThreadAndBlock();
+  EXPECT_TRUE(bus->shutdown_completed());
+  dbus_thread.Stop();
+}
+
+TEST(BusTest, ShutdownAndBlock) {
+  Bus::Options options;
+  scoped_refptr<Bus> bus = new Bus(options);
+  ASSERT_FALSE(bus->shutdown_completed());
+
+  // Shut down synchronously.
+  bus->ShutdownAndBlock();
+  EXPECT_TRUE(bus->shutdown_completed());
+}
+
+TEST(BusTest, ShutdownAndBlockWithDBusThread) {
+  // Start the D-Bus thread.
+  base::Thread::Options thread_options;
+  thread_options.message_loop_type = base::MessageLoop::TYPE_IO;
+  base::Thread dbus_thread("D-Bus thread");
+  dbus_thread.StartWithOptions(thread_options);
+
+  // Create the bus.
+  Bus::Options options;
+  options.dbus_task_runner = dbus_thread.task_runner();
+  scoped_refptr<Bus> bus = new Bus(options);
+  ASSERT_FALSE(bus->shutdown_completed());
+
+  // Shut down synchronously.
+  bus->ShutdownOnDBusThreadAndBlock();
+  EXPECT_TRUE(bus->shutdown_completed());
+  dbus_thread.Stop();
+}
+
+TEST(BusTest, DoubleAddAndRemoveMatch) {
+  Bus::Options options;
+  scoped_refptr<Bus> bus = new Bus(options);
+  ScopedDBusError error;
+
+  bus->Connect();
+
+  // Adds the same rule twice.
+  bus->AddMatch(
+      "type='signal',interface='org.chromium.TestService',path='/'",
+      error.get());
+  ASSERT_FALSE(error.is_set());
+
+  bus->AddMatch(
+      "type='signal',interface='org.chromium.TestService',path='/'",
+      error.get());
+  ASSERT_FALSE(error.is_set());
+
+  // Removes the same rule twice.
+  ASSERT_TRUE(bus->RemoveMatch(
+      "type='signal',interface='org.chromium.TestService',path='/'",
+      error.get()));
+  ASSERT_FALSE(error.is_set());
+
+  // The rule should be still in the bus since it was removed only once.
+  // A second removal shouldn't give an error.
+  ASSERT_TRUE(bus->RemoveMatch(
+      "type='signal',interface='org.chromium.TestService',path='/'",
+      error.get()));
+  ASSERT_FALSE(error.is_set());
+
+  // A third attemp to remove the same rule should fail.
+  ASSERT_FALSE(bus->RemoveMatch(
+      "type='signal',interface='org.chromium.TestService',path='/'",
+      error.get()));
+
+  bus->ShutdownAndBlock();
+}
+
+TEST(BusTest, ListenForServiceOwnerChange) {
+  base::MessageLoopForIO message_loop;
+
+  // This enables FileDescriptorWatcher, which is required by dbus::Watch.
+  base::FileDescriptorWatcher file_descriptor_watcher(&message_loop);
+
+  RunLoopWithExpectedCount run_loop_state;
+
+  // Create the bus.
+  Bus::Options bus_options;
+  scoped_refptr<Bus> bus = new Bus(bus_options);
+
+  // Add a listener.
+  std::string service_owner1;
+  int num_of_owner_changes1 = 0;
+  Bus::GetServiceOwnerCallback callback1 =
+      base::Bind(&OnServiceOwnerChanged,
+                 &run_loop_state,
+                 &service_owner1,
+                 &num_of_owner_changes1);
+  bus->ListenForServiceOwnerChange("org.chromium.TestService", callback1);
+  // This should be a no-op.
+  bus->ListenForServiceOwnerChange("org.chromium.TestService", callback1);
+  base::RunLoop().RunUntilIdle();
+
+  // Nothing has happened yet. Check initial state.
+  EXPECT_TRUE(service_owner1.empty());
+  EXPECT_EQ(0, num_of_owner_changes1);
+
+  // Make an ownership change.
+  ASSERT_TRUE(bus->RequestOwnershipAndBlock("org.chromium.TestService",
+                                            Bus::REQUIRE_PRIMARY));
+  run_loop_state.Run(1);
+
+  {
+    // Get the current service owner and check to make sure the listener got
+    // the right value.
+    std::string current_service_owner =
+        bus->GetServiceOwnerAndBlock("org.chromium.TestService",
+                                     Bus::REPORT_ERRORS);
+    ASSERT_FALSE(current_service_owner.empty());
+
+    // Make sure the listener heard about the new owner.
+    EXPECT_EQ(current_service_owner, service_owner1);
+
+    // Test the second ListenForServiceOwnerChange() above is indeed a no-op.
+    EXPECT_EQ(1, num_of_owner_changes1);
+  }
+
+  // Add a second listener.
+  std::string service_owner2;
+  int num_of_owner_changes2 = 0;
+  Bus::GetServiceOwnerCallback callback2 =
+      base::Bind(&OnServiceOwnerChanged,
+                 &run_loop_state,
+                 &service_owner2,
+                 &num_of_owner_changes2);
+  bus->ListenForServiceOwnerChange("org.chromium.TestService", callback2);
+  base::RunLoop().RunUntilIdle();
+
+  // Release the ownership and make sure the service owner listeners fire with
+  // the right values and the right number of times.
+  ASSERT_TRUE(bus->ReleaseOwnership("org.chromium.TestService"));
+  run_loop_state.Run(2);
+
+  EXPECT_TRUE(service_owner1.empty());
+  EXPECT_TRUE(service_owner2.empty());
+  EXPECT_EQ(2, num_of_owner_changes1);
+  EXPECT_EQ(1, num_of_owner_changes2);
+
+  // Unlisten so shutdown can proceed correctly.
+  bus->UnlistenForServiceOwnerChange("org.chromium.TestService", callback1);
+  bus->UnlistenForServiceOwnerChange("org.chromium.TestService", callback2);
+  base::RunLoop().RunUntilIdle();
+
+  // Shut down synchronously.
+  bus->ShutdownAndBlock();
+  EXPECT_TRUE(bus->shutdown_completed());
+}
+
+TEST(BusTest, GetConnectionName) {
+  Bus::Options options;
+  scoped_refptr<Bus> bus = new Bus(options);
+
+  // Connection name is empty since bus is not connected.
+  EXPECT_FALSE(bus->is_connected());
+  EXPECT_TRUE(bus->GetConnectionName().empty());
+
+  // Connect bus to D-Bus.
+  bus->Connect();
+
+  // Connection name is not empty after connection is established.
+  EXPECT_TRUE(bus->is_connected());
+  EXPECT_FALSE(bus->GetConnectionName().empty());
+
+  // Shut down synchronously.
+  bus->ShutdownAndBlock();
+  EXPECT_TRUE(bus->shutdown_completed());
+}
+
+}  // namespace dbus
diff --git a/dbus/dbus_statistics_unittest.cc b/dbus/dbus_statistics_unittest.cc
new file mode 100644
index 0000000..164d0b3
--- /dev/null
+++ b/dbus/dbus_statistics_unittest.cc
@@ -0,0 +1,178 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "dbus/dbus_statistics.h"
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace dbus {
+
+class DBusStatisticsTest : public testing::Test {
+ public:
+  DBusStatisticsTest() = default;
+
+  void SetUp() override { statistics::Initialize(); }
+
+  void TearDown() override { statistics::Shutdown(); }
+
+ protected:
+  void AddTestMethodCalls() {
+    statistics::AddSentMethodCall(
+        "service1", "service1.interface1", "method1");
+    statistics::AddReceivedSignal(
+        "service1", "service1.interface1", "method1");
+    statistics::AddBlockingSentMethodCall(
+        "service1", "service1.interface1", "method1");
+
+    statistics::AddSentMethodCall(
+        "service1", "service1.interface1", "method2");
+    statistics::AddSentMethodCall(
+        "service1", "service1.interface1", "method2");
+    statistics::AddReceivedSignal(
+        "service1", "service1.interface1", "method2");
+
+    statistics::AddSentMethodCall(
+        "service1", "service1.interface1", "method3");
+    statistics::AddSentMethodCall(
+        "service1", "service1.interface1", "method3");
+    statistics::AddSentMethodCall(
+        "service1", "service1.interface1", "method3");
+
+    statistics::AddSentMethodCall(
+        "service1", "service1.interface2", "method1");
+
+    statistics::AddSentMethodCall(
+        "service1", "service1.interface2", "method2");
+
+    statistics::AddSentMethodCall(
+        "service2", "service2.interface1", "method1");
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(DBusStatisticsTest);
+};
+
+TEST_F(DBusStatisticsTest, TestDBusStatsBasic) {
+  int sent = 0, received = 0, block = 0;
+
+  // Add a sent call
+  statistics::AddSentMethodCall("service1", "service1.interface1", "method1");
+  ASSERT_TRUE(statistics::testing::GetCalls(
+      "service1", "service1.interface1", "method1", &sent, &received, &block));
+  EXPECT_EQ(1, sent);
+  EXPECT_EQ(0, received);
+  EXPECT_EQ(0, block);
+
+  // Add a received call
+  statistics::AddReceivedSignal("service1", "service1.interface1", "method1");
+  ASSERT_TRUE(statistics::testing::GetCalls(
+      "service1", "service1.interface1", "method1", &sent, &received, &block));
+  EXPECT_EQ(1, sent);
+  EXPECT_EQ(1, received);
+  EXPECT_EQ(0, block);
+
+  // Add a block call
+  statistics::AddBlockingSentMethodCall(
+      "service1", "service1.interface1", "method1");
+  ASSERT_TRUE(statistics::testing::GetCalls(
+      "service1", "service1.interface1", "method1", &sent, &received, &block));
+  EXPECT_EQ(1, sent);
+  EXPECT_EQ(1, received);
+  EXPECT_EQ(1, block);
+}
+
+TEST_F(DBusStatisticsTest, TestDBusStatsMulti) {
+  int sent = 0, received = 0, block = 0;
+
+  // Add some more stats to exercise accessing multiple different stats.
+  AddTestMethodCalls();
+
+  // Make sure all entries can be found in the set and their counts were
+  // incremented correctly.
+  ASSERT_TRUE(statistics::testing::GetCalls(
+      "service1", "service1.interface1", "method1", &sent, &received, &block));
+  EXPECT_EQ(1, sent);
+  EXPECT_EQ(1, received);
+  ASSERT_TRUE(statistics::testing::GetCalls(
+      "service1", "service1.interface1", "method2", &sent, &received, &block));
+  EXPECT_EQ(2, sent);
+  EXPECT_EQ(1, received);
+  ASSERT_TRUE(statistics::testing::GetCalls(
+      "service1", "service1.interface1", "method3", &sent, &received, &block));
+  EXPECT_EQ(3, sent);
+  EXPECT_EQ(0, received);
+  ASSERT_TRUE(statistics::testing::GetCalls(
+      "service1", "service1.interface2", "method1", &sent, &received, &block));
+  EXPECT_EQ(1, sent);
+  EXPECT_EQ(0, received);
+  ASSERT_TRUE(statistics::testing::GetCalls(
+      "service1", "service1.interface2", "method2", &sent, &received, &block));
+  EXPECT_EQ(1, sent);
+  EXPECT_EQ(0, received);
+  ASSERT_TRUE(statistics::testing::GetCalls(
+      "service2", "service2.interface1", "method1", &sent, &received, &block));
+  EXPECT_EQ(1, sent);
+  EXPECT_EQ(0, received);
+
+  ASSERT_FALSE(statistics::testing::GetCalls(
+      "service1", "service1.interface3", "method2", &sent, &received, &block));
+}
+
+TEST_F(DBusStatisticsTest, TestGetAsString) {
+  std::string output_none = GetAsString(statistics::SHOW_SERVICE,
+                                        statistics::FORMAT_TOTALS);
+  EXPECT_EQ("No DBus calls.", output_none);
+
+  AddTestMethodCalls();
+
+  std::string output_service = GetAsString(statistics::SHOW_SERVICE,
+                                           statistics::FORMAT_TOTALS);
+  const std::string expected_output_service(
+      "service1: Sent (BLOCKING): 1 Sent: 8 Received: 2\n"
+      "service2: Sent: 1\n");
+  EXPECT_EQ(expected_output_service, output_service);
+
+  std::string output_interface = GetAsString(statistics::SHOW_INTERFACE,
+                                             statistics::FORMAT_TOTALS);
+  const std::string expected_output_interface(
+      "service1.interface1: Sent (BLOCKING): 1 Sent: 6 Received: 2\n"
+      "service1.interface2: Sent: 2\n"
+      "service2.interface1: Sent: 1\n");
+  EXPECT_EQ(expected_output_interface, output_interface);
+
+  std::string output_per_minute = GetAsString(statistics::SHOW_INTERFACE,
+                                              statistics::FORMAT_PER_MINUTE);
+  const std::string expected_output_per_minute(
+      "service1.interface1: Sent (BLOCKING): 1/min Sent: 6/min"
+      " Received: 2/min\n"
+      "service1.interface2: Sent: 2/min\n"
+      "service2.interface1: Sent: 1/min\n");
+  EXPECT_EQ(expected_output_per_minute, output_per_minute);
+
+  std::string output_all = GetAsString(statistics::SHOW_INTERFACE,
+                                       statistics::FORMAT_ALL);
+  const std::string expected_output_all(
+      "service1.interface1: Sent (BLOCKING): 1 (1/min) Sent: 6 (6/min)"
+      " Received: 2 (2/min)\n"
+      "service1.interface2: Sent: 2 (2/min)\n"
+      "service2.interface1: Sent: 1 (1/min)\n");
+  EXPECT_EQ(expected_output_all, output_all);
+
+
+  std::string output_method = GetAsString(statistics::SHOW_METHOD,
+                                          statistics::FORMAT_TOTALS);
+  const std::string expected_output_method(
+      "service1.interface1.method1: Sent (BLOCKING): 1 Sent: 1 Received: 1\n"
+      "service1.interface1.method2: Sent: 2 Received: 1\n"
+      "service1.interface1.method3: Sent: 3\n"
+      "service1.interface2.method1: Sent: 1\n"
+      "service1.interface2.method2: Sent: 1\n"
+      "service2.interface1.method1: Sent: 1\n");
+  EXPECT_EQ(expected_output_method, output_method);
+
+}
+
+}  // namespace dbus
diff --git a/dbus/end_to_end_async_unittest.cc b/dbus/end_to_end_async_unittest.cc
new file mode 100644
index 0000000..73e11c4
--- /dev/null
+++ b/dbus/end_to_end_async_unittest.cc
@@ -0,0 +1,645 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+
+#include <algorithm>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "base/stl_util.h"
+#include "base/test/test_timeouts.h"
+#include "base/threading/thread.h"
+#include "base/threading/thread_restrictions.h"
+#include "dbus/bus.h"
+#include "dbus/message.h"
+#include "dbus/object_path.h"
+#include "dbus/object_proxy.h"
+#include "dbus/test_service.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace dbus {
+
+namespace {
+
+// See comments in ObjectProxy::RunResponseCallback() for why the number was
+// chosen.
+const int kHugePayloadSize = 64 << 20;  // 64 MB
+
+}  // namespace
+
+// The end-to-end test exercises the asynchronous APIs in ObjectProxy and
+// ExportedObject.
+class EndToEndAsyncTest : public testing::Test {
+ public:
+  void SetUp() override {
+    // Make the main thread not to allow IO.
+    base::ThreadRestrictions::SetIOAllowed(false);
+
+    // Start the D-Bus thread.
+    dbus_thread_.reset(new base::Thread("D-Bus Thread"));
+    base::Thread::Options thread_options;
+    thread_options.message_loop_type = base::MessageLoop::TYPE_IO;
+    ASSERT_TRUE(dbus_thread_->StartWithOptions(thread_options));
+
+    // Start the test service, using the D-Bus thread.
+    TestService::Options options;
+    options.dbus_task_runner = dbus_thread_->task_runner();
+    test_service_.reset(new TestService(options));
+    ASSERT_TRUE(test_service_->StartService());
+    ASSERT_TRUE(test_service_->WaitUntilServiceIsStarted());
+    ASSERT_TRUE(test_service_->HasDBusThread());
+
+    // Create the client, using the D-Bus thread.
+    Bus::Options bus_options;
+    bus_options.bus_type = Bus::SESSION;
+    bus_options.connection_type = Bus::PRIVATE;
+    bus_options.dbus_task_runner = dbus_thread_->task_runner();
+    bus_ = new Bus(bus_options);
+    object_proxy_ = bus_->GetObjectProxy(
+        test_service_->service_name(),
+        ObjectPath("/org/chromium/TestObject"));
+    ASSERT_TRUE(bus_->HasDBusThread());
+
+    // Connect to the "Test" signal of "org.chromium.TestInterface" from
+    // the remote object.
+    object_proxy_->ConnectToSignal(
+        "org.chromium.TestInterface",
+        "Test",
+        base::Bind(&EndToEndAsyncTest::OnTestSignal,
+                   base::Unretained(this)),
+        base::Bind(&EndToEndAsyncTest::OnConnected,
+                   base::Unretained(this)));
+    // Wait until the object proxy is connected to the signal.
+    run_loop_.reset(new base::RunLoop());
+    run_loop_->Run();
+
+    // Connect to the "Test2" signal of "org.chromium.TestInterface" from
+    // the remote object. There was a bug where we were emitting error
+    // messages like "Requested to remove an unknown match rule: ..." at
+    // the shutdown of Bus when an object proxy is connected to more than
+    // one signal of the same interface. See crosbug.com/23382 for details.
+    object_proxy_->ConnectToSignal(
+        "org.chromium.TestInterface",
+        "Test2",
+        base::Bind(&EndToEndAsyncTest::OnTest2Signal,
+                   base::Unretained(this)),
+        base::Bind(&EndToEndAsyncTest::OnConnected,
+                   base::Unretained(this)));
+    // Wait until the object proxy is connected to the signal.
+    run_loop_.reset(new base::RunLoop());
+    run_loop_->Run();
+
+    // Create a second object proxy for the root object.
+    root_object_proxy_ = bus_->GetObjectProxy(test_service_->service_name(),
+                                              ObjectPath("/"));
+    ASSERT_TRUE(bus_->HasDBusThread());
+
+    // Connect to the "Test" signal of "org.chromium.TestInterface" from
+    // the root remote object too.
+    root_object_proxy_->ConnectToSignal(
+        "org.chromium.TestInterface",
+        "Test",
+        base::Bind(&EndToEndAsyncTest::OnRootTestSignal,
+                   base::Unretained(this)),
+        base::Bind(&EndToEndAsyncTest::OnConnected,
+                   base::Unretained(this)));
+    // Wait until the root object proxy is connected to the signal.
+    run_loop_.reset(new base::RunLoop());
+    run_loop_->Run();
+  }
+
+  void TearDown() override {
+    bus_->ShutdownOnDBusThreadAndBlock();
+
+    // Shut down the service.
+    test_service_->ShutdownAndBlock();
+
+    // Reset to the default.
+    base::ThreadRestrictions::SetIOAllowed(true);
+
+    // Stopping a thread is considered an IO operation, so do this after
+    // allowing IO.
+    test_service_->Stop();
+  }
+
+ protected:
+  // Replaces the bus with a broken one.
+  void SetUpBrokenBus() {
+    // Shut down the existing bus.
+    bus_->ShutdownOnDBusThreadAndBlock();
+
+    // Create new bus with invalid address.
+    const char kInvalidAddress[] = "";
+    Bus::Options bus_options;
+    bus_options.bus_type = Bus::CUSTOM_ADDRESS;
+    bus_options.address = kInvalidAddress;
+    bus_options.connection_type = Bus::PRIVATE;
+    bus_options.dbus_task_runner = dbus_thread_->task_runner();
+    bus_ = new Bus(bus_options);
+    ASSERT_TRUE(bus_->HasDBusThread());
+
+    // Create new object proxy.
+    object_proxy_ = bus_->GetObjectProxy(
+        test_service_->service_name(),
+        ObjectPath("/org/chromium/TestObject"));
+  }
+
+  // Calls the method asynchronously. OnResponse() will be called once the
+  // response is received.
+  void CallMethod(MethodCall* method_call,
+                  int timeout_ms) {
+    object_proxy_->CallMethod(method_call,
+                              timeout_ms,
+                              base::Bind(&EndToEndAsyncTest::OnResponse,
+                                         base::Unretained(this)));
+  }
+
+  // Calls the method asynchronously. OnResponse() will be called once the
+  // response is received without error, otherwise OnError() will be called.
+  void CallMethodWithErrorCallback(MethodCall* method_call,
+                                   int timeout_ms) {
+    object_proxy_->CallMethodWithErrorCallback(
+        method_call,
+        timeout_ms,
+        base::Bind(&EndToEndAsyncTest::OnResponse, base::Unretained(this)),
+        base::Bind(&EndToEndAsyncTest::OnError, base::Unretained(this)));
+  }
+
+  // Wait for the give number of responses.
+  void WaitForResponses(size_t num_responses) {
+    while (response_strings_.size() < num_responses) {
+      run_loop_.reset(new base::RunLoop);
+      run_loop_->Run();
+    }
+  }
+
+  // Called when the response is received.
+  void OnResponse(Response* response) {
+    // |response| will be deleted on exit of the function. Copy the
+    // payload to |response_strings_|.
+    if (response) {
+      MessageReader reader(response);
+      std::string response_string;
+      ASSERT_TRUE(reader.PopString(&response_string));
+      response_strings_.push_back(response_string);
+    } else {
+      response_strings_.push_back(std::string());
+    }
+    run_loop_->Quit();
+  };
+
+  // Wait for the given number of errors.
+  void WaitForErrors(size_t num_errors) {
+    while (error_names_.size() < num_errors) {
+      run_loop_.reset(new base::RunLoop);
+      run_loop_->Run();
+    }
+  }
+
+  // Called when an error is received.
+  void OnError(ErrorResponse* error) {
+    // |error| will be deleted on exit of the function. Copy the payload to
+    // |error_names_|.
+    if (error) {
+      ASSERT_NE("", error->GetErrorName());
+      error_names_.push_back(error->GetErrorName());
+    } else {
+      error_names_.push_back(std::string());
+    }
+    run_loop_->Quit();
+  }
+
+  // Called when the "Test" signal is received, in the main thread.
+  // Copy the string payload to |test_signal_string_|.
+  void OnTestSignal(Signal* signal) {
+    MessageReader reader(signal);
+    ASSERT_TRUE(reader.PopString(&test_signal_string_));
+    run_loop_->Quit();
+  }
+
+  // Called when the "Test" signal is received, in the main thread, by
+  // the root object proxy. Copy the string payload to
+  // |root_test_signal_string_|.
+  void OnRootTestSignal(Signal* signal) {
+    MessageReader reader(signal);
+    ASSERT_TRUE(reader.PopString(&root_test_signal_string_));
+    run_loop_->Quit();
+  }
+
+  // Called when the "Test2" signal is received, in the main thread.
+  void OnTest2Signal(Signal* signal) {
+    MessageReader reader(signal);
+    run_loop_->Quit();
+  }
+
+  // Called when connected to the signal.
+  void OnConnected(const std::string& interface_name,
+                   const std::string& signal_name,
+                   bool success) {
+    ASSERT_TRUE(success);
+    run_loop_->Quit();
+  }
+
+  // Wait for the hey signal to be received.
+  void WaitForTestSignal() {
+    // OnTestSignal() will quit the message loop.
+    run_loop_.reset(new base::RunLoop);
+    run_loop_->Run();
+  }
+
+  base::MessageLoop message_loop_;
+  std::unique_ptr<base::RunLoop> run_loop_;
+  std::vector<std::string> response_strings_;
+  std::vector<std::string> error_names_;
+  std::unique_ptr<base::Thread> dbus_thread_;
+  scoped_refptr<Bus> bus_;
+  ObjectProxy* object_proxy_;
+  ObjectProxy* root_object_proxy_;
+  std::unique_ptr<TestService> test_service_;
+  // Text message from "Test" signal.
+  std::string test_signal_string_;
+  // Text message from "Test" signal delivered to root.
+  std::string root_test_signal_string_;
+};
+
+TEST_F(EndToEndAsyncTest, Echo) {
+  const char* kHello = "hello";
+
+  // Create the method call.
+  MethodCall method_call("org.chromium.TestInterface", "Echo");
+  MessageWriter writer(&method_call);
+  writer.AppendString(kHello);
+
+  // Call the method.
+  const int timeout_ms = ObjectProxy::TIMEOUT_USE_DEFAULT;
+  CallMethod(&method_call, timeout_ms);
+
+  // Check the response.
+  WaitForResponses(1);
+  EXPECT_EQ(kHello, response_strings_[0]);
+}
+
+TEST_F(EndToEndAsyncTest, EchoWithErrorCallback) {
+  const char* kHello = "hello";
+
+  // Create the method call.
+  MethodCall method_call("org.chromium.TestInterface", "Echo");
+  MessageWriter writer(&method_call);
+  writer.AppendString(kHello);
+
+  // Call the method.
+  const int timeout_ms = ObjectProxy::TIMEOUT_USE_DEFAULT;
+  CallMethodWithErrorCallback(&method_call, timeout_ms);
+
+  // Check the response.
+  WaitForResponses(1);
+  EXPECT_EQ(kHello, response_strings_[0]);
+  EXPECT_TRUE(error_names_.empty());
+}
+
+// Call Echo method three times.
+TEST_F(EndToEndAsyncTest, EchoThreeTimes) {
+  const char* kMessages[] = { "foo", "bar", "baz" };
+
+  for (size_t i = 0; i < arraysize(kMessages); ++i) {
+    // Create the method call.
+    MethodCall method_call("org.chromium.TestInterface", "Echo");
+    MessageWriter writer(&method_call);
+    writer.AppendString(kMessages[i]);
+
+    // Call the method.
+    const int timeout_ms = ObjectProxy::TIMEOUT_USE_DEFAULT;
+    CallMethod(&method_call, timeout_ms);
+  }
+
+  // Check the responses.
+  WaitForResponses(3);
+  // Sort as the order of the returned messages is not deterministic.
+  std::sort(response_strings_.begin(), response_strings_.end());
+  EXPECT_EQ("bar", response_strings_[0]);
+  EXPECT_EQ("baz", response_strings_[1]);
+  EXPECT_EQ("foo", response_strings_[2]);
+}
+
+TEST_F(EndToEndAsyncTest, Echo_HugePayload) {
+  const std::string kHugePayload(kHugePayloadSize, 'o');
+
+  // Create the method call with a huge payload.
+  MethodCall method_call("org.chromium.TestInterface", "Echo");
+  MessageWriter writer(&method_call);
+  writer.AppendString(kHugePayload);
+
+  // Call the method.
+  const int timeout_ms = ObjectProxy::TIMEOUT_USE_DEFAULT;
+  CallMethod(&method_call, timeout_ms);
+
+  // This caused a DCHECK failure before. Ensure that the issue is fixed.
+  WaitForResponses(1);
+  EXPECT_EQ(kHugePayload, response_strings_[0]);
+}
+
+TEST_F(EndToEndAsyncTest, BrokenBus) {
+  const char* kHello = "hello";
+
+  // Set up a broken bus.
+  SetUpBrokenBus();
+
+  // Create the method call.
+  MethodCall method_call("org.chromium.TestInterface", "Echo");
+  MessageWriter writer(&method_call);
+  writer.AppendString(kHello);
+
+  // Call the method.
+  const int timeout_ms = ObjectProxy::TIMEOUT_USE_DEFAULT;
+  CallMethod(&method_call, timeout_ms);
+  WaitForResponses(1);
+
+  // Should fail because of the broken bus.
+  ASSERT_EQ("", response_strings_[0]);
+}
+
+TEST_F(EndToEndAsyncTest, BrokenBusWithErrorCallback) {
+  const char* kHello = "hello";
+
+  // Set up a broken bus.
+  SetUpBrokenBus();
+
+  // Create the method call.
+  MethodCall method_call("org.chromium.TestInterface", "Echo");
+  MessageWriter writer(&method_call);
+  writer.AppendString(kHello);
+
+  // Call the method.
+  const int timeout_ms = ObjectProxy::TIMEOUT_USE_DEFAULT;
+  CallMethodWithErrorCallback(&method_call, timeout_ms);
+  WaitForErrors(1);
+
+  // Should fail because of the broken bus.
+  ASSERT_TRUE(response_strings_.empty());
+  ASSERT_EQ("", error_names_[0]);
+}
+
+TEST_F(EndToEndAsyncTest, Timeout) {
+  const char* kHello = "hello";
+
+  // Create the method call.
+  MethodCall method_call("org.chromium.TestInterface", "SlowEcho");
+  MessageWriter writer(&method_call);
+  writer.AppendString(kHello);
+
+  // Call the method with timeout of 0ms.
+  const int timeout_ms = 0;
+  CallMethod(&method_call, timeout_ms);
+  WaitForResponses(1);
+
+  // Should fail because of timeout.
+  ASSERT_EQ("", response_strings_[0]);
+}
+
+TEST_F(EndToEndAsyncTest, TimeoutWithErrorCallback) {
+  const char* kHello = "hello";
+
+  // Create the method call.
+  MethodCall method_call("org.chromium.TestInterface", "SlowEcho");
+  MessageWriter writer(&method_call);
+  writer.AppendString(kHello);
+
+  // Call the method with timeout of 0ms.
+  const int timeout_ms = 0;
+  CallMethodWithErrorCallback(&method_call, timeout_ms);
+  WaitForErrors(1);
+
+  // Should fail because of timeout.
+  ASSERT_TRUE(response_strings_.empty());
+  ASSERT_EQ(DBUS_ERROR_NO_REPLY, error_names_[0]);
+}
+
+TEST_F(EndToEndAsyncTest, CancelPendingCalls) {
+  const char* kHello = "hello";
+
+  // Create the method call.
+  MethodCall method_call("org.chromium.TestInterface", "Echo");
+  MessageWriter writer(&method_call);
+  writer.AppendString(kHello);
+
+  // Call the method.
+  const int timeout_ms = ObjectProxy::TIMEOUT_USE_DEFAULT;
+  CallMethod(&method_call, timeout_ms);
+
+  // Remove the object proxy before receiving the result.
+  // This results in cancelling the pending method call.
+  bus_->RemoveObjectProxy(test_service_->service_name(),
+                          ObjectPath("/org/chromium/TestObject"),
+                          base::DoNothing());
+
+  // We shouldn't receive any responses. Wait for a while just to make sure.
+  run_loop_.reset(new base::RunLoop);
+  message_loop_.task_runner()->PostDelayedTask(
+      FROM_HERE, run_loop_->QuitClosure(), TestTimeouts::tiny_timeout());
+  run_loop_->Run();
+  EXPECT_TRUE(response_strings_.empty());
+}
+
+// Tests calling a method that sends its reply asynchronously.
+TEST_F(EndToEndAsyncTest, AsyncEcho) {
+  const char* kHello = "hello";
+
+  // Create the method call.
+  MethodCall method_call("org.chromium.TestInterface", "AsyncEcho");
+  MessageWriter writer(&method_call);
+  writer.AppendString(kHello);
+
+  // Call the method.
+  const int timeout_ms = ObjectProxy::TIMEOUT_USE_DEFAULT;
+  CallMethod(&method_call, timeout_ms);
+
+  // Check the response.
+  WaitForResponses(1);
+  EXPECT_EQ(kHello, response_strings_[0]);
+}
+
+TEST_F(EndToEndAsyncTest, NonexistentMethod) {
+  MethodCall method_call("org.chromium.TestInterface", "Nonexistent");
+
+  const int timeout_ms = ObjectProxy::TIMEOUT_USE_DEFAULT;
+  CallMethod(&method_call, timeout_ms);
+  WaitForResponses(1);
+
+  // Should fail because the method is nonexistent.
+  ASSERT_EQ("", response_strings_[0]);
+}
+
+TEST_F(EndToEndAsyncTest, NonexistentMethodWithErrorCallback) {
+  MethodCall method_call("org.chromium.TestInterface", "Nonexistent");
+
+  const int timeout_ms = ObjectProxy::TIMEOUT_USE_DEFAULT;
+  CallMethodWithErrorCallback(&method_call, timeout_ms);
+  WaitForErrors(1);
+
+  // Should fail because the method is nonexistent.
+  ASSERT_TRUE(response_strings_.empty());
+  ASSERT_EQ(DBUS_ERROR_UNKNOWN_METHOD, error_names_[0]);
+}
+
+TEST_F(EndToEndAsyncTest, BrokenMethod) {
+  MethodCall method_call("org.chromium.TestInterface", "BrokenMethod");
+
+  const int timeout_ms = ObjectProxy::TIMEOUT_USE_DEFAULT;
+  CallMethod(&method_call, timeout_ms);
+  WaitForResponses(1);
+
+  // Should fail because the method is broken.
+  ASSERT_EQ("", response_strings_[0]);
+}
+
+TEST_F(EndToEndAsyncTest, BrokenMethodWithErrorCallback) {
+  MethodCall method_call("org.chromium.TestInterface", "BrokenMethod");
+
+  const int timeout_ms = ObjectProxy::TIMEOUT_USE_DEFAULT;
+  CallMethodWithErrorCallback(&method_call, timeout_ms);
+  WaitForErrors(1);
+
+  // Should fail because the method is broken.
+  ASSERT_TRUE(response_strings_.empty());
+  ASSERT_EQ(DBUS_ERROR_FAILED, error_names_[0]);
+}
+
+TEST_F(EndToEndAsyncTest, InvalidServiceName) {
+  // Bus name cannot contain '/'.
+  const std::string invalid_service_name = ":1/2";
+
+  // Replace object proxy with new one.
+  object_proxy_ = bus_->GetObjectProxy(invalid_service_name,
+                                       ObjectPath("/org/chromium/TestObject"));
+
+  MethodCall method_call("org.chromium.TestInterface", "Echo");
+
+  const int timeout_ms = ObjectProxy::TIMEOUT_USE_DEFAULT;
+  CallMethodWithErrorCallback(&method_call, timeout_ms);
+  WaitForErrors(1);
+
+  // Should fail because of the invalid bus name.
+  ASSERT_TRUE(response_strings_.empty());
+  ASSERT_EQ("", error_names_[0]);
+}
+
+TEST_F(EndToEndAsyncTest, EmptyResponseCallback) {
+  const char* kHello = "hello";
+
+  // Create the method call.
+  MethodCall method_call("org.chromium.TestInterface", "Echo");
+  MessageWriter writer(&method_call);
+  writer.AppendString(kHello);
+
+  // Call the method with an empty callback.
+  const int timeout_ms = ObjectProxy::TIMEOUT_USE_DEFAULT;
+  object_proxy_->CallMethod(&method_call, timeout_ms, base::DoNothing());
+  // Post a delayed task to quit the message loop.
+  run_loop_.reset(new base::RunLoop);
+  message_loop_.task_runner()->PostDelayedTask(
+      FROM_HERE, run_loop_->QuitClosure(), TestTimeouts::tiny_timeout());
+  run_loop_->Run();
+  // We cannot tell if the empty callback is called, but at least we can
+  // check if the test does not crash.
+}
+
+TEST_F(EndToEndAsyncTest, TestSignal) {
+  const char kMessage[] = "hello, world";
+  // Send the test signal from the exported object.
+  test_service_->SendTestSignal(kMessage);
+  // Receive the signal with the object proxy. The signal is handled in
+  // EndToEndAsyncTest::OnTestSignal() in the main thread.
+  WaitForTestSignal();
+  ASSERT_EQ(kMessage, test_signal_string_);
+}
+
+TEST_F(EndToEndAsyncTest, TestSignalFromRoot) {
+  const char kMessage[] = "hello, world";
+  // Object proxies are tied to a particular object path, if a signal
+  // arrives from a different object path like "/" the first object proxy
+  // |object_proxy_| should not handle it, and should leave it for the root
+  // object proxy |root_object_proxy_|.
+  test_service_->SendTestSignalFromRoot(kMessage);
+  WaitForTestSignal();
+  // Verify the signal was not received by the specific proxy.
+  ASSERT_TRUE(test_signal_string_.empty());
+  // Verify the string WAS received by the root proxy.
+  ASSERT_EQ(kMessage, root_test_signal_string_);
+}
+
+TEST_F(EndToEndAsyncTest, TestHugeSignal) {
+  const std::string kHugeMessage(kHugePayloadSize, 'o');
+
+  // Send the huge signal from the exported object.
+  test_service_->SendTestSignal(kHugeMessage);
+  // This caused a DCHECK failure before. Ensure that the issue is fixed.
+  WaitForTestSignal();
+  ASSERT_EQ(kHugeMessage, test_signal_string_);
+}
+
+class SignalMultipleHandlerTest : public EndToEndAsyncTest {
+ public:
+  SignalMultipleHandlerTest() = default;
+
+  void SetUp() override {
+    // Set up base class.
+    EndToEndAsyncTest::SetUp();
+
+    // Connect the root object proxy's signal handler to a new handler
+    // so that we can verify that a second call to ConnectSignal() delivers
+    // to both our new handler and the old.
+    object_proxy_->ConnectToSignal(
+        "org.chromium.TestInterface",
+        "Test",
+        base::Bind(&SignalMultipleHandlerTest::OnAdditionalTestSignal,
+                   base::Unretained(this)),
+        base::Bind(&SignalMultipleHandlerTest::OnAdditionalConnected,
+                   base::Unretained(this)));
+    // Wait until the object proxy is connected to the signal.
+    run_loop_.reset(new base::RunLoop);
+    run_loop_->Run();
+  }
+
+ protected:
+  // Called when the "Test" signal is received, in the main thread.
+  // Copy the string payload to |additional_test_signal_string_|.
+  void OnAdditionalTestSignal(Signal* signal) {
+    MessageReader reader(signal);
+    ASSERT_TRUE(reader.PopString(&additional_test_signal_string_));
+    run_loop_->Quit();
+  }
+
+  // Called when connected to the signal.
+  void OnAdditionalConnected(const std::string& interface_name,
+                             const std::string& signal_name,
+                             bool success) {
+    ASSERT_TRUE(success);
+    run_loop_->Quit();
+  }
+
+  // Text message from "Test" signal delivered to additional handler.
+  std::string additional_test_signal_string_;
+};
+
+TEST_F(SignalMultipleHandlerTest, TestMultipleHandlers) {
+  const char kMessage[] = "hello, world";
+  // Send the test signal from the exported object.
+  test_service_->SendTestSignal(kMessage);
+  // Receive the signal with the object proxy.
+  WaitForTestSignal();
+  // Verify the string WAS received by the original handler.
+  ASSERT_EQ(kMessage, test_signal_string_);
+  // Verify the signal WAS ALSO received by the additional handler.
+  ASSERT_EQ(kMessage, additional_test_signal_string_);
+}
+
+}  // namespace dbus
diff --git a/dbus/end_to_end_sync_unittest.cc b/dbus/end_to_end_sync_unittest.cc
new file mode 100644
index 0000000..0bc146f
--- /dev/null
+++ b/dbus/end_to_end_sync_unittest.cc
@@ -0,0 +1,126 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <memory>
+
+#include "base/memory/ref_counted.h"
+#include "dbus/bus.h"
+#include "dbus/message.h"
+#include "dbus/object_path.h"
+#include "dbus/object_proxy.h"
+#include "dbus/test_service.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace dbus {
+
+// The end-to-end test exercises the synchronous APIs in ObjectProxy and
+// ExportedObject. The test will launch a thread for the service side
+// operations (i.e. ExportedObject side).
+class EndToEndSyncTest : public testing::Test {
+ public:
+  EndToEndSyncTest() = default;
+
+  void SetUp() override {
+    // Start the test service;
+    TestService::Options options;
+    test_service_.reset(new TestService(options));
+    ASSERT_TRUE(test_service_->StartService());
+    ASSERT_TRUE(test_service_->WaitUntilServiceIsStarted());
+    ASSERT_FALSE(test_service_->HasDBusThread());
+
+    // Create the client.
+    Bus::Options client_bus_options;
+    client_bus_options.bus_type = Bus::SESSION;
+    client_bus_options.connection_type = Bus::PRIVATE;
+    client_bus_ = new Bus(client_bus_options);
+    object_proxy_ = client_bus_->GetObjectProxy(
+        test_service_->service_name(),
+        ObjectPath("/org/chromium/TestObject"));
+    ASSERT_FALSE(client_bus_->HasDBusThread());
+  }
+
+  void TearDown() override {
+    test_service_->ShutdownAndBlock();
+    test_service_->Stop();
+    client_bus_->ShutdownAndBlock();
+  }
+
+ protected:
+  std::unique_ptr<TestService> test_service_;
+  scoped_refptr<Bus> client_bus_;
+  ObjectProxy* object_proxy_;
+};
+
+TEST_F(EndToEndSyncTest, Echo) {
+  const std::string kHello = "hello";
+
+  // Create the method call.
+  MethodCall method_call("org.chromium.TestInterface", "Echo");
+  MessageWriter writer(&method_call);
+  writer.AppendString(kHello);
+
+  // Call the method.
+  const int timeout_ms = ObjectProxy::TIMEOUT_USE_DEFAULT;
+  std::unique_ptr<Response> response(
+      object_proxy_->CallMethodAndBlock(&method_call, timeout_ms));
+  ASSERT_TRUE(response.get());
+
+  // Check the response. kHello should be echoed back.
+  MessageReader reader(response.get());
+  std::string returned_message;
+  ASSERT_TRUE(reader.PopString(&returned_message));
+  EXPECT_EQ(kHello, returned_message);
+}
+
+TEST_F(EndToEndSyncTest, Timeout) {
+  const std::string kHello = "hello";
+
+  // Create the method call.
+  MethodCall method_call("org.chromium.TestInterface", "DelayedEcho");
+  MessageWriter writer(&method_call);
+  writer.AppendString(kHello);
+
+  // Call the method with timeout of 0ms.
+  const int timeout_ms = 0;
+  std::unique_ptr<Response> response(
+      object_proxy_->CallMethodAndBlock(&method_call, timeout_ms));
+  // Should fail because of timeout.
+  ASSERT_FALSE(response.get());
+}
+
+TEST_F(EndToEndSyncTest, NonexistentMethod) {
+  MethodCall method_call("org.chromium.TestInterface", "Nonexistent");
+
+  const int timeout_ms = ObjectProxy::TIMEOUT_USE_DEFAULT;
+  std::unique_ptr<Response> response(
+      object_proxy_->CallMethodAndBlock(&method_call, timeout_ms));
+  ASSERT_FALSE(response.get());
+}
+
+TEST_F(EndToEndSyncTest, BrokenMethod) {
+  MethodCall method_call("org.chromium.TestInterface", "BrokenMethod");
+
+  const int timeout_ms = ObjectProxy::TIMEOUT_USE_DEFAULT;
+  std::unique_ptr<Response> response(
+      object_proxy_->CallMethodAndBlock(&method_call, timeout_ms));
+  ASSERT_FALSE(response.get());
+}
+
+TEST_F(EndToEndSyncTest, InvalidServiceName) {
+  // Bus name cannot contain '/'.
+  const std::string invalid_service_name = ":1/2";
+
+  // Replace object proxy with new one.
+  object_proxy_ = client_bus_->GetObjectProxy(
+      invalid_service_name, ObjectPath("/org/chromium/TestObject"));
+
+  MethodCall method_call("org.chromium.TestInterface", "Echo");
+
+  const int timeout_ms = ObjectProxy::TIMEOUT_USE_DEFAULT;
+  std::unique_ptr<Response> response(
+      object_proxy_->CallMethodAndBlock(&method_call, timeout_ms));
+  ASSERT_FALSE(response.get());
+}
+
+}  // namespace dbus
diff --git a/dbus/message_unittest.cc b/dbus/message_unittest.cc
new file mode 100644
index 0000000..c17ecf6
--- /dev/null
+++ b/dbus/message_unittest.cc
@@ -0,0 +1,727 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "dbus/message.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "dbus/object_path.h"
+#include "dbus/test_proto.pb.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace dbus {
+
+// Test that a byte can be properly written and read. We only have this
+// test for byte, as repeating this for other basic types is too redundant.
+TEST(MessageTest, AppendAndPopByte) {
+  std::unique_ptr<Response> message(Response::CreateEmpty());
+  MessageWriter writer(message.get());
+  writer.AppendByte(123);  // The input is 123.
+
+  MessageReader reader(message.get());
+  ASSERT_TRUE(reader.HasMoreData());  // Should have data to read.
+  ASSERT_EQ(Message::BYTE, reader.GetDataType());
+  ASSERT_EQ("y", reader.GetDataSignature());
+
+  bool bool_value = false;
+  // Should fail as the type is not bool here.
+  ASSERT_FALSE(reader.PopBool(&bool_value));
+
+  uint8_t byte_value = 0;
+  ASSERT_TRUE(reader.PopByte(&byte_value));
+  EXPECT_EQ(123, byte_value);          // Should match with the input.
+  ASSERT_FALSE(reader.HasMoreData());  // Should not have more data to read.
+
+  // Try to get another byte. Should fail.
+  ASSERT_FALSE(reader.PopByte(&byte_value));
+}
+
+// Check all basic types can be properly written and read.
+TEST(MessageTest, AppendAndPopBasicDataTypes) {
+  std::unique_ptr<Response> message(Response::CreateEmpty());
+  MessageWriter writer(message.get());
+
+  // Append 0, 1, 2, 3, 4, 5, 6, 7, 8, "string", "/object/path".
+  writer.AppendByte(0);
+  writer.AppendBool(true);
+  writer.AppendInt16(2);
+  writer.AppendUint16(3);
+  writer.AppendInt32(4);
+  writer.AppendUint32(5);
+  writer.AppendInt64(6);
+  writer.AppendUint64(7);
+  writer.AppendDouble(8.0);
+  writer.AppendString("string");
+  writer.AppendObjectPath(ObjectPath("/object/path"));
+
+  uint8_t byte_value = 0;
+  bool bool_value = false;
+  int16_t int16_value = 0;
+  uint16_t uint16_value = 0;
+  int32_t int32_value = 0;
+  uint32_t uint32_value = 0;
+  int64_t int64_value = 0;
+  uint64_t uint64_value = 0;
+  double double_value = 0;
+  std::string string_value;
+  ObjectPath object_path_value;
+
+  MessageReader reader(message.get());
+  ASSERT_TRUE(reader.HasMoreData());
+  ASSERT_EQ("y", reader.GetDataSignature());
+  ASSERT_TRUE(reader.PopByte(&byte_value));
+  ASSERT_EQ("b", reader.GetDataSignature());
+  ASSERT_TRUE(reader.PopBool(&bool_value));
+  ASSERT_EQ("n", reader.GetDataSignature());
+  ASSERT_TRUE(reader.PopInt16(&int16_value));
+  ASSERT_EQ("q", reader.GetDataSignature());
+  ASSERT_TRUE(reader.PopUint16(&uint16_value));
+  ASSERT_EQ("i", reader.GetDataSignature());
+  ASSERT_TRUE(reader.PopInt32(&int32_value));
+  ASSERT_EQ("u", reader.GetDataSignature());
+  ASSERT_TRUE(reader.PopUint32(&uint32_value));
+  ASSERT_EQ("x", reader.GetDataSignature());
+  ASSERT_TRUE(reader.PopInt64(&int64_value));
+  ASSERT_EQ("t", reader.GetDataSignature());
+  ASSERT_TRUE(reader.PopUint64(&uint64_value));
+  ASSERT_EQ("d", reader.GetDataSignature());
+  ASSERT_TRUE(reader.PopDouble(&double_value));
+  ASSERT_EQ("s", reader.GetDataSignature());
+  ASSERT_TRUE(reader.PopString(&string_value));
+  ASSERT_EQ("o", reader.GetDataSignature());
+  ASSERT_TRUE(reader.PopObjectPath(&object_path_value));
+  ASSERT_EQ("", reader.GetDataSignature());
+  ASSERT_FALSE(reader.HasMoreData());
+
+  // 0, 1, 2, 3, 4, 5, 6, 7, 8, "string", "/object/path" should be returned.
+  EXPECT_EQ(0, byte_value);
+  EXPECT_EQ(true, bool_value);
+  EXPECT_EQ(2, int16_value);
+  EXPECT_EQ(3U, uint16_value);
+  EXPECT_EQ(4, int32_value);
+  EXPECT_EQ(5U, uint32_value);
+  EXPECT_EQ(6, int64_value);
+  EXPECT_EQ(7U, uint64_value);
+  EXPECT_DOUBLE_EQ(8.0, double_value);
+  EXPECT_EQ("string", string_value);
+  EXPECT_EQ(ObjectPath("/object/path"), object_path_value);
+}
+
+// Check all basic types can be properly written and read.
+TEST(MessageTest, AppendAndPopFileDescriptor) {
+  if (!IsDBusTypeUnixFdSupported()) {
+    LOG(WARNING) << "FD passing is not supported";
+    return;
+  }
+
+  std::unique_ptr<Response> message(Response::CreateEmpty());
+  MessageWriter writer(message.get());
+
+  // Append stdout.
+  const int fd_in = 1;
+  writer.AppendFileDescriptor(fd_in);
+
+  base::ScopedFD fd_out;
+
+  MessageReader reader(message.get());
+  ASSERT_TRUE(reader.HasMoreData());
+  ASSERT_EQ(Message::UNIX_FD, reader.GetDataType());
+  ASSERT_EQ("h", reader.GetDataSignature());
+  ASSERT_TRUE(reader.PopFileDescriptor(&fd_out));
+  ASSERT_FALSE(reader.HasMoreData());
+
+  // Stdout should be returned but we cannot check the descriptor
+  // value because stdout will be dup'd.  Instead check st_rdev
+  // which should be identical.
+  struct stat sb_stdout;
+  int status_stdout = HANDLE_EINTR(fstat(fd_in, &sb_stdout));
+  ASSERT_GE(status_stdout, 0);
+  struct stat sb_fd;
+  int status_fd = HANDLE_EINTR(fstat(fd_out.get(), &sb_fd));
+  ASSERT_GE(status_fd, 0);
+  EXPECT_EQ(sb_stdout.st_rdev, sb_fd.st_rdev);
+}
+
+// Check all variant types can be properly written and read.
+TEST(MessageTest, AppendAndPopVariantDataTypes) {
+  std::unique_ptr<Response> message(Response::CreateEmpty());
+  MessageWriter writer(message.get());
+
+  // Append 0, 1, 2, 3, 4, 5, 6, 7, 8, "string", "/object/path".
+  writer.AppendVariantOfByte(0);
+  writer.AppendVariantOfBool(true);
+  writer.AppendVariantOfInt16(2);
+  writer.AppendVariantOfUint16(3);
+  writer.AppendVariantOfInt32(4);
+  writer.AppendVariantOfUint32(5);
+  writer.AppendVariantOfInt64(6);
+  writer.AppendVariantOfUint64(7);
+  writer.AppendVariantOfDouble(8.0);
+  writer.AppendVariantOfString("string");
+  writer.AppendVariantOfObjectPath(ObjectPath("/object/path"));
+
+  uint8_t byte_value = 0;
+  bool bool_value = false;
+  int16_t int16_value = 0;
+  uint16_t uint16_value = 0;
+  int32_t int32_value = 0;
+  uint32_t uint32_value = 0;
+  int64_t int64_value = 0;
+  uint64_t uint64_value = 0;
+  double double_value = 0;
+  std::string string_value;
+  ObjectPath object_path_value;
+
+  MessageReader reader(message.get());
+  ASSERT_TRUE(reader.HasMoreData());
+  ASSERT_EQ("v", reader.GetDataSignature());
+  ASSERT_TRUE(reader.PopVariantOfByte(&byte_value));
+  ASSERT_EQ("v", reader.GetDataSignature());
+  ASSERT_TRUE(reader.PopVariantOfBool(&bool_value));
+  ASSERT_EQ("v", reader.GetDataSignature());
+  ASSERT_TRUE(reader.PopVariantOfInt16(&int16_value));
+  ASSERT_EQ("v", reader.GetDataSignature());
+  ASSERT_TRUE(reader.PopVariantOfUint16(&uint16_value));
+  ASSERT_EQ("v", reader.GetDataSignature());
+  ASSERT_TRUE(reader.PopVariantOfInt32(&int32_value));
+  ASSERT_EQ("v", reader.GetDataSignature());
+  ASSERT_TRUE(reader.PopVariantOfUint32(&uint32_value));
+  ASSERT_EQ("v", reader.GetDataSignature());
+  ASSERT_TRUE(reader.PopVariantOfInt64(&int64_value));
+  ASSERT_EQ("v", reader.GetDataSignature());
+  ASSERT_TRUE(reader.PopVariantOfUint64(&uint64_value));
+  ASSERT_EQ("v", reader.GetDataSignature());
+  ASSERT_TRUE(reader.PopVariantOfDouble(&double_value));
+  ASSERT_EQ("v", reader.GetDataSignature());
+  ASSERT_TRUE(reader.PopVariantOfString(&string_value));
+  ASSERT_EQ("v", reader.GetDataSignature());
+  ASSERT_TRUE(reader.PopVariantOfObjectPath(&object_path_value));
+  ASSERT_EQ("", reader.GetDataSignature());
+  ASSERT_FALSE(reader.HasMoreData());
+
+  // 0, 1, 2, 3, 4, 5, 6, 7, 8, "string", "/object/path" should be returned.
+  EXPECT_EQ(0, byte_value);
+  EXPECT_EQ(true, bool_value);
+  EXPECT_EQ(2, int16_value);
+  EXPECT_EQ(3U, uint16_value);
+  EXPECT_EQ(4, int32_value);
+  EXPECT_EQ(5U, uint32_value);
+  EXPECT_EQ(6, int64_value);
+  EXPECT_EQ(7U, uint64_value);
+  EXPECT_DOUBLE_EQ(8.0, double_value);
+  EXPECT_EQ("string", string_value);
+  EXPECT_EQ(ObjectPath("/object/path"), object_path_value);
+}
+
+TEST(MessageTest, ArrayOfBytes) {
+  std::unique_ptr<Response> message(Response::CreateEmpty());
+  MessageWriter writer(message.get());
+  std::vector<uint8_t> bytes;
+  bytes.push_back(1);
+  bytes.push_back(2);
+  bytes.push_back(3);
+  writer.AppendArrayOfBytes(bytes.data(), bytes.size());
+
+  MessageReader reader(message.get());
+  const uint8_t* output_bytes = nullptr;
+  size_t length = 0;
+  ASSERT_EQ("ay", reader.GetDataSignature());
+  ASSERT_TRUE(reader.PopArrayOfBytes(&output_bytes, &length));
+  ASSERT_FALSE(reader.HasMoreData());
+  ASSERT_EQ(3U, length);
+  EXPECT_EQ(1, output_bytes[0]);
+  EXPECT_EQ(2, output_bytes[1]);
+  EXPECT_EQ(3, output_bytes[2]);
+}
+
+TEST(MessageTest, ArrayOfInt32s) {
+  std::unique_ptr<Response> message(Response::CreateEmpty());
+  MessageWriter writer(message.get());
+  std::vector<int32_t> int32s;
+  int32s.push_back(1);
+  int32s.push_back(2);
+  int32s.push_back(3);
+  writer.AppendArrayOfInt32s(int32s.data(), int32s.size());
+
+  MessageReader reader(message.get());
+  const int32_t* output_int32s = nullptr;
+  size_t length = 0;
+  ASSERT_EQ("ai", reader.GetDataSignature());
+  ASSERT_TRUE(reader.PopArrayOfInt32s(&output_int32s, &length));
+  ASSERT_FALSE(reader.HasMoreData());
+  ASSERT_EQ(3U, length);
+  EXPECT_EQ(1, output_int32s[0]);
+  EXPECT_EQ(2, output_int32s[1]);
+  EXPECT_EQ(3, output_int32s[2]);
+}
+
+TEST(MessageTest, ArrayOfUint32s) {
+  std::unique_ptr<Response> message(Response::CreateEmpty());
+  MessageWriter writer(message.get());
+  std::vector<uint32_t> uint32s;
+  uint32s.push_back(1);
+  uint32s.push_back(2);
+  uint32s.push_back(3);
+  writer.AppendArrayOfUint32s(uint32s.data(), uint32s.size());
+
+  MessageReader reader(message.get());
+  const uint32_t* output_uint32s = nullptr;
+  size_t length = 0;
+  ASSERT_EQ("au", reader.GetDataSignature());
+  ASSERT_TRUE(reader.PopArrayOfUint32s(&output_uint32s, &length));
+  ASSERT_FALSE(reader.HasMoreData());
+  ASSERT_EQ(3U, length);
+  EXPECT_EQ(1U, output_uint32s[0]);
+  EXPECT_EQ(2U, output_uint32s[1]);
+  EXPECT_EQ(3U, output_uint32s[2]);
+}
+
+TEST(MessageTest, ArrayOfDoubles) {
+  std::unique_ptr<Response> message(Response::CreateEmpty());
+  MessageWriter writer(message.get());
+  std::vector<double> doubles;
+  doubles.push_back(0.2);
+  doubles.push_back(0.5);
+  doubles.push_back(1);
+  writer.AppendArrayOfDoubles(doubles.data(), doubles.size());
+
+  MessageReader reader(message.get());
+  const double* output_doubles = nullptr;
+  size_t length = 0;
+  ASSERT_EQ("ad", reader.GetDataSignature());
+  ASSERT_TRUE(reader.PopArrayOfDoubles(&output_doubles, &length));
+  ASSERT_FALSE(reader.HasMoreData());
+  ASSERT_EQ(3U, length);
+  EXPECT_EQ(0.2, output_doubles[0]);
+  EXPECT_EQ(0.5, output_doubles[1]);
+  EXPECT_EQ(1, output_doubles[2]);
+}
+
+TEST(MessageTest, ArrayOfBytes_Empty) {
+  std::unique_ptr<Response> message(Response::CreateEmpty());
+  MessageWriter writer(message.get());
+  std::vector<uint8_t> bytes;
+  writer.AppendArrayOfBytes(bytes.data(), bytes.size());
+
+  MessageReader reader(message.get());
+  const uint8_t* output_bytes = nullptr;
+  size_t length = 0;
+  ASSERT_EQ("ay", reader.GetDataSignature());
+  ASSERT_TRUE(reader.PopArrayOfBytes(&output_bytes, &length));
+  ASSERT_FALSE(reader.HasMoreData());
+  ASSERT_EQ(0U, length);
+  EXPECT_EQ(nullptr, output_bytes);
+}
+
+TEST(MessageTest, ArrayOfStrings) {
+  std::unique_ptr<Response> message(Response::CreateEmpty());
+  MessageWriter writer(message.get());
+  std::vector<std::string> strings;
+  strings.push_back("fee");
+  strings.push_back("fie");
+  strings.push_back("foe");
+  strings.push_back("fum");
+  writer.AppendArrayOfStrings(strings);
+
+  MessageReader reader(message.get());
+  std::vector<std::string> output_strings;
+  ASSERT_EQ("as", reader.GetDataSignature());
+  ASSERT_TRUE(reader.PopArrayOfStrings(&output_strings));
+  ASSERT_FALSE(reader.HasMoreData());
+  ASSERT_EQ(4U, output_strings.size());
+  EXPECT_EQ("fee", output_strings[0]);
+  EXPECT_EQ("fie", output_strings[1]);
+  EXPECT_EQ("foe", output_strings[2]);
+  EXPECT_EQ("fum", output_strings[3]);
+}
+
+TEST(MessageTest, ArrayOfObjectPaths) {
+  std::unique_ptr<Response> message(Response::CreateEmpty());
+  MessageWriter writer(message.get());
+  std::vector<ObjectPath> object_paths;
+  object_paths.push_back(ObjectPath("/object/path/1"));
+  object_paths.push_back(ObjectPath("/object/path/2"));
+  object_paths.push_back(ObjectPath("/object/path/3"));
+  writer.AppendArrayOfObjectPaths(object_paths);
+
+  MessageReader reader(message.get());
+  std::vector<ObjectPath> output_object_paths;
+  ASSERT_EQ("ao", reader.GetDataSignature());
+  ASSERT_TRUE(reader.PopArrayOfObjectPaths(&output_object_paths));
+  ASSERT_FALSE(reader.HasMoreData());
+  ASSERT_EQ(3U, output_object_paths.size());
+  EXPECT_EQ(ObjectPath("/object/path/1"), output_object_paths[0]);
+  EXPECT_EQ(ObjectPath("/object/path/2"), output_object_paths[1]);
+  EXPECT_EQ(ObjectPath("/object/path/3"), output_object_paths[2]);
+}
+
+TEST(MessageTest, ProtoBuf) {
+  std::unique_ptr<Response> message(Response::CreateEmpty());
+  MessageWriter writer(message.get());
+  TestProto send_message;
+  send_message.set_text("testing");
+  send_message.set_number(123);
+  writer.AppendProtoAsArrayOfBytes(send_message);
+
+  MessageReader reader(message.get());
+  TestProto receive_message;
+  ASSERT_EQ("ay", reader.GetDataSignature());
+  ASSERT_TRUE(reader.PopArrayOfBytesAsProto(&receive_message));
+  EXPECT_EQ(receive_message.text(), send_message.text());
+  EXPECT_EQ(receive_message.number(), send_message.number());
+}
+
+// Test that an array can be properly written and read. We only have this
+// test for array, as repeating this for other container types is too
+// redundant.
+TEST(MessageTest, OpenArrayAndPopArray) {
+  std::unique_ptr<Response> message(Response::CreateEmpty());
+  MessageWriter writer(message.get());
+  MessageWriter array_writer(nullptr);
+  writer.OpenArray("s", &array_writer);  // Open an array of strings.
+  array_writer.AppendString("foo");
+  array_writer.AppendString("bar");
+  array_writer.AppendString("baz");
+  writer.CloseContainer(&array_writer);
+
+  MessageReader reader(message.get());
+  ASSERT_EQ(Message::ARRAY, reader.GetDataType());
+  ASSERT_EQ("as", reader.GetDataSignature());
+  MessageReader array_reader(nullptr);
+  ASSERT_TRUE(reader.PopArray(&array_reader));
+  ASSERT_FALSE(reader.HasMoreData());  // Should not have more data to read.
+
+  std::string string_value;
+  ASSERT_TRUE(array_reader.PopString(&string_value));
+  EXPECT_EQ("foo", string_value);
+  ASSERT_TRUE(array_reader.PopString(&string_value));
+  EXPECT_EQ("bar", string_value);
+  ASSERT_TRUE(array_reader.PopString(&string_value));
+  EXPECT_EQ("baz", string_value);
+  // Should not have more data to read.
+  ASSERT_FALSE(array_reader.HasMoreData());
+}
+
+// Create a complex message using array, struct, variant, dict entry, and
+// make sure it can be read properly.
+TEST(MessageTest, CreateComplexMessageAndReadIt) {
+  std::unique_ptr<Response> message(Response::CreateEmpty());
+  MessageWriter writer(message.get());
+  {
+    MessageWriter array_writer(nullptr);
+    // Open an array of variants.
+    writer.OpenArray("v", &array_writer);
+    {
+      // The first value in the array.
+      {
+        MessageWriter variant_writer(nullptr);
+        // Open a variant of a boolean.
+        array_writer.OpenVariant("b", &variant_writer);
+        variant_writer.AppendBool(true);
+        array_writer.CloseContainer(&variant_writer);
+      }
+
+      // The second value in the array.
+      {
+        MessageWriter variant_writer(nullptr);
+        // Open a variant of a struct that contains a string and an int32_t.
+        array_writer.OpenVariant("(si)", &variant_writer);
+        {
+          MessageWriter struct_writer(nullptr);
+          variant_writer.OpenStruct(&struct_writer);
+          struct_writer.AppendString("string");
+          struct_writer.AppendInt32(123);
+          variant_writer.CloseContainer(&struct_writer);
+        }
+        array_writer.CloseContainer(&variant_writer);
+      }
+
+      // The third value in the array.
+      {
+        MessageWriter variant_writer(nullptr);
+        // Open a variant of an array of string-to-int64_t dict entries.
+        array_writer.OpenVariant("a{sx}", &variant_writer);
+        {
+          // Opens an array of string-to-int64_t dict entries.
+          MessageWriter dict_array_writer(nullptr);
+          variant_writer.OpenArray("{sx}", &dict_array_writer);
+          {
+            // Opens a string-to-int64_t dict entries.
+            MessageWriter dict_entry_writer(nullptr);
+            dict_array_writer.OpenDictEntry(&dict_entry_writer);
+            dict_entry_writer.AppendString("foo");
+            dict_entry_writer.AppendInt64(INT64_C(1234567890123456789));
+            dict_array_writer.CloseContainer(&dict_entry_writer);
+          }
+          variant_writer.CloseContainer(&dict_array_writer);
+        }
+        array_writer.CloseContainer(&variant_writer);
+      }
+    }
+    writer.CloseContainer(&array_writer);
+  }
+  // What we have created looks like this:
+  EXPECT_EQ(
+      "message_type: MESSAGE_METHOD_RETURN\n"
+      "signature: av\n"
+      "\n"
+      "array [\n"
+      "  variant     bool true\n"
+      "  variant     struct {\n"
+      "      string \"string\"\n"
+      "      int32_t 123\n"
+      "    }\n"
+      "  variant     array [\n"
+      "      dict entry {\n"
+      "        string \"foo\"\n"
+      "        int64_t 1234567890123456789\n"
+      "      }\n"
+      "    ]\n"
+      "]\n",
+      message->ToString());
+
+  MessageReader reader(message.get());
+  ASSERT_EQ("av", reader.GetDataSignature());
+  MessageReader array_reader(nullptr);
+  ASSERT_TRUE(reader.PopArray(&array_reader));
+
+  // The first value in the array.
+  bool bool_value = false;
+  ASSERT_EQ("v", array_reader.GetDataSignature());
+  ASSERT_TRUE(array_reader.PopVariantOfBool(&bool_value));
+  EXPECT_EQ(true, bool_value);
+
+  // The second value in the array.
+  {
+    MessageReader variant_reader(nullptr);
+    ASSERT_TRUE(array_reader.PopVariant(&variant_reader));
+    {
+      MessageReader struct_reader(nullptr);
+      ASSERT_EQ("(si)", variant_reader.GetDataSignature());
+      ASSERT_TRUE(variant_reader.PopStruct(&struct_reader));
+      std::string string_value;
+      ASSERT_TRUE(struct_reader.PopString(&string_value));
+      EXPECT_EQ("string", string_value);
+      int32_t int32_value = 0;
+      ASSERT_TRUE(struct_reader.PopInt32(&int32_value));
+      EXPECT_EQ(123, int32_value);
+      ASSERT_FALSE(struct_reader.HasMoreData());
+    }
+    ASSERT_FALSE(variant_reader.HasMoreData());
+  }
+
+  // The third value in the array.
+  {
+    MessageReader variant_reader(nullptr);
+    ASSERT_TRUE(array_reader.PopVariant(&variant_reader));
+    {
+      MessageReader dict_array_reader(nullptr);
+      ASSERT_EQ("a{sx}", variant_reader.GetDataSignature());
+      ASSERT_TRUE(variant_reader.PopArray(&dict_array_reader));
+      {
+        MessageReader dict_entry_reader(nullptr);
+        ASSERT_TRUE(dict_array_reader.PopDictEntry(&dict_entry_reader));
+        std::string string_value;
+        ASSERT_TRUE(dict_entry_reader.PopString(&string_value));
+        EXPECT_EQ("foo", string_value);
+        int64_t int64_value = 0;
+        ASSERT_TRUE(dict_entry_reader.PopInt64(&int64_value));
+        EXPECT_EQ(INT64_C(1234567890123456789), int64_value);
+      }
+      ASSERT_FALSE(dict_array_reader.HasMoreData());
+    }
+    ASSERT_FALSE(variant_reader.HasMoreData());
+  }
+  ASSERT_FALSE(array_reader.HasMoreData());
+  ASSERT_FALSE(reader.HasMoreData());
+}
+
+TEST(MessageTest, MethodCall) {
+  MethodCall method_call("com.example.Interface", "SomeMethod");
+  EXPECT_NE(nullptr, method_call.raw_message());
+  EXPECT_EQ(Message::MESSAGE_METHOD_CALL, method_call.GetMessageType());
+  EXPECT_EQ("MESSAGE_METHOD_CALL", method_call.GetMessageTypeAsString());
+  method_call.SetDestination("com.example.Service");
+  method_call.SetPath(ObjectPath("/com/example/Object"));
+
+  MessageWriter writer(&method_call);
+  writer.AppendString("payload");
+
+  EXPECT_EQ(
+      "message_type: MESSAGE_METHOD_CALL\n"
+      "destination: com.example.Service\n"
+      "path: /com/example/Object\n"
+      "interface: com.example.Interface\n"
+      "member: SomeMethod\n"
+      "signature: s\n"
+      "\n"
+      "string \"payload\"\n",
+      method_call.ToString());
+}
+
+TEST(MessageTest, MethodCall_FromRawMessage) {
+  DBusMessage* raw_message = dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_CALL);
+  dbus_message_set_interface(raw_message, "com.example.Interface");
+  dbus_message_set_member(raw_message, "SomeMethod");
+
+  std::unique_ptr<MethodCall> method_call(
+      MethodCall::FromRawMessage(raw_message));
+  EXPECT_EQ("com.example.Interface", method_call->GetInterface());
+  EXPECT_EQ("SomeMethod", method_call->GetMember());
+}
+
+TEST(MessageTest, Signal) {
+  Signal signal("com.example.Interface", "SomeSignal");
+  EXPECT_NE(nullptr, signal.raw_message());
+  EXPECT_EQ(Message::MESSAGE_SIGNAL, signal.GetMessageType());
+  EXPECT_EQ("MESSAGE_SIGNAL", signal.GetMessageTypeAsString());
+  signal.SetPath(ObjectPath("/com/example/Object"));
+
+  MessageWriter writer(&signal);
+  writer.AppendString("payload");
+
+  EXPECT_EQ(
+      "message_type: MESSAGE_SIGNAL\n"
+      "path: /com/example/Object\n"
+      "interface: com.example.Interface\n"
+      "member: SomeSignal\n"
+      "signature: s\n"
+      "\n"
+      "string \"payload\"\n",
+      signal.ToString());
+}
+
+TEST(MessageTest, Signal_FromRawMessage) {
+  DBusMessage* raw_message = dbus_message_new(DBUS_MESSAGE_TYPE_SIGNAL);
+  dbus_message_set_interface(raw_message, "com.example.Interface");
+  dbus_message_set_member(raw_message, "SomeSignal");
+
+  std::unique_ptr<Signal> signal(Signal::FromRawMessage(raw_message));
+  EXPECT_EQ("com.example.Interface", signal->GetInterface());
+  EXPECT_EQ("SomeSignal", signal->GetMember());
+}
+
+TEST(MessageTest, Response) {
+  std::unique_ptr<Response> response(Response::CreateEmpty());
+  EXPECT_TRUE(response->raw_message());
+  EXPECT_EQ(Message::MESSAGE_METHOD_RETURN, response->GetMessageType());
+  EXPECT_EQ("MESSAGE_METHOD_RETURN", response->GetMessageTypeAsString());
+}
+
+TEST(MessageTest, Response_FromMethodCall) {
+  const uint32_t kSerial = 123;
+  MethodCall method_call("com.example.Interface", "SomeMethod");
+  method_call.SetSerial(kSerial);
+
+  std::unique_ptr<Response> response(Response::FromMethodCall(&method_call));
+  EXPECT_EQ(Message::MESSAGE_METHOD_RETURN, response->GetMessageType());
+  EXPECT_EQ("MESSAGE_METHOD_RETURN", response->GetMessageTypeAsString());
+  // The serial should be copied to the reply serial.
+  EXPECT_EQ(kSerial, response->GetReplySerial());
+}
+
+TEST(MessageTest, ErrorResponse_FromMethodCall) {
+  const uint32_t kSerial = 123;
+  const char kErrorMessage[] = "error message";
+
+  MethodCall method_call("com.example.Interface", "SomeMethod");
+  method_call.SetSerial(kSerial);
+
+  std::unique_ptr<ErrorResponse> error_response(ErrorResponse::FromMethodCall(
+      &method_call, DBUS_ERROR_FAILED, kErrorMessage));
+  EXPECT_EQ(Message::MESSAGE_ERROR, error_response->GetMessageType());
+  EXPECT_EQ("MESSAGE_ERROR", error_response->GetMessageTypeAsString());
+  // The serial should be copied to the reply serial.
+  EXPECT_EQ(kSerial, error_response->GetReplySerial());
+
+  // Error message should be added to the payload.
+  MessageReader reader(error_response.get());
+  std::string error_message;
+  ASSERT_TRUE(reader.PopString(&error_message));
+  EXPECT_EQ(kErrorMessage, error_message);
+}
+
+TEST(MessageTest, GetAndSetHeaders) {
+  std::unique_ptr<Response> message(Response::CreateEmpty());
+
+  EXPECT_EQ("", message->GetDestination());
+  EXPECT_EQ(ObjectPath(std::string()), message->GetPath());
+  EXPECT_EQ("", message->GetInterface());
+  EXPECT_EQ("", message->GetMember());
+  EXPECT_EQ("", message->GetErrorName());
+  EXPECT_EQ("", message->GetSender());
+  EXPECT_EQ(0U, message->GetSerial());
+  EXPECT_EQ(0U, message->GetReplySerial());
+
+  EXPECT_TRUE(message->SetDestination("org.chromium.destination"));
+  EXPECT_TRUE(message->SetPath(ObjectPath("/org/chromium/path")));
+  EXPECT_TRUE(message->SetInterface("org.chromium.interface"));
+  EXPECT_TRUE(message->SetMember("member"));
+  EXPECT_TRUE(message->SetErrorName("org.chromium.error"));
+  EXPECT_TRUE(message->SetSender(":1.2"));
+  message->SetSerial(123);
+  message->SetReplySerial(456);
+
+  EXPECT_EQ("org.chromium.destination", message->GetDestination());
+  EXPECT_EQ(ObjectPath("/org/chromium/path"), message->GetPath());
+  EXPECT_EQ("org.chromium.interface", message->GetInterface());
+  EXPECT_EQ("member", message->GetMember());
+  EXPECT_EQ("org.chromium.error", message->GetErrorName());
+  EXPECT_EQ(":1.2", message->GetSender());
+  EXPECT_EQ(123U, message->GetSerial());
+  EXPECT_EQ(456U, message->GetReplySerial());
+}
+
+TEST(MessageTest, SetInvalidHeaders) {
+  std::unique_ptr<Response> message(Response::CreateEmpty());
+  EXPECT_EQ("", message->GetDestination());
+  EXPECT_EQ(ObjectPath(std::string()), message->GetPath());
+  EXPECT_EQ("", message->GetInterface());
+  EXPECT_EQ("", message->GetMember());
+  EXPECT_EQ("", message->GetErrorName());
+  EXPECT_EQ("", message->GetSender());
+
+  // Empty element between periods.
+  EXPECT_FALSE(message->SetDestination("org..chromium"));
+  // Trailing '/' is only allowed for the root path.
+  EXPECT_FALSE(message->SetPath(ObjectPath("/org/chromium/")));
+  // Interface name cannot contain '/'.
+  EXPECT_FALSE(message->SetInterface("org/chromium/interface"));
+  // Member name cannot begin with a digit.
+  EXPECT_FALSE(message->SetMember("1member"));
+  // Error name cannot begin with a period.
+  EXPECT_FALSE(message->SetErrorName(".org.chromium.error"));
+  // Disallowed characters.
+  EXPECT_FALSE(message->SetSender("?!#*"));
+
+  EXPECT_EQ("", message->GetDestination());
+  EXPECT_EQ(ObjectPath(std::string()), message->GetPath());
+  EXPECT_EQ("", message->GetInterface());
+  EXPECT_EQ("", message->GetMember());
+  EXPECT_EQ("", message->GetErrorName());
+  EXPECT_EQ("", message->GetSender());
+}
+
+TEST(MessageTest, ToString_LongString) {
+  const std::string kLongString(1000, 'o');
+
+  std::unique_ptr<Response> message(Response::CreateEmpty());
+  MessageWriter writer(message.get());
+  writer.AppendString(kLongString);
+
+  ASSERT_EQ(
+      "message_type: MESSAGE_METHOD_RETURN\n"
+      "signature: s\n\n"
+      "string \"oooooooooooooooooooooooooooooooooooooooooooooooo"
+      "oooooooooooooooooooooooooooooooooooooooooooooooooooo... "
+      "(1000 bytes in total)\"\n",
+      message->ToString());
+}
+
+}  // namespace dbus
diff --git a/dbus/mock_unittest.cc b/dbus/mock_unittest.cc
new file mode 100644
index 0000000..aa26bc4
--- /dev/null
+++ b/dbus/mock_unittest.cc
@@ -0,0 +1,215 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "dbus/message.h"
+#include "dbus/mock_bus.h"
+#include "dbus/mock_exported_object.h"
+#include "dbus/mock_object_proxy.h"
+#include "dbus/object_path.h"
+#include "dbus/scoped_dbus_error.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::_;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::Unused;
+
+namespace dbus {
+
+class MockTest : public testing::Test {
+ public:
+  MockTest() = default;
+
+  void SetUp() override {
+    // Create a mock bus.
+    Bus::Options options;
+    options.bus_type = Bus::SYSTEM;
+    mock_bus_ = new MockBus(options);
+
+    // Create a mock proxy.
+    mock_proxy_ = new MockObjectProxy(
+        mock_bus_.get(),
+        "org.chromium.TestService",
+        ObjectPath("/org/chromium/TestObject"));
+
+    // Set an expectation so mock_proxy's CallMethodAndBlock() will use
+    // CreateMockProxyResponse() to return responses.
+    EXPECT_CALL(*mock_proxy_.get(), CallMethodAndBlock(_, _))
+        .WillRepeatedly(Invoke(this, &MockTest::CreateMockProxyResponse));
+    EXPECT_CALL(*mock_proxy_.get(),
+                CallMethodAndBlockWithErrorDetails(_, _, _))
+        .WillRepeatedly(
+            Invoke(this, &MockTest::CreateMockProxyResponseWithErrorDetails));
+
+    // Set an expectation so mock_proxy's CallMethod() will use
+    // HandleMockProxyResponseWithMessageLoop() to return responses.
+    EXPECT_CALL(*mock_proxy_.get(), DoCallMethod(_, _, _))
+        .WillRepeatedly(
+            Invoke(this, &MockTest::HandleMockProxyResponseWithMessageLoop));
+
+    // Set an expectation so mock_bus's GetObjectProxy() for the given
+    // service name and the object path will return mock_proxy_.
+    EXPECT_CALL(*mock_bus_.get(),
+                GetObjectProxy("org.chromium.TestService",
+                               ObjectPath("/org/chromium/TestObject")))
+        .WillOnce(Return(mock_proxy_.get()));
+
+    // ShutdownAndBlock() will be called in TearDown().
+    EXPECT_CALL(*mock_bus_.get(), ShutdownAndBlock()).WillOnce(Return());
+  }
+
+  void TearDown() override { mock_bus_->ShutdownAndBlock(); }
+
+  // Called when the response is received.
+  void OnResponse(Response* response) {
+    // |response| will be deleted on exit of the function. Copy the
+    // payload to |response_string_|.
+    if (response) {
+      MessageReader reader(response);
+      ASSERT_TRUE(reader.PopString(&response_string_));
+    }
+    run_loop_->Quit();
+  };
+
+ protected:
+  std::string response_string_;
+  base::MessageLoop message_loop_;
+  std::unique_ptr<base::RunLoop> run_loop_;
+  scoped_refptr<MockBus> mock_bus_;
+  scoped_refptr<MockObjectProxy> mock_proxy_;
+
+ private:
+  // Returns a response for the given method call. Used to implement
+  // CallMethodAndBlock() for |mock_proxy_|.
+  std::unique_ptr<Response> CreateMockProxyResponse(MethodCall* method_call,
+                                                    int timeout_ms) {
+    if (method_call->GetInterface() == "org.chromium.TestInterface" &&
+        method_call->GetMember() == "Echo") {
+      MessageReader reader(method_call);
+      std::string text_message;
+      if (reader.PopString(&text_message)) {
+        std::unique_ptr<Response> response = Response::CreateEmpty();
+        MessageWriter writer(response.get());
+        writer.AppendString(text_message);
+        return response;
+      }
+    }
+
+    LOG(ERROR) << "Unexpected method call: " << method_call->ToString();
+    return nullptr;
+  }
+
+  std::unique_ptr<Response> CreateMockProxyResponseWithErrorDetails(
+      MethodCall* method_call, int timeout_ms, ScopedDBusError* error) {
+    dbus_set_error(error->get(), DBUS_ERROR_NOT_SUPPORTED, "Not implemented");
+    return nullptr;
+  }
+
+  // Creates a response and runs the given response callback in the
+  // message loop with the response. Used to implement for |mock_proxy_|.
+  void HandleMockProxyResponseWithMessageLoop(
+      MethodCall* method_call,
+      int timeout_ms,
+      ObjectProxy::ResponseCallback* response_callback) {
+    std::unique_ptr<Response> response =
+        CreateMockProxyResponse(method_call, timeout_ms);
+    message_loop_.task_runner()->PostTask(
+        FROM_HERE,
+        base::BindOnce(&MockTest::RunResponseCallback, base::Unretained(this),
+                       std::move(*response_callback), std::move(response)));
+  }
+
+  // Runs the given response callback with the given response.
+  void RunResponseCallback(
+      ObjectProxy::ResponseCallback response_callback,
+      std::unique_ptr<Response> response) {
+    std::move(response_callback).Run(response.get());
+  }
+};
+
+// This test demonstrates how to mock a synchronous method call using the
+// mock classes.
+TEST_F(MockTest, CallMethodAndBlock) {
+  const char kHello[] = "Hello";
+  // Get an object proxy from the mock bus.
+  ObjectProxy* proxy = mock_bus_->GetObjectProxy(
+      "org.chromium.TestService",
+      ObjectPath("/org/chromium/TestObject"));
+
+  // Create a method call.
+  MethodCall method_call("org.chromium.TestInterface", "Echo");
+  MessageWriter writer(&method_call);
+  writer.AppendString(kHello);
+
+  // Call the method.
+  std::unique_ptr<Response> response(proxy->CallMethodAndBlock(
+      &method_call, ObjectProxy::TIMEOUT_USE_DEFAULT));
+
+  // Check the response.
+  ASSERT_TRUE(response.get());
+  MessageReader reader(response.get());
+  std::string text_message;
+  ASSERT_TRUE(reader.PopString(&text_message));
+  // The text message should be echo'ed back.
+  EXPECT_EQ(kHello, text_message);
+}
+
+TEST_F(MockTest, CallMethodAndBlockWithErrorDetails) {
+  // Get an object proxy from the mock bus.
+  ObjectProxy* proxy = mock_bus_->GetObjectProxy(
+      "org.chromium.TestService",
+      ObjectPath("/org/chromium/TestObject"));
+
+  // Create a method call.
+  MethodCall method_call("org.chromium.TestInterface", "Echo");
+
+  ScopedDBusError error;
+  // Call the method.
+  std::unique_ptr<Response> response(proxy->CallMethodAndBlockWithErrorDetails(
+      &method_call, ObjectProxy::TIMEOUT_USE_DEFAULT, &error));
+
+  // Check the response.
+  ASSERT_FALSE(response.get());
+  ASSERT_TRUE(error.is_set());
+  EXPECT_STREQ(DBUS_ERROR_NOT_SUPPORTED, error.name());
+  EXPECT_STREQ("Not implemented", error.message());
+}
+
+// This test demonstrates how to mock an asynchronous method call using the
+// mock classes.
+TEST_F(MockTest, CallMethod) {
+  const char kHello[] = "hello";
+
+  // Get an object proxy from the mock bus.
+  ObjectProxy* proxy = mock_bus_->GetObjectProxy(
+      "org.chromium.TestService",
+      ObjectPath("/org/chromium/TestObject"));
+
+  // Create a method call.
+  MethodCall method_call("org.chromium.TestInterface", "Echo");
+  MessageWriter writer(&method_call);
+  writer.AppendString(kHello);
+
+  // Call the method.
+  run_loop_.reset(new base::RunLoop);
+  proxy->CallMethod(&method_call,
+                    ObjectProxy::TIMEOUT_USE_DEFAULT,
+                    base::Bind(&MockTest::OnResponse,
+                               base::Unretained(this)));
+  // Run the message loop to let OnResponse be called.
+  run_loop_->Run();
+
+  EXPECT_EQ(kHello, response_string_);
+}
+
+}  // namespace dbus
diff --git a/dbus/object_manager_unittest.cc b/dbus/object_manager_unittest.cc
new file mode 100644
index 0000000..0fb74ad
--- /dev/null
+++ b/dbus/object_manager_unittest.cc
@@ -0,0 +1,419 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "dbus/object_manager.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "base/threading/thread.h"
+#include "base/threading/thread_restrictions.h"
+#include "dbus/bus.h"
+#include "dbus/object_path.h"
+#include "dbus/object_proxy.h"
+#include "dbus/property.h"
+#include "dbus/test_service.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace dbus {
+
+// The object manager test exercises the asynchronous APIs in ObjectManager,
+// and by extension PropertySet and Property<>.
+class ObjectManagerTest
+    : public testing::Test,
+      public ObjectManager::Interface {
+ public:
+  ObjectManagerTest() : timeout_expired_(false) {
+  }
+
+  struct Properties : public PropertySet {
+    Property<std::string> name;
+    Property<int16_t> version;
+    Property<std::vector<std::string>> methods;
+    Property<std::vector<ObjectPath>> objects;
+
+    Properties(ObjectProxy* object_proxy,
+               const std::string& interface_name,
+               PropertyChangedCallback property_changed_callback)
+        : PropertySet(object_proxy, interface_name, property_changed_callback) {
+      RegisterProperty("Name", &name);
+      RegisterProperty("Version", &version);
+      RegisterProperty("Methods", &methods);
+      RegisterProperty("Objects", &objects);
+    }
+  };
+
+  PropertySet* CreateProperties(ObjectProxy* object_proxy,
+                                const ObjectPath& object_path,
+                                const std::string& interface_name) override {
+    Properties* properties = new Properties(
+        object_proxy, interface_name,
+        base::Bind(&ObjectManagerTest::OnPropertyChanged,
+                   base::Unretained(this), object_path));
+    return static_cast<PropertySet*>(properties);
+  }
+
+  void SetUp() override {
+    // Make the main thread not to allow IO.
+    base::ThreadRestrictions::SetIOAllowed(false);
+
+    // Start the D-Bus thread.
+    dbus_thread_.reset(new base::Thread("D-Bus Thread"));
+    base::Thread::Options thread_options;
+    thread_options.message_loop_type = base::MessageLoop::TYPE_IO;
+    ASSERT_TRUE(dbus_thread_->StartWithOptions(thread_options));
+
+    // Start the test service, using the D-Bus thread.
+    TestService::Options options;
+    options.dbus_task_runner = dbus_thread_->task_runner();
+    test_service_.reset(new TestService(options));
+    ASSERT_TRUE(test_service_->StartService());
+    ASSERT_TRUE(test_service_->WaitUntilServiceIsStarted());
+    ASSERT_TRUE(test_service_->HasDBusThread());
+
+    // Create the client, using the D-Bus thread.
+    Bus::Options bus_options;
+    bus_options.bus_type = Bus::SESSION;
+    bus_options.connection_type = Bus::PRIVATE;
+    bus_options.dbus_task_runner = dbus_thread_->task_runner();
+    bus_ = new Bus(bus_options);
+    ASSERT_TRUE(bus_->HasDBusThread());
+
+    object_manager_ = bus_->GetObjectManager(
+        test_service_->service_name(),
+        ObjectPath("/org/chromium/TestService"));
+    object_manager_->RegisterInterface("org.chromium.TestInterface", this);
+
+    WaitForObject();
+  }
+
+  void TearDown() override {
+    bus_->ShutdownOnDBusThreadAndBlock();
+
+    // Shut down the service.
+    test_service_->ShutdownAndBlock();
+
+    // Reset to the default.
+    base::ThreadRestrictions::SetIOAllowed(true);
+
+    // Stopping a thread is considered an IO operation, so do this after
+    // allowing IO.
+    test_service_->Stop();
+
+    base::RunLoop().RunUntilIdle();
+  }
+
+  void MethodCallback(Response* response) {
+    method_callback_called_ = true;
+    run_loop_->Quit();
+  }
+
+  // Called from the PropertiesChangedAsObjectsReceived test case. The test will
+  // not run the message loop if it receives the expected PropertiesChanged
+  // signal before the timeout. This method immediately fails the test.
+  void PropertiesChangedTestTimeout() {
+    timeout_expired_ = true;
+    run_loop_->Quit();
+
+    FAIL() << "Never received PropertiesChanged";
+  }
+
+ protected:
+  // Called when an object is added.
+  void ObjectAdded(const ObjectPath& object_path,
+                   const std::string& interface_name) override {
+    added_objects_.push_back(std::make_pair(object_path, interface_name));
+    run_loop_->Quit();
+  }
+
+  // Called when an object is removed.
+  void ObjectRemoved(const ObjectPath& object_path,
+                     const std::string& interface_name) override {
+    removed_objects_.push_back(std::make_pair(object_path, interface_name));
+    run_loop_->Quit();
+  }
+
+  // Called when a property value is updated.
+  void OnPropertyChanged(const ObjectPath& object_path,
+                         const std::string& name) {
+    // Store the value of the "Name" property if that's the one that
+    // changed.
+    Properties* properties = static_cast<Properties*>(
+        object_manager_->GetProperties(
+            object_path,
+            "org.chromium.TestInterface"));
+    if (name == properties->name.name())
+      last_name_value_ = properties->name.value();
+
+    // Store the updated property.
+    updated_properties_.push_back(name);
+    run_loop_->Quit();
+  }
+
+  static const size_t kExpectedObjects = 1;
+  static const size_t kExpectedProperties = 4;
+
+  void WaitForObject() {
+    while (added_objects_.size() < kExpectedObjects ||
+           updated_properties_.size() < kExpectedProperties) {
+      run_loop_.reset(new base::RunLoop);
+      run_loop_->Run();
+    }
+    for (size_t i = 0; i < kExpectedObjects; ++i)
+      added_objects_.erase(added_objects_.begin());
+    for (size_t i = 0; i < kExpectedProperties; ++i)
+      updated_properties_.erase(updated_properties_.begin());
+  }
+
+  void WaitForRemoveObject() {
+    while (removed_objects_.size() < kExpectedObjects) {
+      run_loop_.reset(new base::RunLoop);
+      run_loop_->Run();
+    }
+    for (size_t i = 0; i < kExpectedObjects; ++i)
+      removed_objects_.erase(removed_objects_.begin());
+  }
+
+  void WaitForMethodCallback() {
+    run_loop_.reset(new base::RunLoop);
+    run_loop_->Run();
+    method_callback_called_ = false;
+  }
+
+  void PerformAction(const std::string& action, const ObjectPath& object_path) {
+    ObjectProxy* object_proxy = bus_->GetObjectProxy(
+        test_service_->service_name(),
+        ObjectPath("/org/chromium/TestObject"));
+
+    MethodCall method_call("org.chromium.TestInterface", "PerformAction");
+    MessageWriter writer(&method_call);
+    writer.AppendString(action);
+    writer.AppendObjectPath(object_path);
+
+    object_proxy->CallMethod(&method_call,
+                             ObjectProxy::TIMEOUT_USE_DEFAULT,
+                             base::Bind(&ObjectManagerTest::MethodCallback,
+                                        base::Unretained(this)));
+    WaitForMethodCallback();
+  }
+
+  base::MessageLoop message_loop_;
+  std::unique_ptr<base::RunLoop> run_loop_;
+  std::unique_ptr<base::Thread> dbus_thread_;
+  scoped_refptr<Bus> bus_;
+  ObjectManager* object_manager_;
+  std::unique_ptr<TestService> test_service_;
+
+  std::string last_name_value_;
+  bool timeout_expired_;
+
+  std::vector<std::pair<ObjectPath, std::string>> added_objects_;
+  std::vector<std::pair<ObjectPath, std::string>> removed_objects_;
+  std::vector<std::string> updated_properties_;
+
+  bool method_callback_called_;
+};
+
+
+TEST_F(ObjectManagerTest, InitialObject) {
+  ObjectProxy* object_proxy = object_manager_->GetObjectProxy(
+      ObjectPath("/org/chromium/TestObject"));
+  EXPECT_NE(nullptr, object_proxy);
+
+  Properties* properties = static_cast<Properties*>(
+      object_manager_->GetProperties(ObjectPath("/org/chromium/TestObject"),
+                                     "org.chromium.TestInterface"));
+  EXPECT_NE(nullptr, properties);
+
+  EXPECT_EQ("TestService", properties->name.value());
+  EXPECT_EQ(10, properties->version.value());
+
+  std::vector<std::string> methods = properties->methods.value();
+  ASSERT_EQ(4U, methods.size());
+  EXPECT_EQ("Echo", methods[0]);
+  EXPECT_EQ("SlowEcho", methods[1]);
+  EXPECT_EQ("AsyncEcho", methods[2]);
+  EXPECT_EQ("BrokenMethod", methods[3]);
+
+  std::vector<ObjectPath> objects = properties->objects.value();
+  ASSERT_EQ(1U, objects.size());
+  EXPECT_EQ(ObjectPath("/TestObjectPath"), objects[0]);
+}
+
+TEST_F(ObjectManagerTest, UnknownObjectProxy) {
+  ObjectProxy* object_proxy = object_manager_->GetObjectProxy(
+      ObjectPath("/org/chromium/UnknownObject"));
+  EXPECT_EQ(nullptr, object_proxy);
+}
+
+TEST_F(ObjectManagerTest, UnknownObjectProperties) {
+  Properties* properties = static_cast<Properties*>(
+      object_manager_->GetProperties(ObjectPath("/org/chromium/UnknownObject"),
+                                     "org.chromium.TestInterface"));
+  EXPECT_EQ(nullptr, properties);
+}
+
+TEST_F(ObjectManagerTest, UnknownInterfaceProperties) {
+  Properties* properties = static_cast<Properties*>(
+      object_manager_->GetProperties(ObjectPath("/org/chromium/TestObject"),
+                                     "org.chromium.UnknownService"));
+  EXPECT_EQ(nullptr, properties);
+}
+
+TEST_F(ObjectManagerTest, GetObjects) {
+  std::vector<ObjectPath> object_paths = object_manager_->GetObjects();
+  ASSERT_EQ(1U, object_paths.size());
+  EXPECT_EQ(ObjectPath("/org/chromium/TestObject"), object_paths[0]);
+}
+
+TEST_F(ObjectManagerTest, GetObjectsWithInterface) {
+  std::vector<ObjectPath> object_paths =
+      object_manager_->GetObjectsWithInterface("org.chromium.TestInterface");
+  ASSERT_EQ(1U, object_paths.size());
+  EXPECT_EQ(ObjectPath("/org/chromium/TestObject"), object_paths[0]);
+}
+
+TEST_F(ObjectManagerTest, GetObjectsWithUnknownInterface) {
+  std::vector<ObjectPath> object_paths =
+      object_manager_->GetObjectsWithInterface("org.chromium.UnknownService");
+  EXPECT_EQ(0U, object_paths.size());
+}
+
+TEST_F(ObjectManagerTest, SameObject) {
+  ObjectManager* object_manager = bus_->GetObjectManager(
+      test_service_->service_name(),
+      ObjectPath("/org/chromium/TestService"));
+  EXPECT_EQ(object_manager_, object_manager);
+}
+
+TEST_F(ObjectManagerTest, DifferentObjectForService) {
+  ObjectManager* object_manager = bus_->GetObjectManager(
+      "org.chromium.DifferentService",
+      ObjectPath("/org/chromium/TestService"));
+  EXPECT_NE(object_manager_, object_manager);
+}
+
+TEST_F(ObjectManagerTest, DifferentObjectForPath) {
+  ObjectManager* object_manager = bus_->GetObjectManager(
+      test_service_->service_name(),
+      ObjectPath("/org/chromium/DifferentService"));
+  EXPECT_NE(object_manager_, object_manager);
+}
+
+TEST_F(ObjectManagerTest, SecondObject) {
+  PerformAction("AddObject", ObjectPath("/org/chromium/SecondObject"));
+  WaitForObject();
+
+  ObjectProxy* object_proxy = object_manager_->GetObjectProxy(
+      ObjectPath("/org/chromium/SecondObject"));
+  EXPECT_NE(nullptr, object_proxy);
+
+  Properties* properties = static_cast<Properties*>(
+      object_manager_->GetProperties(ObjectPath("/org/chromium/SecondObject"),
+                                     "org.chromium.TestInterface"));
+  EXPECT_NE(nullptr, properties);
+
+  std::vector<ObjectPath> object_paths = object_manager_->GetObjects();
+  ASSERT_EQ(2U, object_paths.size());
+
+  std::sort(object_paths.begin(), object_paths.end());
+  EXPECT_EQ(ObjectPath("/org/chromium/SecondObject"), object_paths[0]);
+  EXPECT_EQ(ObjectPath("/org/chromium/TestObject"), object_paths[1]);
+
+  object_paths =
+      object_manager_->GetObjectsWithInterface("org.chromium.TestInterface");
+  ASSERT_EQ(2U, object_paths.size());
+
+  std::sort(object_paths.begin(), object_paths.end());
+  EXPECT_EQ(ObjectPath("/org/chromium/SecondObject"), object_paths[0]);
+  EXPECT_EQ(ObjectPath("/org/chromium/TestObject"), object_paths[1]);
+}
+
+TEST_F(ObjectManagerTest, RemoveSecondObject) {
+  PerformAction("AddObject", ObjectPath("/org/chromium/SecondObject"));
+  WaitForObject();
+
+  std::vector<ObjectPath> object_paths = object_manager_->GetObjects();
+  ASSERT_EQ(2U, object_paths.size());
+
+  PerformAction("RemoveObject", ObjectPath("/org/chromium/SecondObject"));
+  WaitForRemoveObject();
+
+  ObjectProxy* object_proxy = object_manager_->GetObjectProxy(
+      ObjectPath("/org/chromium/SecondObject"));
+  EXPECT_EQ(nullptr, object_proxy);
+
+  Properties* properties = static_cast<Properties*>(
+      object_manager_->GetProperties(ObjectPath("/org/chromium/SecondObject"),
+                                     "org.chromium.TestInterface"));
+  EXPECT_EQ(nullptr, properties);
+
+  object_paths = object_manager_->GetObjects();
+  ASSERT_EQ(1U, object_paths.size());
+  EXPECT_EQ(ObjectPath("/org/chromium/TestObject"), object_paths[0]);
+
+  object_paths =
+      object_manager_->GetObjectsWithInterface("org.chromium.TestInterface");
+  ASSERT_EQ(1U, object_paths.size());
+  EXPECT_EQ(ObjectPath("/org/chromium/TestObject"), object_paths[0]);
+}
+
+TEST_F(ObjectManagerTest, OwnershipLost) {
+  PerformAction("ReleaseOwnership", ObjectPath("/org/chromium/TestService"));
+  WaitForRemoveObject();
+
+  std::vector<ObjectPath> object_paths = object_manager_->GetObjects();
+  ASSERT_EQ(0U, object_paths.size());
+}
+
+TEST_F(ObjectManagerTest, OwnershipLostAndRegained) {
+  PerformAction("Ownership", ObjectPath("/org/chromium/TestService"));
+  WaitForRemoveObject();
+  WaitForObject();
+
+  std::vector<ObjectPath> object_paths = object_manager_->GetObjects();
+  ASSERT_EQ(1U, object_paths.size());
+}
+
+TEST_F(ObjectManagerTest, PropertiesChangedAsObjectsReceived) {
+  // Remove the existing object manager.
+  object_manager_->UnregisterInterface("org.chromium.TestInterface");
+  run_loop_.reset(new base::RunLoop);
+  EXPECT_TRUE(bus_->RemoveObjectManager(
+      test_service_->service_name(),
+      ObjectPath("/org/chromium/TestService"),
+      run_loop_->QuitClosure()));
+  run_loop_->Run();
+
+  PerformAction("SetSendImmediatePropertiesChanged",
+                ObjectPath("/org/chromium/TestService"));
+
+  object_manager_ = bus_->GetObjectManager(
+      test_service_->service_name(),
+      ObjectPath("/org/chromium/TestService"));
+  object_manager_->RegisterInterface("org.chromium.TestInterface", this);
+
+  // The newly created object manager should call GetManagedObjects immediately
+  // after setting up the match rule for PropertiesChanged. We should process
+  // the PropertiesChanged event right after that. If we don't receive it within
+  // 2 seconds, then fail the test.
+  message_loop_.task_runner()->PostDelayedTask(
+      FROM_HERE, base::Bind(&ObjectManagerTest::PropertiesChangedTestTimeout,
+                            base::Unretained(this)),
+      base::TimeDelta::FromSeconds(2));
+
+  while (last_name_value_ != "ChangedTestServiceName" && !timeout_expired_) {
+    run_loop_.reset(new base::RunLoop);
+    run_loop_->Run();
+  }
+}
+
+}  // namespace dbus
diff --git a/dbus/object_proxy_unittest.cc b/dbus/object_proxy_unittest.cc
new file mode 100644
index 0000000..1f5746f
--- /dev/null
+++ b/dbus/object_proxy_unittest.cc
@@ -0,0 +1,147 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "dbus/object_proxy.h"
+#include "base/bind.h"
+#include "base/files/file_descriptor_watcher_posix.h"
+#include "base/memory/ref_counted.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "dbus/bus.h"
+#include "dbus/test_service.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace dbus {
+namespace {
+
+class ObjectProxyTest : public testing::Test {
+ protected:
+  ObjectProxyTest() : file_descriptor_watcher_(&message_loop_) {}
+
+  void SetUp() override {
+    Bus::Options bus_options;
+    bus_options.bus_type = Bus::SESSION;
+    bus_options.connection_type = Bus::PRIVATE;
+    bus_ = new Bus(bus_options);
+  }
+
+  void TearDown() override { bus_->ShutdownAndBlock(); }
+
+  base::MessageLoopForIO message_loop_;
+
+  // This enables FileDescriptorWatcher, which is required by dbus::Watch.
+  base::FileDescriptorWatcher file_descriptor_watcher_;
+
+  scoped_refptr<Bus> bus_;
+};
+
+// Used as a WaitForServiceToBeAvailableCallback.
+void OnServiceIsAvailable(bool* dest_service_is_available,
+                          int* num_calls,
+                          bool src_service_is_available) {
+  *dest_service_is_available = src_service_is_available;
+  (*num_calls)++;
+}
+
+// Used as a callback for TestService::RequestOwnership().
+void OnOwnershipRequestDone(bool success) {
+  ASSERT_TRUE(success);
+}
+
+// Used as a callback for TestService::ReleaseOwnership().
+void OnOwnershipReleased() {}
+
+TEST_F(ObjectProxyTest, WaitForServiceToBeAvailableRunOnce) {
+  TestService::Options options;
+  TestService test_service(options);
+  ObjectProxy* object_proxy = bus_->GetObjectProxy(
+      test_service.service_name(), ObjectPath("/org/chromium/TestObject"));
+
+  // The callback is not yet called because the service is not available.
+  int num_calls = 0;
+  bool service_is_available = false;
+  object_proxy->WaitForServiceToBeAvailable(
+      base::Bind(&OnServiceIsAvailable, &service_is_available, &num_calls));
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(0, num_calls);
+
+  // Start the service. The callback should be called asynchronously.
+  ASSERT_TRUE(test_service.StartService());
+  ASSERT_TRUE(test_service.WaitUntilServiceIsStarted());
+  ASSERT_TRUE(test_service.has_ownership());
+  num_calls = 0;
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(1, num_calls);
+  EXPECT_TRUE(service_is_available);
+
+  // Release the service's ownership of its name. The callback should not be
+  // invoked again.
+  test_service.ReleaseOwnership(base::Bind(&OnOwnershipReleased));
+  num_calls = 0;
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(0, num_calls);
+
+  // Take ownership of the name and check that the callback is not called.
+  test_service.RequestOwnership(base::Bind(&OnOwnershipRequestDone));
+  num_calls = 0;
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(0, num_calls);
+}
+
+TEST_F(ObjectProxyTest, WaitForServiceToBeAvailableAlreadyRunning) {
+  TestService::Options options;
+  TestService test_service(options);
+  ObjectProxy* object_proxy = bus_->GetObjectProxy(
+      test_service.service_name(), ObjectPath("/org/chromium/TestObject"));
+
+  ASSERT_TRUE(test_service.StartService());
+  ASSERT_TRUE(test_service.WaitUntilServiceIsStarted());
+  ASSERT_TRUE(test_service.has_ownership());
+
+  // Since the service is already running, the callback should be invoked
+  // immediately (but asynchronously, rather than the callback being invoked
+  // directly within WaitForServiceToBeAvailable()).
+  int num_calls = 0;
+  bool service_is_available = false;
+  object_proxy->WaitForServiceToBeAvailable(
+      base::Bind(&OnServiceIsAvailable, &service_is_available, &num_calls));
+  EXPECT_EQ(0, num_calls);
+
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(1, num_calls);
+  EXPECT_TRUE(service_is_available);
+}
+
+TEST_F(ObjectProxyTest, WaitForServiceToBeAvailableMultipleCallbacks) {
+  TestService::Options options;
+  TestService test_service(options);
+  ObjectProxy* object_proxy = bus_->GetObjectProxy(
+      test_service.service_name(), ObjectPath("/org/chromium/TestObject"));
+
+  // Register two callbacks.
+  int num_calls_1 = 0, num_calls_2 = 0;
+  bool service_is_available_1 = false, service_is_available_2 = false;
+  object_proxy->WaitForServiceToBeAvailable(
+      base::Bind(&OnServiceIsAvailable, &service_is_available_1, &num_calls_1));
+  object_proxy->WaitForServiceToBeAvailable(
+      base::Bind(&OnServiceIsAvailable, &service_is_available_2, &num_calls_2));
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(0, num_calls_1);
+  EXPECT_EQ(0, num_calls_2);
+
+  // Start the service and confirm that both callbacks are invoked.
+  ASSERT_TRUE(test_service.StartService());
+  ASSERT_TRUE(test_service.WaitUntilServiceIsStarted());
+  ASSERT_TRUE(test_service.has_ownership());
+  num_calls_1 = 0;
+  num_calls_2 = 0;
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(1, num_calls_1);
+  EXPECT_EQ(1, num_calls_2);
+  EXPECT_TRUE(service_is_available_1);
+  EXPECT_TRUE(service_is_available_2);
+}
+
+}  // namespace
+}  // namespace dbus
diff --git a/dbus/property_unittest.cc b/dbus/property_unittest.cc
new file mode 100644
index 0000000..5b26edd
--- /dev/null
+++ b/dbus/property_unittest.cc
@@ -0,0 +1,545 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "dbus/property.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/threading/thread.h"
+#include "base/threading/thread_restrictions.h"
+#include "dbus/bus.h"
+#include "dbus/object_path.h"
+#include "dbus/object_proxy.h"
+#include "dbus/test_service.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace dbus {
+
+// The property test exerises the asynchronous APIs in PropertySet and
+// Property<>.
+class PropertyTest : public testing::Test {
+ public:
+  PropertyTest() = default;
+
+  struct Properties : public PropertySet {
+    Property<std::string> name;
+    Property<int16_t> version;
+    Property<std::vector<std::string>> methods;
+    Property<std::vector<ObjectPath>> objects;
+    Property<std::vector<uint8_t>> bytes;
+
+    Properties(ObjectProxy* object_proxy,
+               PropertyChangedCallback property_changed_callback)
+        : PropertySet(object_proxy,
+                      "org.chromium.TestInterface",
+                      property_changed_callback) {
+      RegisterProperty("Name", &name);
+      RegisterProperty("Version", &version);
+      RegisterProperty("Methods", &methods);
+      RegisterProperty("Objects", &objects);
+      RegisterProperty("Bytes", &bytes);
+    }
+  };
+
+  void SetUp() override {
+    // Make the main thread not to allow IO.
+    base::ThreadRestrictions::SetIOAllowed(false);
+
+    // Start the D-Bus thread.
+    dbus_thread_.reset(new base::Thread("D-Bus Thread"));
+    base::Thread::Options thread_options;
+    thread_options.message_loop_type = base::MessageLoop::TYPE_IO;
+    ASSERT_TRUE(dbus_thread_->StartWithOptions(thread_options));
+
+    // Start the test service, using the D-Bus thread.
+    TestService::Options options;
+    options.dbus_task_runner = dbus_thread_->task_runner();
+    test_service_.reset(new TestService(options));
+    ASSERT_TRUE(test_service_->StartService());
+    ASSERT_TRUE(test_service_->WaitUntilServiceIsStarted());
+    ASSERT_TRUE(test_service_->HasDBusThread());
+
+    // Create the client, using the D-Bus thread.
+    Bus::Options bus_options;
+    bus_options.bus_type = Bus::SESSION;
+    bus_options.connection_type = Bus::PRIVATE;
+    bus_options.dbus_task_runner = dbus_thread_->task_runner();
+    bus_ = new Bus(bus_options);
+    object_proxy_ = bus_->GetObjectProxy(
+        test_service_->service_name(),
+        ObjectPath("/org/chromium/TestObject"));
+    ASSERT_TRUE(bus_->HasDBusThread());
+
+    // Create the properties structure
+    properties_.reset(new Properties(
+        object_proxy_,
+        base::Bind(&PropertyTest::OnPropertyChanged,
+                   base::Unretained(this))));
+    properties_->ConnectSignals();
+    properties_->GetAll();
+  }
+
+  void TearDown() override {
+    bus_->ShutdownOnDBusThreadAndBlock();
+
+    // Shut down the service.
+    test_service_->ShutdownAndBlock();
+
+    // Reset to the default.
+    base::ThreadRestrictions::SetIOAllowed(true);
+
+    // Stopping a thread is considered an IO operation, so do this after
+    // allowing IO.
+    test_service_->Stop();
+  }
+
+  // Generic callback, bind with a string |id| for passing to
+  // WaitForCallback() to ensure the callback for the right method is
+  // waited for.
+  void PropertyCallback(const std::string& id, bool success) {
+    last_callback_ = id;
+    run_loop_->Quit();
+  }
+
+  // Generic method callback, that might be used together with
+  // WaitForMethodCallback to test wether method was succesfully called.
+  void MethodCallback(Response* response) { run_loop_->Quit(); }
+
+ protected:
+  // Called when a property value is updated.
+  void OnPropertyChanged(const std::string& name) {
+    updated_properties_.push_back(name);
+    run_loop_->Quit();
+  }
+
+  // Waits for the given number of updates.
+  void WaitForUpdates(size_t num_updates) {
+    while (updated_properties_.size() < num_updates) {
+      run_loop_.reset(new base::RunLoop);
+      run_loop_->Run();
+    }
+    for (size_t i = 0; i < num_updates; ++i)
+      updated_properties_.erase(updated_properties_.begin());
+  }
+
+  // Name, Version, Methods, Objects
+  static const int kExpectedSignalUpdates = 5;
+
+  // Waits for initial values to be set.
+  void WaitForGetAll() {
+    WaitForUpdates(kExpectedSignalUpdates);
+  }
+
+  // Waits until MethodCallback is called.
+  void WaitForMethodCallback() {
+    run_loop_.reset(new base::RunLoop);
+    run_loop_->Run();
+  }
+
+  // Waits for the callback. |id| is the string bound to the callback when
+  // the method call is made that identifies it and distinguishes from any
+  // other; you can set this to whatever you wish.
+  void WaitForCallback(const std::string& id) {
+    while (last_callback_ != id) {
+      run_loop_.reset(new base::RunLoop);
+      run_loop_->Run();
+    }
+  }
+
+  base::MessageLoop message_loop_;
+  std::unique_ptr<base::RunLoop> run_loop_;
+  std::unique_ptr<base::Thread> dbus_thread_;
+  scoped_refptr<Bus> bus_;
+  ObjectProxy* object_proxy_;
+  std::unique_ptr<Properties> properties_;
+  std::unique_ptr<TestService> test_service_;
+  // Properties updated.
+  std::vector<std::string> updated_properties_;
+  // Last callback received.
+  std::string last_callback_;
+};
+
+TEST_F(PropertyTest, InitialValues) {
+  EXPECT_FALSE(properties_->name.is_valid());
+  EXPECT_FALSE(properties_->version.is_valid());
+
+  WaitForGetAll();
+
+  EXPECT_TRUE(properties_->name.is_valid());
+  EXPECT_EQ("TestService", properties_->name.value());
+  EXPECT_TRUE(properties_->version.is_valid());
+  EXPECT_EQ(10, properties_->version.value());
+
+  std::vector<std::string> methods = properties_->methods.value();
+  ASSERT_EQ(4U, methods.size());
+  EXPECT_EQ("Echo", methods[0]);
+  EXPECT_EQ("SlowEcho", methods[1]);
+  EXPECT_EQ("AsyncEcho", methods[2]);
+  EXPECT_EQ("BrokenMethod", methods[3]);
+
+  std::vector<ObjectPath> objects = properties_->objects.value();
+  ASSERT_EQ(1U, objects.size());
+  EXPECT_EQ(ObjectPath("/TestObjectPath"), objects[0]);
+
+  std::vector<uint8_t> bytes = properties_->bytes.value();
+  ASSERT_EQ(4U, bytes.size());
+  EXPECT_EQ('T', bytes[0]);
+  EXPECT_EQ('e', bytes[1]);
+  EXPECT_EQ('s', bytes[2]);
+  EXPECT_EQ('t', bytes[3]);
+}
+
+TEST_F(PropertyTest, UpdatedValues) {
+  WaitForGetAll();
+
+  // Update the value of the "Name" property, this value should not change.
+  properties_->name.Get(base::Bind(&PropertyTest::PropertyCallback,
+                                   base::Unretained(this),
+                                   "Name"));
+  WaitForCallback("Name");
+  WaitForUpdates(1);
+
+  EXPECT_EQ("TestService", properties_->name.value());
+
+  // Update the value of the "Version" property, this value should be changed.
+  properties_->version.Get(base::Bind(&PropertyTest::PropertyCallback,
+                                      base::Unretained(this),
+                                      "Version"));
+  WaitForCallback("Version");
+  WaitForUpdates(1);
+
+  EXPECT_EQ(20, properties_->version.value());
+
+  // Update the value of the "Methods" property, this value should not change
+  // and should not grow to contain duplicate entries.
+  properties_->methods.Get(base::Bind(&PropertyTest::PropertyCallback,
+                                      base::Unretained(this),
+                                      "Methods"));
+  WaitForCallback("Methods");
+  WaitForUpdates(1);
+
+  std::vector<std::string> methods = properties_->methods.value();
+  ASSERT_EQ(4U, methods.size());
+  EXPECT_EQ("Echo", methods[0]);
+  EXPECT_EQ("SlowEcho", methods[1]);
+  EXPECT_EQ("AsyncEcho", methods[2]);
+  EXPECT_EQ("BrokenMethod", methods[3]);
+
+  // Update the value of the "Objects" property, this value should not change
+  // and should not grow to contain duplicate entries.
+  properties_->objects.Get(base::Bind(&PropertyTest::PropertyCallback,
+                                      base::Unretained(this),
+                                      "Objects"));
+  WaitForCallback("Objects");
+  WaitForUpdates(1);
+
+  std::vector<ObjectPath> objects = properties_->objects.value();
+  ASSERT_EQ(1U, objects.size());
+  EXPECT_EQ(ObjectPath("/TestObjectPath"), objects[0]);
+
+  // Update the value of the "Bytes" property, this value should not change
+  // and should not grow to contain duplicate entries.
+  properties_->bytes.Get(base::Bind(&PropertyTest::PropertyCallback,
+                                    base::Unretained(this),
+                                   "Bytes"));
+  WaitForCallback("Bytes");
+  WaitForUpdates(1);
+
+  std::vector<uint8_t> bytes = properties_->bytes.value();
+  ASSERT_EQ(4U, bytes.size());
+  EXPECT_EQ('T', bytes[0]);
+  EXPECT_EQ('e', bytes[1]);
+  EXPECT_EQ('s', bytes[2]);
+  EXPECT_EQ('t', bytes[3]);
+}
+
+TEST_F(PropertyTest, Get) {
+  WaitForGetAll();
+
+  // Ask for the new Version property.
+  properties_->version.Get(base::Bind(&PropertyTest::PropertyCallback,
+                                      base::Unretained(this),
+                                      "Get"));
+  WaitForCallback("Get");
+
+  // Make sure we got a property update too.
+  WaitForUpdates(1);
+
+  EXPECT_EQ(20, properties_->version.value());
+}
+
+TEST_F(PropertyTest, Set) {
+  WaitForGetAll();
+
+  // Set a new name.
+  properties_->name.Set("NewService",
+                        base::Bind(&PropertyTest::PropertyCallback,
+                                   base::Unretained(this),
+                                   "Set"));
+  WaitForCallback("Set");
+
+  // TestService sends a property update.
+  WaitForUpdates(1);
+
+  EXPECT_EQ("NewService", properties_->name.value());
+}
+
+TEST_F(PropertyTest, Invalidate) {
+  WaitForGetAll();
+
+  EXPECT_TRUE(properties_->name.is_valid());
+
+  // Invalidate name.
+  MethodCall method_call("org.chromium.TestInterface", "PerformAction");
+  MessageWriter writer(&method_call);
+  writer.AppendString("InvalidateProperty");
+  writer.AppendObjectPath(ObjectPath("/org/chromium/TestService"));
+  object_proxy_->CallMethod(
+      &method_call, ObjectProxy::TIMEOUT_USE_DEFAULT,
+      base::Bind(&PropertyTest::MethodCallback, base::Unretained(this)));
+  WaitForMethodCallback();
+
+  // TestService sends a property update.
+  WaitForUpdates(1);
+
+  EXPECT_FALSE(properties_->name.is_valid());
+
+  // Set name to something valid.
+  properties_->name.Set("NewService",
+                        base::Bind(&PropertyTest::PropertyCallback,
+                                   base::Unretained(this), "Set"));
+  WaitForCallback("Set");
+
+  // TestService sends a property update.
+  WaitForUpdates(1);
+
+  EXPECT_TRUE(properties_->name.is_valid());
+}
+
+TEST(PropertyTestStatic, ReadWriteStringMap) {
+  std::unique_ptr<Response> message(Response::CreateEmpty());
+  MessageWriter writer(message.get());
+  MessageWriter variant_writer(nullptr);
+  MessageWriter variant_array_writer(nullptr);
+  MessageWriter struct_entry_writer(nullptr);
+
+  writer.OpenVariant("a{ss}", &variant_writer);
+  variant_writer.OpenArray("{ss}", &variant_array_writer);
+  const char* items[] = {"One", "Two", "Three", "Four"};
+  for (unsigned i = 0; i < arraysize(items); ++i) {
+    variant_array_writer.OpenDictEntry(&struct_entry_writer);
+    struct_entry_writer.AppendString(items[i]);
+    struct_entry_writer.AppendString(base::UintToString(i + 1));
+    variant_array_writer.CloseContainer(&struct_entry_writer);
+  }
+  variant_writer.CloseContainer(&variant_array_writer);
+  writer.CloseContainer(&variant_writer);
+
+  MessageReader reader(message.get());
+  Property<std::map<std::string, std::string>> string_map;
+  EXPECT_TRUE(string_map.PopValueFromReader(&reader));
+  ASSERT_EQ(4U, string_map.value().size());
+  EXPECT_EQ("1", string_map.value().at("One"));
+  EXPECT_EQ("2", string_map.value().at("Two"));
+  EXPECT_EQ("3", string_map.value().at("Three"));
+  EXPECT_EQ("4", string_map.value().at("Four"));
+}
+
+TEST(PropertyTestStatic, SerializeStringMap) {
+  std::map<std::string, std::string> test_map;
+  test_map["Hi"] = "There";
+  test_map["Map"] = "Test";
+  test_map["Random"] = "Text";
+
+  std::unique_ptr<Response> message(Response::CreateEmpty());
+  MessageWriter writer(message.get());
+
+  Property<std::map<std::string, std::string>> string_map;
+  string_map.ReplaceSetValueForTesting(test_map);
+  string_map.AppendSetValueToWriter(&writer);
+
+  MessageReader reader(message.get());
+  EXPECT_TRUE(string_map.PopValueFromReader(&reader));
+  EXPECT_EQ(test_map, string_map.value());
+}
+
+TEST(PropertyTestStatic, ReadWriteNetAddressArray) {
+  std::unique_ptr<Response> message(Response::CreateEmpty());
+  MessageWriter writer(message.get());
+  MessageWriter variant_writer(nullptr);
+  MessageWriter variant_array_writer(nullptr);
+  MessageWriter struct_entry_writer(nullptr);
+
+  writer.OpenVariant("a(ayq)", &variant_writer);
+  variant_writer.OpenArray("(ayq)", &variant_array_writer);
+  uint8_t ip_bytes[] = {0x54, 0x65, 0x73, 0x74, 0x30};
+  for (uint16_t i = 0; i < 5; ++i) {
+    variant_array_writer.OpenStruct(&struct_entry_writer);
+    ip_bytes[4] = 0x30 + i;
+    struct_entry_writer.AppendArrayOfBytes(ip_bytes, arraysize(ip_bytes));
+    struct_entry_writer.AppendUint16(i);
+    variant_array_writer.CloseContainer(&struct_entry_writer);
+  }
+  variant_writer.CloseContainer(&variant_array_writer);
+  writer.CloseContainer(&variant_writer);
+
+  MessageReader reader(message.get());
+  Property<std::vector<std::pair<std::vector<uint8_t>, uint16_t>>> ip_list;
+  EXPECT_TRUE(ip_list.PopValueFromReader(&reader));
+
+  ASSERT_EQ(5U, ip_list.value().size());
+  size_t item_index = 0;
+  for (auto& item : ip_list.value()) {
+    ASSERT_EQ(5U, item.first.size());
+    ip_bytes[4] = 0x30 + item_index;
+    EXPECT_EQ(0, memcmp(ip_bytes, item.first.data(), 5U));
+    EXPECT_EQ(item_index, item.second);
+    ++item_index;
+  }
+}
+
+TEST(PropertyTestStatic, SerializeNetAddressArray) {
+  std::vector<std::pair<std::vector<uint8_t>, uint16_t>> test_list;
+
+  uint8_t ip_bytes[] = {0x54, 0x65, 0x73, 0x74, 0x30};
+  for (uint16_t i = 0; i < 5; ++i) {
+    ip_bytes[4] = 0x30 + i;
+    std::vector<uint8_t> bytes(ip_bytes, ip_bytes + arraysize(ip_bytes));
+    test_list.push_back(make_pair(bytes, 16));
+  }
+
+  std::unique_ptr<Response> message(Response::CreateEmpty());
+  MessageWriter writer(message.get());
+
+  Property<std::vector<std::pair<std::vector<uint8_t>, uint16_t>>> ip_list;
+  ip_list.ReplaceSetValueForTesting(test_list);
+  ip_list.AppendSetValueToWriter(&writer);
+
+  MessageReader reader(message.get());
+  EXPECT_TRUE(ip_list.PopValueFromReader(&reader));
+  EXPECT_EQ(test_list, ip_list.value());
+}
+
+TEST(PropertyTestStatic, ReadWriteStringToByteVectorMap) {
+  std::unique_ptr<Response> message(Response::CreateEmpty());
+  MessageWriter writer(message.get());
+  MessageWriter variant_writer(nullptr);
+  MessageWriter dict_writer(nullptr);
+
+  writer.OpenVariant("a{sv}", &variant_writer);
+  variant_writer.OpenArray("{sv}", &dict_writer);
+
+  const char* keys[] = {"One", "Two", "Three", "Four"};
+  const std::vector<uint8_t> values[] = {{1}, {1, 2}, {1, 2, 3}, {1, 2, 3, 4}};
+  for (unsigned i = 0; i < arraysize(keys); ++i) {
+    MessageWriter entry_writer(nullptr);
+    dict_writer.OpenDictEntry(&entry_writer);
+
+    entry_writer.AppendString(keys[i]);
+
+    MessageWriter value_varient_writer(nullptr);
+    entry_writer.OpenVariant("ay", &value_varient_writer);
+    value_varient_writer.AppendArrayOfBytes(values[i].data(), values[i].size());
+    entry_writer.CloseContainer(&value_varient_writer);
+
+    dict_writer.CloseContainer(&entry_writer);
+  }
+
+  variant_writer.CloseContainer(&dict_writer);
+  writer.CloseContainer(&variant_writer);
+
+  MessageReader reader(message.get());
+  Property<std::map<std::string, std::vector<uint8_t>>> test_property;
+  EXPECT_TRUE(test_property.PopValueFromReader(&reader));
+
+  ASSERT_EQ(arraysize(keys), test_property.value().size());
+  for (unsigned i = 0; i < arraysize(keys); ++i)
+    EXPECT_EQ(values[i], test_property.value().at(keys[i]));
+}
+
+TEST(PropertyTestStatic, SerializeStringToByteVectorMap) {
+  std::map<std::string, std::vector<uint8_t>> test_map;
+  test_map["Hi"] = {1, 2, 3};
+  test_map["Map"] = {0xab, 0xcd};
+  test_map["Random"] = {0x0};
+
+  std::unique_ptr<Response> message(Response::CreateEmpty());
+  MessageWriter writer(message.get());
+
+  Property<std::map<std::string, std::vector<uint8_t>>> test_property;
+  test_property.ReplaceSetValueForTesting(test_map);
+  test_property.AppendSetValueToWriter(&writer);
+
+  MessageReader reader(message.get());
+  EXPECT_TRUE(test_property.PopValueFromReader(&reader));
+  EXPECT_EQ(test_map, test_property.value());
+}
+
+TEST(PropertyTestStatic, ReadWriteUInt16ToByteVectorMap) {
+  std::unique_ptr<Response> message(Response::CreateEmpty());
+  MessageWriter writer(message.get());
+  MessageWriter variant_writer(nullptr);
+  MessageWriter dict_writer(nullptr);
+
+  writer.OpenVariant("a{qv}", &variant_writer);
+  variant_writer.OpenArray("{qv}", &dict_writer);
+
+  const uint16_t keys[] = {11, 12, 13, 14};
+  const std::vector<uint8_t> values[] = {{1}, {1, 2}, {1, 2, 3}, {1, 2, 3, 4}};
+  for (unsigned i = 0; i < arraysize(keys); ++i) {
+    MessageWriter entry_writer(nullptr);
+    dict_writer.OpenDictEntry(&entry_writer);
+
+    entry_writer.AppendUint16(keys[i]);
+
+    MessageWriter value_varient_writer(nullptr);
+    entry_writer.OpenVariant("ay", &value_varient_writer);
+    value_varient_writer.AppendArrayOfBytes(values[i].data(), values[i].size());
+    entry_writer.CloseContainer(&value_varient_writer);
+
+    dict_writer.CloseContainer(&entry_writer);
+  }
+
+  variant_writer.CloseContainer(&dict_writer);
+  writer.CloseContainer(&variant_writer);
+
+  MessageReader reader(message.get());
+  Property<std::map<uint16_t, std::vector<uint8_t>>> test_property;
+  EXPECT_TRUE(test_property.PopValueFromReader(&reader));
+
+  ASSERT_EQ(arraysize(keys), test_property.value().size());
+  for (unsigned i = 0; i < arraysize(keys); ++i)
+    EXPECT_EQ(values[i], test_property.value().at(keys[i]));
+}
+
+TEST(PropertyTestStatic, SerializeUInt16ToByteVectorMap) {
+  std::map<uint16_t, std::vector<uint8_t>> test_map;
+  test_map[11] = {1, 2, 3};
+  test_map[12] = {0xab, 0xcd};
+  test_map[13] = {0x0};
+
+  std::unique_ptr<Response> message(Response::CreateEmpty());
+  MessageWriter writer(message.get());
+
+  Property<std::map<uint16_t, std::vector<uint8_t>>> test_property;
+  test_property.ReplaceSetValueForTesting(test_map);
+  test_property.AppendSetValueToWriter(&writer);
+
+  MessageReader reader(message.get());
+  EXPECT_TRUE(test_property.PopValueFromReader(&reader));
+  EXPECT_EQ(test_map, test_property.value());
+}
+
+}  // namespace dbus
diff --git a/dbus/signal_sender_verification_unittest.cc b/dbus/signal_sender_verification_unittest.cc
new file mode 100644
index 0000000..3cc22f9
--- /dev/null
+++ b/dbus/signal_sender_verification_unittest.cc
@@ -0,0 +1,394 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/message_loop/message_loop.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/metrics/histogram_samples.h"
+#include "base/run_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "base/test/test_timeouts.h"
+#include "base/threading/platform_thread.h"
+#include "base/threading/thread_restrictions.h"
+#include "dbus/bus.h"
+#include "dbus/message.h"
+#include "dbus/object_proxy.h"
+#include "dbus/test_service.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace dbus {
+
+// The test for sender verification in ObjectProxy.
+class SignalSenderVerificationTest : public testing::Test {
+ public:
+  SignalSenderVerificationTest()
+      : on_name_owner_changed_called_(false),
+        on_ownership_called_(false) {
+  }
+
+  void SetUp() override {
+    // Make the main thread not to allow IO.
+    base::ThreadRestrictions::SetIOAllowed(false);
+
+    // Start the D-Bus thread.
+    dbus_thread_.reset(new base::Thread("D-Bus Thread"));
+    base::Thread::Options thread_options;
+    thread_options.message_loop_type = base::MessageLoop::TYPE_IO;
+    ASSERT_TRUE(dbus_thread_->StartWithOptions(thread_options));
+
+    // Create the test service, using the D-Bus thread.
+    TestService::Options options;
+    options.dbus_task_runner = dbus_thread_->task_runner();
+    test_service_.reset(new TestService(options));
+
+    // Create the client, using the D-Bus thread.
+    Bus::Options bus_options;
+    bus_options.bus_type = Bus::SESSION;
+    bus_options.connection_type = Bus::PRIVATE;
+    bus_options.dbus_task_runner = dbus_thread_->task_runner();
+    bus_ = new Bus(bus_options);
+    object_proxy_ = bus_->GetObjectProxy(
+        test_service_->service_name(),
+        ObjectPath("/org/chromium/TestObject"));
+    ASSERT_TRUE(bus_->HasDBusThread());
+
+    object_proxy_->SetNameOwnerChangedCallback(
+        base::Bind(&SignalSenderVerificationTest::OnNameOwnerChanged,
+                   base::Unretained(this),
+                   &on_name_owner_changed_called_));
+
+    // Connect to the "Test" signal of "org.chromium.TestInterface" from
+    // the remote object.
+    object_proxy_->ConnectToSignal(
+        "org.chromium.TestInterface",
+        "Test",
+        base::Bind(&SignalSenderVerificationTest::OnTestSignal,
+                   base::Unretained(this)),
+        base::Bind(&SignalSenderVerificationTest::OnConnected,
+                   base::Unretained(this)));
+    // Wait until the object proxy is connected to the signal.
+    run_loop_.reset(new base::RunLoop);
+    run_loop_->Run();
+
+    // Start the test service.
+    ASSERT_TRUE(test_service_->StartService());
+    ASSERT_TRUE(test_service_->WaitUntilServiceIsStarted());
+    ASSERT_TRUE(test_service_->HasDBusThread());
+    ASSERT_TRUE(test_service_->has_ownership());
+
+    // Same setup for the second TestService. This service should not have the
+    // ownership of the name at this point.
+    options.service_name = test_service_->service_name();
+    test_service2_.reset(new TestService(options));
+    ASSERT_TRUE(test_service2_->StartService());
+    ASSERT_TRUE(test_service2_->WaitUntilServiceIsStarted());
+    ASSERT_TRUE(test_service2_->HasDBusThread());
+    ASSERT_FALSE(test_service2_->has_ownership());
+
+    // The name should be owned and known at this point.
+    if (!on_name_owner_changed_called_) {
+      run_loop_.reset(new base::RunLoop);
+      run_loop_->Run();
+    }
+    ASSERT_FALSE(latest_name_owner_.empty());
+  }
+
+  void TearDown() override {
+    bus_->ShutdownOnDBusThreadAndBlock();
+
+    // Shut down the service.
+    test_service_->ShutdownAndBlock();
+    test_service2_->ShutdownAndBlock();
+
+    // Reset to the default.
+    base::ThreadRestrictions::SetIOAllowed(true);
+
+    // Stopping a thread is considered an IO operation, so do this after
+    // allowing IO.
+    test_service_->Stop();
+    test_service2_->Stop();
+  }
+
+  void OnOwnership(bool expected, bool success) {
+    ASSERT_EQ(expected, success);
+    // PostTask to quit the MessageLoop as this is called from D-Bus thread.
+    message_loop_.task_runner()->PostTask(
+        FROM_HERE,
+        base::Bind(&SignalSenderVerificationTest::OnOwnershipInternal,
+                   base::Unretained(this)));
+  }
+
+  void OnOwnershipInternal() {
+    on_ownership_called_ = true;
+    run_loop_->Quit();
+  }
+
+  void OnNameOwnerChanged(bool* called_flag,
+                          const std::string& old_owner,
+                          const std::string& new_owner) {
+    latest_name_owner_ = new_owner;
+    *called_flag = true;
+    run_loop_->Quit();
+  }
+
+  // Called when the "Test" signal is received, in the main thread.
+  // Copy the string payload to |test_signal_string_|.
+  void OnTestSignal(Signal* signal) {
+    MessageReader reader(signal);
+    ASSERT_TRUE(reader.PopString(&test_signal_string_));
+    run_loop_->Quit();
+  }
+
+  // Called when connected to the signal.
+  void OnConnected(const std::string& interface_name,
+                   const std::string& signal_name,
+                   bool success) {
+    ASSERT_TRUE(success);
+    run_loop_->Quit();
+  }
+
+ protected:
+  // Wait for the hey signal to be received.
+  void WaitForTestSignal() {
+    // OnTestSignal() will quit the message loop.
+    run_loop_.reset(new base::RunLoop);
+    run_loop_->Run();
+  }
+
+  // Stopping a thread is considered an IO operation, so we need to fiddle with
+  // thread restrictions before and after calling Stop() on a TestService.
+  void SafeServiceStop(TestService* test_service) {
+    base::ThreadRestrictions::SetIOAllowed(true);
+    test_service->Stop();
+    base::ThreadRestrictions::SetIOAllowed(false);
+  }
+
+  base::MessageLoop message_loop_;
+  std::unique_ptr<base::RunLoop> run_loop_;
+  std::unique_ptr<base::Thread> dbus_thread_;
+  scoped_refptr<Bus> bus_;
+  ObjectProxy* object_proxy_;
+  std::unique_ptr<TestService> test_service_;
+  std::unique_ptr<TestService> test_service2_;
+  // Text message from "Test" signal.
+  std::string test_signal_string_;
+
+  // The known latest name owner of TestService. Updated in OnNameOwnerChanged.
+  std::string latest_name_owner_;
+
+  // Boolean flags to record callback calls.
+  bool on_name_owner_changed_called_;
+  bool on_ownership_called_;
+};
+
+TEST_F(SignalSenderVerificationTest, TestSignalAccepted) {
+  const char kMessage[] = "hello, world";
+  // Send the test signal from the exported object.
+  test_service_->SendTestSignal(kMessage);
+  // Receive the signal with the object proxy. The signal is handled in
+  // SignalSenderVerificationTest::OnTestSignal() in the main thread.
+  WaitForTestSignal();
+  ASSERT_EQ(kMessage, test_signal_string_);
+}
+
+TEST_F(SignalSenderVerificationTest, TestSignalRejected) {
+  const char kNewMessage[] = "hello, new world";
+  test_service2_->SendTestSignal(kNewMessage);
+
+  // This test tests that our callback is NOT called by the ObjectProxy.
+  // Sleep to have message delivered to the client via the D-Bus service.
+  base::PlatformThread::Sleep(TestTimeouts::tiny_timeout());
+
+  ASSERT_EQ("", test_signal_string_);
+}
+
+// Flaky. https://crbug.com/785555
+TEST_F(SignalSenderVerificationTest, DISABLED_TestOwnerChanged) {
+  const char kMessage[] = "hello, world";
+
+  // Send the test signal from the exported object.
+  test_service_->SendTestSignal(kMessage);
+  // Receive the signal with the object proxy. The signal is handled in
+  // SignalSenderVerificationTest::OnTestSignal() in the main thread.
+  WaitForTestSignal();
+  ASSERT_EQ(kMessage, test_signal_string_);
+
+  // Release and acquire the name ownership.
+  // latest_name_owner_ should be non empty as |test_service_| owns the name.
+  ASSERT_FALSE(latest_name_owner_.empty());
+  test_service_->ShutdownAndBlock();
+  // OnNameOwnerChanged will PostTask to quit the message loop.
+  run_loop_.reset(new base::RunLoop);
+  run_loop_->Run();
+  // latest_name_owner_ should be empty as the owner is gone.
+  ASSERT_TRUE(latest_name_owner_.empty());
+
+  // Reset the flag as NameOwnerChanged is already received in setup.
+  on_name_owner_changed_called_ = false;
+  on_ownership_called_ = false;
+  test_service2_->RequestOwnership(
+      base::Bind(&SignalSenderVerificationTest::OnOwnership,
+                 base::Unretained(this), true));
+  // Both of OnNameOwnerChanged() and OnOwnership() should quit the MessageLoop,
+  // but there's no expected order of those 2 event.
+  run_loop_.reset(new base::RunLoop);
+  run_loop_->Run();
+  if (!on_name_owner_changed_called_ || !on_ownership_called_) {
+    run_loop_.reset(new base::RunLoop);
+    run_loop_->Run();
+  }
+  ASSERT_TRUE(on_name_owner_changed_called_);
+  ASSERT_TRUE(on_ownership_called_);
+
+  // latest_name_owner_ becomes non empty as the new owner appears.
+  ASSERT_FALSE(latest_name_owner_.empty());
+
+  // Now the second service owns the name.
+  const char kNewMessage[] = "hello, new world";
+
+  test_service2_->SendTestSignal(kNewMessage);
+  WaitForTestSignal();
+  ASSERT_EQ(kNewMessage, test_signal_string_);
+}
+
+// Flaky. https://crbug.com/785555
+TEST_F(SignalSenderVerificationTest, DISABLED_TestOwnerStealing) {
+  // Release and acquire the name ownership.
+  // latest_name_owner_ should be non empty as |test_service_| owns the name.
+  ASSERT_FALSE(latest_name_owner_.empty());
+  test_service_->ShutdownAndBlock();
+  // OnNameOwnerChanged will PostTask to quit the message loop.
+  run_loop_.reset(new base::RunLoop);
+  run_loop_->Run();
+  // latest_name_owner_ should be empty as the owner is gone.
+  ASSERT_TRUE(latest_name_owner_.empty());
+  // Reset the flag as NameOwnerChanged is already received in setup.
+  on_name_owner_changed_called_ = false;
+
+  // Start a test service that allows theft, using the D-Bus thread.
+  TestService::Options options;
+  options.dbus_task_runner = dbus_thread_->task_runner();
+  options.request_ownership_options = Bus::REQUIRE_PRIMARY_ALLOW_REPLACEMENT;
+  options.service_name = test_service_->service_name();
+  TestService stealable_test_service(options);
+  ASSERT_TRUE(stealable_test_service.StartService());
+  ASSERT_TRUE(stealable_test_service.WaitUntilServiceIsStarted());
+  ASSERT_TRUE(stealable_test_service.HasDBusThread());
+  ASSERT_TRUE(stealable_test_service.has_ownership());
+
+  // OnNameOwnerChanged will PostTask to quit the message loop.
+  run_loop_.reset(new base::RunLoop);
+  run_loop_->Run();
+
+  // Send a signal to check that the service is correctly owned.
+  const char kMessage[] = "hello, world";
+
+  // Send the test signal from the exported object.
+  stealable_test_service.SendTestSignal(kMessage);
+  // Receive the signal with the object proxy. The signal is handled in
+  // SignalSenderVerificationTest::OnTestSignal() in the main thread.
+  WaitForTestSignal();
+  ASSERT_EQ(kMessage, test_signal_string_);
+
+  // Reset the flag as NameOwnerChanged was called above.
+  on_name_owner_changed_called_ = false;
+  test_service2_->RequestOwnership(
+      base::Bind(&SignalSenderVerificationTest::OnOwnership,
+                 base::Unretained(this), true));
+  // Both of OnNameOwnerChanged() and OnOwnership() should quit the MessageLoop,
+  // but there's no expected order of those 2 event.
+  run_loop_.reset(new base::RunLoop);
+  run_loop_->Run();
+  if (!on_name_owner_changed_called_ || !on_ownership_called_) {
+    run_loop_.reset(new base::RunLoop);
+    run_loop_->Run();
+  }
+  ASSERT_TRUE(on_name_owner_changed_called_);
+  ASSERT_TRUE(on_ownership_called_);
+
+  // Now the second service owns the name.
+  const char kNewMessage[] = "hello, new world";
+
+  test_service2_->SendTestSignal(kNewMessage);
+  WaitForTestSignal();
+  ASSERT_EQ(kNewMessage, test_signal_string_);
+
+  SafeServiceStop(&stealable_test_service);
+}
+
+// Fails on Linux ChromiumOS Tests
+TEST_F(SignalSenderVerificationTest, DISABLED_TestMultipleObjects) {
+  const char kMessage[] = "hello, world";
+
+  ObjectProxy* object_proxy2 = bus_->GetObjectProxy(
+      test_service_->service_name(),
+      ObjectPath("/org/chromium/DifferentObject"));
+
+  bool second_name_owner_changed_called = false;
+  object_proxy2->SetNameOwnerChangedCallback(
+      base::Bind(&SignalSenderVerificationTest::OnNameOwnerChanged,
+                 base::Unretained(this),
+                 &second_name_owner_changed_called));
+
+  // Connect to a signal on the additional remote object to trigger the
+  // name owner matching.
+  object_proxy2->ConnectToSignal(
+      "org.chromium.DifferentTestInterface",
+      "Test",
+      base::Bind(&SignalSenderVerificationTest::OnTestSignal,
+                 base::Unretained(this)),
+      base::Bind(&SignalSenderVerificationTest::OnConnected,
+                 base::Unretained(this)));
+  // Wait until the object proxy is connected to the signal.
+  run_loop_.reset(new base::RunLoop);
+  run_loop_->Run();
+
+  // Send the test signal from the exported object.
+  test_service_->SendTestSignal(kMessage);
+  // Receive the signal with the object proxy. The signal is handled in
+  // SignalSenderVerificationTest::OnTestSignal() in the main thread.
+  WaitForTestSignal();
+  ASSERT_EQ(kMessage, test_signal_string_);
+
+  // Release and acquire the name ownership.
+  // latest_name_owner_ should be non empty as |test_service_| owns the name.
+  ASSERT_FALSE(latest_name_owner_.empty());
+  test_service_->ShutdownAndBlock();
+  // OnNameOwnerChanged will PostTask to quit the message loop.
+  run_loop_.reset(new base::RunLoop);
+  run_loop_->Run();
+  // latest_name_owner_ should be empty as the owner is gone.
+  ASSERT_TRUE(latest_name_owner_.empty());
+
+  // Reset the flag as NameOwnerChanged is already received in setup.
+  on_name_owner_changed_called_ = false;
+  second_name_owner_changed_called = false;
+  test_service2_->RequestOwnership(
+      base::Bind(&SignalSenderVerificationTest::OnOwnership,
+                 base::Unretained(this), true));
+  // Both of OnNameOwnerChanged() and OnOwnership() should quit the MessageLoop,
+  // but there's no expected order of those 2 event.
+  while (!on_name_owner_changed_called_ || !second_name_owner_changed_called ||
+         !on_ownership_called_) {
+    run_loop_.reset(new base::RunLoop);
+    run_loop_->Run();
+  }
+  ASSERT_TRUE(on_name_owner_changed_called_);
+  ASSERT_TRUE(second_name_owner_changed_called);
+  ASSERT_TRUE(on_ownership_called_);
+
+  // latest_name_owner_ becomes non empty as the new owner appears.
+  ASSERT_FALSE(latest_name_owner_.empty());
+
+  // Now the second service owns the name.
+  const char kNewMessage[] = "hello, new world";
+
+  test_service2_->SendTestSignal(kNewMessage);
+  WaitForTestSignal();
+  ASSERT_EQ(kNewMessage, test_signal_string_);
+}
+
+}  // namespace dbus
diff --git a/dbus/string_util_unittest.cc b/dbus/string_util_unittest.cc
new file mode 100644
index 0000000..3d0ff51
--- /dev/null
+++ b/dbus/string_util_unittest.cc
@@ -0,0 +1,31 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "dbus/string_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace dbus {
+
+TEST(StringUtilTest, IsValidObjectPath) {
+  EXPECT_TRUE(IsValidObjectPath("/"));
+  EXPECT_TRUE(IsValidObjectPath("/foo/bar"));
+  EXPECT_TRUE(IsValidObjectPath("/hoge_fuga/piyo123"));
+  // Empty string.
+  EXPECT_FALSE(IsValidObjectPath(std::string()));
+  // Empty element.
+  EXPECT_FALSE(IsValidObjectPath("//"));
+  EXPECT_FALSE(IsValidObjectPath("/foo//bar"));
+  EXPECT_FALSE(IsValidObjectPath("/foo///bar"));
+  // Trailing '/'.
+  EXPECT_FALSE(IsValidObjectPath("/foo/"));
+  EXPECT_FALSE(IsValidObjectPath("/foo/bar/"));
+  // Not beginning with '/'.
+  EXPECT_FALSE(IsValidObjectPath("foo/bar"));
+  // Invalid characters.
+  EXPECT_FALSE(IsValidObjectPath("/foo.bar"));
+  EXPECT_FALSE(IsValidObjectPath("/foo/*"));
+  EXPECT_FALSE(IsValidObjectPath("/foo/bar(1)"));
+}
+
+}  // namespace dbus
diff --git a/dbus/util_unittest.cc b/dbus/util_unittest.cc
new file mode 100644
index 0000000..4bf6efa
--- /dev/null
+++ b/dbus/util_unittest.cc
@@ -0,0 +1,16 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "dbus/util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace dbus {
+
+TEST(UtilTest, GetAbsoluteMemberName) {
+  EXPECT_EQ("InterfaceName.MemberName",
+            GetAbsoluteMemberName("InterfaceName", "MemberName"));
+  EXPECT_EQ(".", GetAbsoluteMemberName("", ""));
+}
+
+}  // namespace dbus
diff --git a/dbus/values_util_unittest.cc b/dbus/values_util_unittest.cc
new file mode 100644
index 0000000..5a0b81e
--- /dev/null
+++ b/dbus/values_util_unittest.cc
@@ -0,0 +1,687 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "dbus/values_util.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <cmath>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "base/json/json_writer.h"
+#include "base/macros.h"
+#include "base/values.h"
+#include "dbus/message.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace dbus {
+
+TEST(ValuesUtilTest, PopBasicTypes) {
+  std::unique_ptr<Response> response(Response::CreateEmpty());
+  // Append basic type values.
+  MessageWriter writer(response.get());
+  const uint8_t kByteValue = 42;
+  writer.AppendByte(kByteValue);
+  const bool kBoolValue = true;
+  writer.AppendBool(kBoolValue);
+  const int16_t kInt16Value = -43;
+  writer.AppendInt16(kInt16Value);
+  const uint16_t kUint16Value = 44;
+  writer.AppendUint16(kUint16Value);
+  const int32_t kInt32Value = -45;
+  writer.AppendInt32(kInt32Value);
+  const uint32_t kUint32Value = 46;
+  writer.AppendUint32(kUint32Value);
+  const int64_t kInt64Value = -47;
+  writer.AppendInt64(kInt64Value);
+  const uint64_t kUint64Value = 48;
+  writer.AppendUint64(kUint64Value);
+  const double kDoubleValue = 4.9;
+  writer.AppendDouble(kDoubleValue);
+  const std::string kStringValue = "fifty";
+  writer.AppendString(kStringValue);
+  const std::string kEmptyStringValue;
+  writer.AppendString(kEmptyStringValue);
+  const ObjectPath kObjectPathValue("/ObjectPath");
+  writer.AppendObjectPath(kObjectPathValue);
+
+  MessageReader reader(response.get());
+  std::unique_ptr<base::Value> value;
+  std::unique_ptr<base::Value> expected_value;
+  // Pop a byte.
+  value = PopDataAsValue(&reader);
+  ASSERT_NE(nullptr, value);
+  expected_value.reset(new base::Value(kByteValue));
+  EXPECT_TRUE(value->Equals(expected_value.get()));
+  // Pop a bool.
+  value = PopDataAsValue(&reader);
+  ASSERT_NE(nullptr, value);
+  expected_value.reset(new base::Value(kBoolValue));
+  EXPECT_TRUE(value->Equals(expected_value.get()));
+  // Pop an int16_t.
+  value = PopDataAsValue(&reader);
+  ASSERT_NE(nullptr, value);
+  expected_value.reset(new base::Value(kInt16Value));
+  EXPECT_TRUE(value->Equals(expected_value.get()));
+  // Pop a uint16_t.
+  value = PopDataAsValue(&reader);
+  ASSERT_NE(nullptr, value);
+  expected_value.reset(new base::Value(kUint16Value));
+  EXPECT_TRUE(value->Equals(expected_value.get()));
+  // Pop an int32_t.
+  value = PopDataAsValue(&reader);
+  ASSERT_NE(nullptr, value);
+  expected_value.reset(new base::Value(kInt32Value));
+  EXPECT_TRUE(value->Equals(expected_value.get()));
+  // Pop a uint32_t.
+  value = PopDataAsValue(&reader);
+  ASSERT_NE(nullptr, value);
+  expected_value.reset(new base::Value(static_cast<double>(kUint32Value)));
+  EXPECT_TRUE(value->Equals(expected_value.get()));
+  // Pop an int64_t.
+  value = PopDataAsValue(&reader);
+  ASSERT_NE(nullptr, value);
+  expected_value.reset(new base::Value(static_cast<double>(kInt64Value)));
+  EXPECT_TRUE(value->Equals(expected_value.get()));
+  // Pop a uint64_t.
+  value = PopDataAsValue(&reader);
+  ASSERT_NE(nullptr, value);
+  expected_value.reset(new base::Value(static_cast<double>(kUint64Value)));
+  EXPECT_TRUE(value->Equals(expected_value.get()));
+  // Pop a double.
+  value = PopDataAsValue(&reader);
+  ASSERT_NE(nullptr, value);
+  expected_value.reset(new base::Value(kDoubleValue));
+  EXPECT_TRUE(value->Equals(expected_value.get()));
+  // Pop a string.
+  value = PopDataAsValue(&reader);
+  ASSERT_NE(nullptr, value);
+  expected_value.reset(new base::Value(kStringValue));
+  EXPECT_TRUE(value->Equals(expected_value.get()));
+  // Pop an empty string.
+  value = PopDataAsValue(&reader);
+  ASSERT_NE(nullptr, value);
+  expected_value.reset(new base::Value(kEmptyStringValue));
+  EXPECT_TRUE(value->Equals(expected_value.get()));
+  // Pop an object path.
+  value = PopDataAsValue(&reader);
+  ASSERT_NE(nullptr, value);
+  expected_value.reset(new base::Value(kObjectPathValue.value()));
+  EXPECT_TRUE(value->Equals(expected_value.get()));
+}
+
+TEST(ValuesUtilTest, PopVariant) {
+  std::unique_ptr<Response> response(Response::CreateEmpty());
+  // Append variant values.
+  MessageWriter writer(response.get());
+  const bool kBoolValue = true;
+  writer.AppendVariantOfBool(kBoolValue);
+  const int32_t kInt32Value = -45;
+  writer.AppendVariantOfInt32(kInt32Value);
+  const double kDoubleValue = 4.9;
+  writer.AppendVariantOfDouble(kDoubleValue);
+  const std::string kStringValue = "fifty";
+  writer.AppendVariantOfString(kStringValue);
+
+  MessageReader reader(response.get());
+  std::unique_ptr<base::Value> value;
+  std::unique_ptr<base::Value> expected_value;
+  // Pop a bool.
+  value = PopDataAsValue(&reader);
+  ASSERT_NE(nullptr, value);
+  expected_value.reset(new base::Value(kBoolValue));
+  EXPECT_TRUE(value->Equals(expected_value.get()));
+  // Pop an int32_t.
+  value = PopDataAsValue(&reader);
+  ASSERT_NE(nullptr, value);
+  expected_value.reset(new base::Value(kInt32Value));
+  EXPECT_TRUE(value->Equals(expected_value.get()));
+  // Pop a double.
+  value = PopDataAsValue(&reader);
+  ASSERT_NE(nullptr, value);
+  expected_value.reset(new base::Value(kDoubleValue));
+  EXPECT_TRUE(value->Equals(expected_value.get()));
+  // Pop a string.
+  value = PopDataAsValue(&reader);
+  ASSERT_NE(nullptr, value);
+  expected_value.reset(new base::Value(kStringValue));
+  EXPECT_TRUE(value->Equals(expected_value.get()));
+}
+
+// Pop extremely large integers which cannot be precisely represented in
+// double.
+TEST(ValuesUtilTest, PopExtremelyLargeIntegers) {
+  std::unique_ptr<Response> response(Response::CreateEmpty());
+  // Append large integers.
+  MessageWriter writer(response.get());
+  const int64_t kInt64Value = -123456789012345689LL;
+  writer.AppendInt64(kInt64Value);
+  const uint64_t kUint64Value = 9876543210987654321ULL;
+  writer.AppendUint64(kUint64Value);
+
+  MessageReader reader(response.get());
+  std::unique_ptr<base::Value> value;
+  std::unique_ptr<base::Value> expected_value;
+  double double_value = 0;
+  // Pop an int64_t.
+  value = PopDataAsValue(&reader);
+  ASSERT_NE(nullptr, value);
+  expected_value.reset(new base::Value(static_cast<double>(kInt64Value)));
+  EXPECT_TRUE(value->Equals(expected_value.get()));
+  ASSERT_TRUE(value->GetAsDouble(&double_value));
+  EXPECT_NE(kInt64Value, static_cast<int64_t>(double_value));
+  // Pop a uint64_t.
+  value = PopDataAsValue(&reader);
+  ASSERT_NE(nullptr, value);
+  expected_value.reset(new base::Value(static_cast<double>(kUint64Value)));
+  EXPECT_TRUE(value->Equals(expected_value.get()));
+  ASSERT_TRUE(value->GetAsDouble(&double_value));
+  EXPECT_NE(kUint64Value, static_cast<uint64_t>(double_value));
+}
+
+TEST(ValuesUtilTest, PopIntArray) {
+  std::unique_ptr<Response> response(Response::CreateEmpty());
+  // Append an int32_t array.
+  MessageWriter writer(response.get());
+  MessageWriter sub_writer(nullptr);
+  std::vector<int32_t> data;
+  data.push_back(0);
+  data.push_back(1);
+  data.push_back(2);
+  writer.OpenArray("i", &sub_writer);
+  for (size_t i = 0; i != data.size(); ++i)
+    sub_writer.AppendInt32(data[i]);
+  writer.CloseContainer(&sub_writer);
+
+  // Create the expected value.
+  std::unique_ptr<base::ListValue> list_value(new base::ListValue);
+  for (size_t i = 0; i != data.size(); ++i)
+    list_value->AppendInteger(data[i]);
+
+  // Pop an int32_t array.
+  MessageReader reader(response.get());
+  std::unique_ptr<base::Value> value(PopDataAsValue(&reader));
+  ASSERT_NE(nullptr, value);
+  EXPECT_TRUE(value->Equals(list_value.get()));
+}
+
+TEST(ValuesUtilTest, PopStringArray) {
+  std::unique_ptr<Response> response(Response::CreateEmpty());
+  // Append a string array.
+  MessageWriter writer(response.get());
+  MessageWriter sub_writer(nullptr);
+  std::vector<std::string> data;
+  data.push_back("Dreamlifter");
+  data.push_back("Beluga");
+  data.push_back("Mriya");
+  writer.AppendArrayOfStrings(data);
+
+  // Create the expected value.
+  std::unique_ptr<base::ListValue> list_value(new base::ListValue);
+  for (size_t i = 0; i != data.size(); ++i)
+    list_value->AppendString(data[i]);
+
+  // Pop a string array.
+  MessageReader reader(response.get());
+  std::unique_ptr<base::Value> value(PopDataAsValue(&reader));
+  ASSERT_NE(nullptr, value);
+  EXPECT_TRUE(value->Equals(list_value.get()));
+}
+
+TEST(ValuesUtilTest, PopStruct) {
+  std::unique_ptr<Response> response(Response::CreateEmpty());
+  // Append a struct.
+  MessageWriter writer(response.get());
+  MessageWriter sub_writer(nullptr);
+  writer.OpenStruct(&sub_writer);
+  const bool kBoolValue = true;
+  sub_writer.AppendBool(kBoolValue);
+  const int32_t kInt32Value = -123;
+  sub_writer.AppendInt32(kInt32Value);
+  const double kDoubleValue = 1.23;
+  sub_writer.AppendDouble(kDoubleValue);
+  const std::string kStringValue = "one two three";
+  sub_writer.AppendString(kStringValue);
+  writer.CloseContainer(&sub_writer);
+
+  // Create the expected value.
+  base::ListValue list_value;
+  list_value.AppendBoolean(kBoolValue);
+  list_value.AppendInteger(kInt32Value);
+  list_value.AppendDouble(kDoubleValue);
+  list_value.AppendString(kStringValue);
+
+  // Pop a struct.
+  MessageReader reader(response.get());
+  std::unique_ptr<base::Value> value(PopDataAsValue(&reader));
+  ASSERT_NE(nullptr, value);
+  EXPECT_TRUE(value->Equals(&list_value));
+}
+
+TEST(ValuesUtilTest, PopStringToVariantDictionary) {
+  std::unique_ptr<Response> response(Response::CreateEmpty());
+  // Append a dictionary.
+  MessageWriter writer(response.get());
+  MessageWriter sub_writer(nullptr);
+  MessageWriter entry_writer(nullptr);
+  writer.OpenArray("{sv}", &sub_writer);
+  sub_writer.OpenDictEntry(&entry_writer);
+  const std::string kKey1 = "one";
+  entry_writer.AppendString(kKey1);
+  const bool kBoolValue = true;
+  entry_writer.AppendVariantOfBool(kBoolValue);
+  sub_writer.CloseContainer(&entry_writer);
+  sub_writer.OpenDictEntry(&entry_writer);
+  const std::string kKey2 = "two";
+  entry_writer.AppendString(kKey2);
+  const int32_t kInt32Value = -45;
+  entry_writer.AppendVariantOfInt32(kInt32Value);
+  sub_writer.CloseContainer(&entry_writer);
+  sub_writer.OpenDictEntry(&entry_writer);
+  const std::string kKey3 = "three";
+  entry_writer.AppendString(kKey3);
+  const double kDoubleValue = 4.9;
+  entry_writer.AppendVariantOfDouble(kDoubleValue);
+  sub_writer.CloseContainer(&entry_writer);
+  sub_writer.OpenDictEntry(&entry_writer);
+  const std::string kKey4 = "four";
+  entry_writer.AppendString(kKey4);
+  const std::string kStringValue = "fifty";
+  entry_writer.AppendVariantOfString(kStringValue);
+  sub_writer.CloseContainer(&entry_writer);
+  writer.CloseContainer(&sub_writer);
+
+  // Create the expected value.
+  base::DictionaryValue dictionary_value;
+  dictionary_value.SetBoolean(kKey1, kBoolValue);
+  dictionary_value.SetInteger(kKey2, kInt32Value);
+  dictionary_value.SetDouble(kKey3, kDoubleValue);
+  dictionary_value.SetString(kKey4, kStringValue);
+
+  // Pop a dictinoary.
+  MessageReader reader(response.get());
+  std::unique_ptr<base::Value> value(PopDataAsValue(&reader));
+  ASSERT_NE(nullptr, value);
+  EXPECT_TRUE(value->Equals(&dictionary_value));
+}
+
+TEST(ValuesUtilTest, PopDictionaryWithDottedStringKey) {
+  std::unique_ptr<Response> response(Response::CreateEmpty());
+  // Append a dictionary.
+  MessageWriter writer(response.get());
+  MessageWriter sub_writer(nullptr);
+  MessageWriter entry_writer(nullptr);
+  writer.OpenArray("{sv}", &sub_writer);
+  sub_writer.OpenDictEntry(&entry_writer);
+  const std::string kKey1 = "www.example.com";  // String including dots.
+  entry_writer.AppendString(kKey1);
+  const bool kBoolValue = true;
+  entry_writer.AppendVariantOfBool(kBoolValue);
+  sub_writer.CloseContainer(&entry_writer);
+  sub_writer.OpenDictEntry(&entry_writer);
+  const std::string kKey2 = ".example";  // String starting with a dot.
+  entry_writer.AppendString(kKey2);
+  const int32_t kInt32Value = -45;
+  entry_writer.AppendVariantOfInt32(kInt32Value);
+  sub_writer.CloseContainer(&entry_writer);
+  sub_writer.OpenDictEntry(&entry_writer);
+  const std::string kKey3 = "example.";  // String ending with a dot.
+  entry_writer.AppendString(kKey3);
+  const double kDoubleValue = 4.9;
+  entry_writer.AppendVariantOfDouble(kDoubleValue);
+  sub_writer.CloseContainer(&entry_writer);
+  writer.CloseContainer(&sub_writer);
+
+  // Create the expected value.
+  base::DictionaryValue dictionary_value;
+  dictionary_value.SetKey(kKey1, base::Value(kBoolValue));
+  dictionary_value.SetKey(kKey2, base::Value(kInt32Value));
+  dictionary_value.SetKey(kKey3, base::Value(kDoubleValue));
+
+  // Pop a dictinoary.
+  MessageReader reader(response.get());
+  std::unique_ptr<base::Value> value(PopDataAsValue(&reader));
+  ASSERT_NE(nullptr, value);
+  EXPECT_TRUE(value->Equals(&dictionary_value));
+}
+
+TEST(ValuesUtilTest, PopDoubleToIntDictionary) {
+  // Create test data.
+  const int32_t kValues[] = {0, 1, 1, 2, 3, 5, 8, 13, 21};
+  const std::vector<int32_t> values(kValues, kValues + arraysize(kValues));
+  std::vector<double> keys(values.size());
+  for (size_t i = 0; i != values.size(); ++i)
+    keys[i] = std::sqrt(values[i]);
+
+  // Append a dictionary.
+  std::unique_ptr<Response> response(Response::CreateEmpty());
+  MessageWriter writer(response.get());
+  MessageWriter sub_writer(nullptr);
+  writer.OpenArray("{di}", &sub_writer);
+  for (size_t i = 0; i != values.size(); ++i) {
+    MessageWriter entry_writer(nullptr);
+    sub_writer.OpenDictEntry(&entry_writer);
+    entry_writer.AppendDouble(keys[i]);
+    entry_writer.AppendInt32(values[i]);
+    sub_writer.CloseContainer(&entry_writer);
+  }
+  writer.CloseContainer(&sub_writer);
+
+  // Create the expected value.
+  base::DictionaryValue dictionary_value;
+  for (size_t i = 0; i != values.size(); ++i) {
+    std::string key_string;
+    base::JSONWriter::Write(base::Value(keys[i]), &key_string);
+    dictionary_value.SetKey(key_string, base::Value(values[i]));
+  }
+
+  // Pop a dictionary.
+  MessageReader reader(response.get());
+  std::unique_ptr<base::Value> value(PopDataAsValue(&reader));
+  ASSERT_NE(nullptr, value);
+  EXPECT_TRUE(value->Equals(&dictionary_value));
+}
+
+TEST(ValuesUtilTest, AppendBasicTypes) {
+  const base::Value kBoolValue(false);
+  const base::Value kIntegerValue(42);
+  const base::Value kDoubleValue(4.2);
+  const base::Value kStringValue("string");
+
+  std::unique_ptr<Response> response(Response::CreateEmpty());
+  MessageWriter writer(response.get());
+  AppendBasicTypeValueData(&writer, kBoolValue);
+  AppendBasicTypeValueData(&writer, kIntegerValue);
+  AppendBasicTypeValueData(&writer, kDoubleValue);
+  AppendBasicTypeValueData(&writer, kStringValue);
+
+  MessageReader reader(response.get());
+  std::unique_ptr<base::Value> value;
+  value = PopDataAsValue(&reader);
+  ASSERT_NE(nullptr, value);
+  EXPECT_TRUE(value->Equals(&kBoolValue));
+  value = PopDataAsValue(&reader);
+  ASSERT_NE(nullptr, value);
+  EXPECT_TRUE(value->Equals(&kIntegerValue));
+  value = PopDataAsValue(&reader);
+  ASSERT_NE(nullptr, value);
+  EXPECT_TRUE(value->Equals(&kDoubleValue));
+  value = PopDataAsValue(&reader);
+  ASSERT_NE(nullptr, value);
+  EXPECT_TRUE(value->Equals(&kStringValue));
+}
+
+TEST(ValuesUtilTest, AppendBasicTypesAsVariant) {
+  const base::Value kBoolValue(false);
+  const base::Value kIntegerValue(42);
+  const base::Value kDoubleValue(4.2);
+  const base::Value kStringValue("string");
+
+  std::unique_ptr<Response> response(Response::CreateEmpty());
+  MessageWriter writer(response.get());
+  AppendBasicTypeValueDataAsVariant(&writer, kBoolValue);
+  AppendBasicTypeValueDataAsVariant(&writer, kIntegerValue);
+  AppendBasicTypeValueDataAsVariant(&writer, kDoubleValue);
+  AppendBasicTypeValueDataAsVariant(&writer, kStringValue);
+
+  MessageReader reader(response.get());
+  std::unique_ptr<base::Value> value;
+  value = PopDataAsValue(&reader);
+  ASSERT_NE(nullptr, value);
+  EXPECT_TRUE(value->Equals(&kBoolValue));
+  value = PopDataAsValue(&reader);
+  ASSERT_NE(nullptr, value);
+  EXPECT_TRUE(value->Equals(&kIntegerValue));
+  value = PopDataAsValue(&reader);
+  ASSERT_NE(nullptr, value);
+  EXPECT_TRUE(value->Equals(&kDoubleValue));
+  value = PopDataAsValue(&reader);
+  ASSERT_NE(nullptr, value);
+  EXPECT_TRUE(value->Equals(&kStringValue));
+}
+
+TEST(ValuesUtilTest, AppendValueDataBasicTypes) {
+  const base::Value kBoolValue(false);
+  const base::Value kIntegerValue(42);
+  const base::Value kDoubleValue(4.2);
+  const base::Value kStringValue("string");
+
+  std::unique_ptr<Response> response(Response::CreateEmpty());
+  MessageWriter writer(response.get());
+  AppendValueData(&writer, kBoolValue);
+  AppendValueData(&writer, kIntegerValue);
+  AppendValueData(&writer, kDoubleValue);
+  AppendValueData(&writer, kStringValue);
+
+  MessageReader reader(response.get());
+  std::unique_ptr<base::Value> value;
+  value = PopDataAsValue(&reader);
+  ASSERT_NE(nullptr, value);
+  EXPECT_TRUE(value->Equals(&kBoolValue));
+  value = PopDataAsValue(&reader);
+  ASSERT_NE(nullptr, value);
+  EXPECT_TRUE(value->Equals(&kIntegerValue));
+  value = PopDataAsValue(&reader);
+  ASSERT_NE(nullptr, value);
+  EXPECT_TRUE(value->Equals(&kDoubleValue));
+  value = PopDataAsValue(&reader);
+  ASSERT_NE(nullptr, value);
+  EXPECT_TRUE(value->Equals(&kStringValue));
+}
+
+TEST(ValuesUtilTest, AppendValueDataAsVariantBasicTypes) {
+  const base::Value kBoolValue(false);
+  const base::Value kIntegerValue(42);
+  const base::Value kDoubleValue(4.2);
+  const base::Value kStringValue("string");
+
+  std::unique_ptr<Response> response(Response::CreateEmpty());
+  MessageWriter writer(response.get());
+  AppendValueDataAsVariant(&writer, kBoolValue);
+  AppendValueDataAsVariant(&writer, kIntegerValue);
+  AppendValueDataAsVariant(&writer, kDoubleValue);
+  AppendValueDataAsVariant(&writer, kStringValue);
+
+  MessageReader reader(response.get());
+  std::unique_ptr<base::Value> value;
+  value = PopDataAsValue(&reader);
+  ASSERT_NE(nullptr, value);
+  EXPECT_TRUE(value->Equals(&kBoolValue));
+  value = PopDataAsValue(&reader);
+  ASSERT_NE(nullptr, value);
+  EXPECT_TRUE(value->Equals(&kIntegerValue));
+  value = PopDataAsValue(&reader);
+  ASSERT_NE(nullptr, value);
+  EXPECT_TRUE(value->Equals(&kDoubleValue));
+  value = PopDataAsValue(&reader);
+  ASSERT_NE(nullptr, value);
+  EXPECT_TRUE(value->Equals(&kStringValue));
+}
+
+TEST(ValuesUtilTest, AppendDictionary) {
+  // Set up the input dictionary.
+  const std::string kKey1 = "one";
+  const std::string kKey2 = "two";
+  const std::string kKey3 = "three";
+  const std::string kKey4 = "four";
+  const std::string kKey5 = "five";
+  const std::string kKey6 = "six";
+
+  const bool kBoolValue = true;
+  const int32_t kInt32Value = -45;
+  const double kDoubleValue = 4.9;
+  const std::string kStringValue = "fifty";
+
+  auto list_value = std::make_unique<base::ListValue>();
+  list_value->AppendBoolean(kBoolValue);
+  list_value->AppendInteger(kInt32Value);
+
+  auto dictionary_value = std::make_unique<base::DictionaryValue>();
+  dictionary_value->SetBoolean(kKey1, kBoolValue);
+  dictionary_value->SetInteger(kKey2, kDoubleValue);
+
+  base::DictionaryValue test_dictionary;
+  test_dictionary.SetBoolean(kKey1, kBoolValue);
+  test_dictionary.SetInteger(kKey2, kInt32Value);
+  test_dictionary.SetDouble(kKey3, kDoubleValue);
+  test_dictionary.SetString(kKey4, kStringValue);
+  test_dictionary.Set(kKey5, std::move(list_value));
+  test_dictionary.Set(kKey6, std::move(dictionary_value));
+
+  std::unique_ptr<Response> response(Response::CreateEmpty());
+  MessageWriter writer(response.get());
+  AppendValueData(&writer, test_dictionary);
+  base::Value int_value(kInt32Value);
+  AppendValueData(&writer, int_value);
+
+  // Read the data.
+  MessageReader reader(response.get());
+  std::unique_ptr<base::Value> value;
+  value = PopDataAsValue(&reader);
+  ASSERT_NE(nullptr, value);
+  EXPECT_TRUE(value->Equals(&test_dictionary));
+  value = PopDataAsValue(&reader);
+  ASSERT_NE(nullptr, value);
+  EXPECT_TRUE(value->Equals(&int_value));
+}
+
+TEST(ValuesUtilTest, AppendDictionaryAsVariant) {
+  // Set up the input dictionary.
+  const std::string kKey1 = "one";
+  const std::string kKey2 = "two";
+  const std::string kKey3 = "three";
+  const std::string kKey4 = "four";
+  const std::string kKey5 = "five";
+  const std::string kKey6 = "six";
+
+  const bool kBoolValue = true;
+  const int32_t kInt32Value = -45;
+  const double kDoubleValue = 4.9;
+  const std::string kStringValue = "fifty";
+
+  auto list_value = std::make_unique<base::ListValue>();
+  list_value->AppendBoolean(kBoolValue);
+  list_value->AppendInteger(kInt32Value);
+
+  auto dictionary_value = std::make_unique<base::DictionaryValue>();
+  dictionary_value->SetBoolean(kKey1, kBoolValue);
+  dictionary_value->SetInteger(kKey2, kDoubleValue);
+
+  base::DictionaryValue test_dictionary;
+  test_dictionary.SetBoolean(kKey1, kBoolValue);
+  test_dictionary.SetInteger(kKey2, kInt32Value);
+  test_dictionary.SetDouble(kKey3, kDoubleValue);
+  test_dictionary.SetString(kKey4, kStringValue);
+  test_dictionary.Set(kKey5, std::move(list_value));
+  test_dictionary.Set(kKey6, std::move(dictionary_value));
+
+  std::unique_ptr<Response> response(Response::CreateEmpty());
+  MessageWriter writer(response.get());
+  AppendValueDataAsVariant(&writer, test_dictionary);
+  base::Value int_value(kInt32Value);
+  AppendValueData(&writer, int_value);
+
+  // Read the data.
+  MessageReader reader(response.get());
+  std::unique_ptr<base::Value> value;
+  value = PopDataAsValue(&reader);
+  ASSERT_NE(nullptr, value);
+  EXPECT_TRUE(value->Equals(&test_dictionary));
+  value = PopDataAsValue(&reader);
+  ASSERT_NE(nullptr, value);
+  EXPECT_TRUE(value->Equals(&int_value));
+}
+
+TEST(ValuesUtilTest, AppendList) {
+  // Set up the input list.
+  const std::string kKey1 = "one";
+  const std::string kKey2 = "two";
+
+  const bool kBoolValue = true;
+  const int32_t kInt32Value = -45;
+  const double kDoubleValue = 4.9;
+  const std::string kStringValue = "fifty";
+
+  std::unique_ptr<base::ListValue> list_value(new base::ListValue());
+  list_value->AppendBoolean(kBoolValue);
+  list_value->AppendInteger(kInt32Value);
+
+  std::unique_ptr<base::DictionaryValue> dictionary_value(
+      new base::DictionaryValue());
+  dictionary_value->SetBoolean(kKey1, kBoolValue);
+  dictionary_value->SetInteger(kKey2, kDoubleValue);
+
+  base::ListValue test_list;
+  test_list.AppendBoolean(kBoolValue);
+  test_list.AppendInteger(kInt32Value);
+  test_list.AppendDouble(kDoubleValue);
+  test_list.AppendString(kStringValue);
+  test_list.Append(std::move(list_value));
+  test_list.Append(std::move(dictionary_value));
+
+  std::unique_ptr<Response> response(Response::CreateEmpty());
+  MessageWriter writer(response.get());
+  AppendValueData(&writer, test_list);
+  base::Value int_value(kInt32Value);
+  AppendValueData(&writer, int_value);
+
+  // Read the data.
+  MessageReader reader(response.get());
+  std::unique_ptr<base::Value> value;
+  value = PopDataAsValue(&reader);
+  ASSERT_NE(nullptr, value);
+  EXPECT_TRUE(value->Equals(&test_list));
+  value = PopDataAsValue(&reader);
+  ASSERT_NE(nullptr, value);
+  EXPECT_TRUE(value->Equals(&int_value));
+}
+
+TEST(ValuesUtilTest, AppendListAsVariant) {
+  // Set up the input list.
+  const std::string kKey1 = "one";
+  const std::string kKey2 = "two";
+
+  const bool kBoolValue = true;
+  const int32_t kInt32Value = -45;
+  const double kDoubleValue = 4.9;
+  const std::string kStringValue = "fifty";
+
+  std::unique_ptr<base::ListValue> list_value(new base::ListValue());
+  list_value->AppendBoolean(kBoolValue);
+  list_value->AppendInteger(kInt32Value);
+
+  std::unique_ptr<base::DictionaryValue> dictionary_value(
+      new base::DictionaryValue());
+  dictionary_value->SetBoolean(kKey1, kBoolValue);
+  dictionary_value->SetInteger(kKey2, kDoubleValue);
+
+  base::ListValue test_list;
+  test_list.AppendBoolean(kBoolValue);
+  test_list.AppendInteger(kInt32Value);
+  test_list.AppendDouble(kDoubleValue);
+  test_list.AppendString(kStringValue);
+  test_list.Append(std::move(list_value));
+  test_list.Append(std::move(dictionary_value));
+
+  std::unique_ptr<Response> response(Response::CreateEmpty());
+  MessageWriter writer(response.get());
+  AppendValueDataAsVariant(&writer, test_list);
+  base::Value int_value(kInt32Value);
+  AppendValueData(&writer, int_value);
+
+  // Read the data.
+  MessageReader reader(response.get());
+  std::unique_ptr<base::Value> value;
+  value = PopDataAsValue(&reader);
+  ASSERT_NE(nullptr, value);
+  EXPECT_TRUE(value->Equals(&test_list));
+  value = PopDataAsValue(&reader);
+  ASSERT_NE(nullptr, value);
+  EXPECT_TRUE(value->Equals(&int_value));
+}
+
+}  // namespace dbus
diff --git a/ipc/constants.mojom b/ipc/constants.mojom
new file mode 100644
index 0000000..502187f
--- /dev/null
+++ b/ipc/constants.mojom
@@ -0,0 +1,8 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module IPC.mojom;
+
+// NOTE: This MUST match the value of MSG_ROUTING_NONE in src/ipc/ipc_message.h.
+const int32 kRoutingIdNone = -2;
diff --git a/ipc/ipc_channel_nacl.cc b/ipc/ipc_channel_nacl.cc
deleted file mode 100644
index 190da45..0000000
--- a/ipc/ipc_channel_nacl.cc
+++ /dev/null
@@ -1,396 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ipc/ipc_channel_nacl.h"
-
-#include <errno.h>
-#include <stddef.h>
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <algorithm>
-
-#include "base/bind.h"
-#include "base/logging.h"
-#include "base/macros.h"
-#include "base/memory/ptr_util.h"
-#include "base/message_loop/message_pump_for_io.h"
-#include "base/single_thread_task_runner.h"
-#include "base/synchronization/lock.h"
-#include "base/task_runner_util.h"
-#include "base/threading/simple_thread.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "ipc/ipc_listener.h"
-#include "ipc/ipc_logging.h"
-#include "ipc/ipc_message_attachment_set.h"
-#include "ipc/ipc_platform_file_attachment_posix.h"
-#include "native_client/src/public/imc_syscalls.h"
-#include "native_client/src/public/imc_types.h"
-
-namespace IPC {
-
-struct MessageContents {
-  std::vector<char> data;
-  std::vector<int> fds;
-};
-
-namespace {
-
-bool ReadDataOnReaderThread(int pipe, MessageContents* contents) {
-  DCHECK(pipe >= 0);
-  if (pipe < 0)
-    return false;
-
-  contents->data.resize(Channel::kReadBufferSize);
-  contents->fds.resize(NACL_ABI_IMC_DESC_MAX);
-
-  NaClAbiNaClImcMsgIoVec iov = { &contents->data[0], contents->data.size() };
-  NaClAbiNaClImcMsgHdr msg = {
-    &iov, 1, &contents->fds[0], contents->fds.size()
-  };
-
-  int bytes_read = imc_recvmsg(pipe, &msg, 0);
-
-  if (bytes_read <= 0) {
-    // NaClIPCAdapter::BlockingReceive returns -1 when the pipe closes (either
-    // due to error or for regular shutdown).
-    contents->data.clear();
-    contents->fds.clear();
-    return false;
-  }
-  DCHECK(bytes_read);
-  // Resize the buffers down to the number of bytes and fds we actually read.
-  contents->data.resize(bytes_read);
-  contents->fds.resize(msg.desc_length);
-  return true;
-}
-
-}  // namespace
-
-// static
-constexpr size_t Channel::kMaximumMessageSize;
-
-class ChannelNacl::ReaderThreadRunner
-    : public base::DelegateSimpleThread::Delegate {
- public:
-  // |pipe|: A file descriptor from which we will read using imc_recvmsg.
-  // |data_read_callback|: A callback we invoke (on the main thread) when we
-  //                       have read data.
-  // |failure_callback|: A callback we invoke when we have a failure reading
-  //                     from |pipe|.
-  // |main_message_loop|: A proxy for the main thread, where we will invoke the
-  //                      above callbacks.
-  ReaderThreadRunner(
-      int pipe,
-      base::Callback<void(std::unique_ptr<MessageContents>)> data_read_callback,
-      base::Callback<void()> failure_callback,
-      scoped_refptr<base::SingleThreadTaskRunner> main_task_runner);
-
-  // DelegateSimpleThread implementation. Reads data from the pipe in a loop
-  // until either we are told to quit or a read fails.
-  void Run() override;
-
- private:
-  int pipe_;
-  base::Callback<void(std::unique_ptr<MessageContents>)> data_read_callback_;
-  base::Callback<void ()> failure_callback_;
-  scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
-
-  DISALLOW_COPY_AND_ASSIGN(ReaderThreadRunner);
-};
-
-ChannelNacl::ReaderThreadRunner::ReaderThreadRunner(
-    int pipe,
-    base::Callback<void(std::unique_ptr<MessageContents>)> data_read_callback,
-    base::Callback<void()> failure_callback,
-    scoped_refptr<base::SingleThreadTaskRunner> main_task_runner)
-    : pipe_(pipe),
-      data_read_callback_(data_read_callback),
-      failure_callback_(failure_callback),
-      main_task_runner_(main_task_runner) {}
-
-void ChannelNacl::ReaderThreadRunner::Run() {
-  while (true) {
-    std::unique_ptr<MessageContents> msg_contents(new MessageContents);
-    bool success = ReadDataOnReaderThread(pipe_, msg_contents.get());
-    if (success) {
-      main_task_runner_->PostTask(
-          FROM_HERE,
-          base::Bind(data_read_callback_, base::Passed(&msg_contents)));
-    } else {
-      main_task_runner_->PostTask(FROM_HERE, failure_callback_);
-      // Because the read failed, we know we're going to quit. Don't bother
-      // trying to read again.
-      return;
-    }
-  }
-}
-
-ChannelNacl::ChannelNacl(const IPC::ChannelHandle& channel_handle,
-                         Mode mode,
-                         Listener* listener)
-    : ChannelReader(listener),
-      mode_(mode),
-      waiting_connect_(true),
-      pipe_(-1),
-      weak_ptr_factory_(this) {
-  if (!CreatePipe(channel_handle)) {
-    // The pipe may have been closed already.
-    const char *modestr = (mode_ & MODE_SERVER_FLAG) ? "server" : "client";
-    LOG(WARNING) << "Unable to create pipe in " << modestr << " mode";
-  }
-}
-
-ChannelNacl::~ChannelNacl() {
-  CleanUp();
-  Close();
-}
-
-bool ChannelNacl::Connect() {
-  WillConnect();
-
-  if (pipe_ == -1) {
-    DLOG(WARNING) << "Channel creation failed";
-    return false;
-  }
-
-  // Note that Connect is called on the "Channel" thread (i.e., the same thread
-  // where Channel::Send will be called, and the same thread that should receive
-  // messages). The constructor might be invoked on another thread (see
-  // ChannelProxy for an example of that). Therefore, we must wait until Connect
-  // is called to decide which SingleThreadTaskRunner to pass to
-  // ReaderThreadRunner.
-  reader_thread_runner_.reset(new ReaderThreadRunner(
-      pipe_,
-      base::Bind(&ChannelNacl::DidRecvMsg, weak_ptr_factory_.GetWeakPtr()),
-      base::Bind(&ChannelNacl::ReadDidFail, weak_ptr_factory_.GetWeakPtr()),
-      base::ThreadTaskRunnerHandle::Get()));
-  reader_thread_.reset(
-      new base::DelegateSimpleThread(reader_thread_runner_.get(),
-                                     "ipc_channel_nacl reader thread"));
-  reader_thread_->Start();
-  waiting_connect_ = false;
-  // If there were any messages queued before connection, send them.
-  ProcessOutgoingMessages();
-  base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::Bind(&ChannelNacl::CallOnChannelConnected,
-                            weak_ptr_factory_.GetWeakPtr()));
-
-  return true;
-}
-
-void ChannelNacl::Close() {
-  // For now, we assume that at shutdown, the reader thread will be woken with
-  // a failure (see NaClIPCAdapter::BlockingRead and CloseChannel). Or... we
-  // might simply be killed with no chance to clean up anyway :-).
-  // If untrusted code tries to close the channel prior to shutdown, it's likely
-  // to hang.
-  // TODO(dmichael): Can we do anything smarter here to make sure the reader
-  //                 thread wakes up and quits?
-  reader_thread_->Join();
-  close(pipe_);
-  pipe_ = -1;
-  reader_thread_runner_.reset();
-  reader_thread_.reset();
-  read_queue_.clear();
-  output_queue_.clear();
-}
-
-bool ChannelNacl::Send(Message* message) {
-  DCHECK(!message->HasAttachments());
-  DVLOG(2) << "sending message @" << message << " on channel @" << this
-           << " with type " << message->type();
-  std::unique_ptr<Message> message_ptr(message);
-
-#if BUILDFLAG(IPC_MESSAGE_LOG_ENABLED)
-  Logging::GetInstance()->OnSendMessage(message_ptr.get());
-#endif  // BUILDFLAG(IPC_MESSAGE_LOG_ENABLED)
-
-  TRACE_EVENT_WITH_FLOW0(TRACE_DISABLED_BY_DEFAULT("ipc.flow"),
-                         "ChannelNacl::Send",
-                         message->header()->flags,
-                         TRACE_EVENT_FLAG_FLOW_OUT);
-  output_queue_.push_back(std::move(message_ptr));
-  if (!waiting_connect_)
-    return ProcessOutgoingMessages();
-
-  return true;
-}
-
-void ChannelNacl::DidRecvMsg(std::unique_ptr<MessageContents> contents) {
-  // Close sets the pipe to -1. It's possible we'll get a buffer sent to us from
-  // the reader thread after Close is called. If so, we ignore it.
-  if (pipe_ == -1)
-    return;
-
-  auto data = std::make_unique<std::vector<char>>();
-  data->swap(contents->data);
-  read_queue_.push_back(std::move(data));
-
-  input_attachments_.reserve(contents->fds.size());
-  for (int fd : contents->fds) {
-    input_attachments_.push_back(
-        new internal::PlatformFileAttachment(base::ScopedFD(fd)));
-  }
-  contents->fds.clear();
-
-  // In POSIX, we would be told when there are bytes to read by implementing
-  // OnFileCanReadWithoutBlocking in MessagePumpForIO::FdWatcher. In NaCl, we
-  // instead know at this point because the reader thread posted some data to
-  // us.
-  ProcessIncomingMessages();
-}
-
-void ChannelNacl::ReadDidFail() {
-  Close();
-}
-
-bool ChannelNacl::CreatePipe(
-    const IPC::ChannelHandle& channel_handle) {
-  DCHECK(pipe_ == -1);
-
-  // There's one possible case in NaCl:
-  // 1) It's a channel wrapping a pipe that is given to us.
-  // We don't support these:
-  // 2) It's for a named channel.
-  // 3) It's for a client that we implement ourself.
-  // 4) It's the initial IPC channel.
-
-  if (channel_handle.socket.fd == -1) {
-    NOTIMPLEMENTED();
-    return false;
-  }
-  pipe_ = channel_handle.socket.fd;
-  return true;
-}
-
-bool ChannelNacl::ProcessOutgoingMessages() {
-  DCHECK(!waiting_connect_);  // Why are we trying to send messages if there's
-                              // no connection?
-  if (output_queue_.empty())
-    return true;
-
-  if (pipe_ == -1)
-    return false;
-
-  // Write out all the messages. The trusted implementation is guaranteed to not
-  // block. See NaClIPCAdapter::Send for the implementation of imc_sendmsg.
-  while (!output_queue_.empty()) {
-    std::unique_ptr<Message> msg = std::move(output_queue_.front());
-    output_queue_.pop_front();
-
-    const size_t num_fds = msg->attachment_set()->size();
-    DCHECK(num_fds <= MessageAttachmentSet::kMaxDescriptorsPerMessage);
-    std::vector<int> fds;
-    fds.reserve(num_fds);
-    for (size_t i = 0; i < num_fds; i++) {
-      scoped_refptr<MessageAttachment> attachment =
-          msg->attachment_set()->GetAttachmentAt(i);
-      DCHECK_EQ(MessageAttachment::Type::PLATFORM_FILE, attachment->GetType());
-      fds.push_back(static_cast<internal::PlatformFileAttachment&>(*attachment)
-                        .TakePlatformFile());
-    }
-
-    NaClAbiNaClImcMsgIoVec iov = {
-      const_cast<void*>(msg->data()), msg->size()
-    };
-    NaClAbiNaClImcMsgHdr msgh = {&iov, 1, fds.data(), num_fds};
-    ssize_t bytes_written = imc_sendmsg(pipe_, &msgh, 0);
-
-    DCHECK(bytes_written);  // The trusted side shouldn't return 0.
-    if (bytes_written < 0) {
-      // The trusted side should only ever give us an error of EPIPE. We
-      // should never be interrupted, nor should we get EAGAIN.
-      DCHECK(errno == EPIPE);
-      Close();
-      PLOG(ERROR) << "pipe_ error on "
-                  << pipe_
-                  << " Currently writing message of size: "
-                  << msg->size();
-      return false;
-    } else {
-      msg->attachment_set()->CommitAllDescriptors();
-    }
-
-    // Message sent OK!
-    DVLOG(2) << "sent message @" << msg.get() << " with type " << msg->type()
-             << " on fd " << pipe_;
-  }
-  return true;
-}
-
-void ChannelNacl::CallOnChannelConnected() {
-  listener()->OnChannelConnected(-1);
-}
-
-ChannelNacl::ReadState ChannelNacl::ReadData(
-    char* buffer,
-    int buffer_len,
-    int* bytes_read) {
-  *bytes_read = 0;
-  if (pipe_ == -1)
-    return READ_FAILED;
-  if (read_queue_.empty())
-    return READ_PENDING;
-  while (!read_queue_.empty() && *bytes_read < buffer_len) {
-    std::vector<char>* vec = read_queue_.front().get();
-    size_t bytes_to_read = buffer_len - *bytes_read;
-    if (vec->size() <= bytes_to_read) {
-      // We can read and discard the entire vector.
-      std::copy(vec->begin(), vec->end(), buffer + *bytes_read);
-      *bytes_read += vec->size();
-      read_queue_.pop_front();
-    } else {
-      // Read all the bytes we can and discard them from the front of the
-      // vector. (This can be slowish, since erase has to move the back of the
-      // vector to the front, but it's hopefully a temporary hack and it keeps
-      // the code simple).
-      std::copy(vec->begin(), vec->begin() + bytes_to_read,
-                buffer + *bytes_read);
-      vec->erase(vec->begin(), vec->begin() + bytes_to_read);
-      *bytes_read += bytes_to_read;
-    }
-  }
-  return READ_SUCCEEDED;
-}
-
-bool ChannelNacl::ShouldDispatchInputMessage(Message* msg) {
-  return true;
-}
-
-bool ChannelNacl::GetAttachments(Message* msg) {
-  uint16_t header_fds = msg->header()->num_fds;
-  CHECK(header_fds == input_attachments_.size());
-  if (header_fds == 0)
-    return true;  // Nothing to do.
-
-  for (auto& attachment : input_attachments_) {
-    msg->attachment_set()->AddAttachment(std::move(attachment));
-  }
-  input_attachments_.clear();
-  return true;
-}
-
-bool ChannelNacl::DidEmptyInputBuffers() {
-  // When the input data buffer is empty, the attachments should be too.
-  return input_attachments_.empty();
-}
-
-void ChannelNacl::HandleInternalMessage(const Message& msg) {
-  // The trusted side IPC::Channel should handle the "hello" handshake; we
-  // should not receive the "Hello" message.
-  NOTREACHED();
-}
-
-// Channel's methods
-
-// static
-std::unique_ptr<Channel> Channel::Create(
-    const IPC::ChannelHandle& channel_handle,
-    Mode mode,
-    Listener* listener) {
-  return std::make_unique<ChannelNacl>(channel_handle, mode, listener);
-}
-
-}  // namespace IPC
diff --git a/ipc/ipc_channel_nacl.h b/ipc/ipc_channel_nacl.h
deleted file mode 100644
index 06e7f6a..0000000
--- a/ipc/ipc_channel_nacl.h
+++ /dev/null
@@ -1,114 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IPC_IPC_CHANNEL_NACL_H_
-#define IPC_IPC_CHANNEL_NACL_H_
-
-#include <memory>
-#include <string>
-
-#include "base/containers/circular_deque.h"
-#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-#include "base/process/process.h"
-#include "base/threading/simple_thread.h"
-#include "ipc/ipc_channel.h"
-#include "ipc/ipc_channel_reader.h"
-
-namespace IPC {
-
-class MessageAttachment;
-
-// Contains the results from one call to imc_recvmsg (data and file
-// descriptors).
-struct MessageContents;
-
-// Similar to the ChannelPosix but for Native Client code.
-// This is somewhat different because sendmsg/recvmsg here do not follow POSIX
-// semantics. Instead, they are implemented by a custom embedding of
-// NaClDescCustom. See NaClIPCAdapter for the trusted-side implementation.
-//
-// We don't need to worry about complicated set up and READWRITE mode for
-// sharing handles. We also currently do not support passing file descriptors or
-// named pipes, and we use background threads to emulate signaling when we can
-// read or write without blocking.
-class ChannelNacl : public Channel,
-                    public internal::ChannelReader {
- public:
-  // Mirror methods of Channel, see ipc_channel.h for description.
-  ChannelNacl(const IPC::ChannelHandle& channel_handle,
-              Mode mode,
-              Listener* listener);
-  ~ChannelNacl() override;
-
-  // Channel implementation.
-  bool Connect() override;
-  void Close() override;
-  bool Send(Message* message) override;
-
-  // Posted to the main thread by ReaderThreadRunner.
-  void DidRecvMsg(std::unique_ptr<MessageContents> contents);
-  void ReadDidFail();
-
- private:
-  class ReaderThreadRunner;
-
-  bool CreatePipe(const IPC::ChannelHandle& channel_handle);
-  bool ProcessOutgoingMessages();
-  void CallOnChannelConnected();
-
-  // ChannelReader implementation.
-  ReadState ReadData(char* buffer,
-                     int buffer_len,
-                     int* bytes_read) override;
-  bool ShouldDispatchInputMessage(Message* msg) override;
-  bool GetAttachments(Message* msg) override;
-  bool DidEmptyInputBuffers() override;
-  void HandleInternalMessage(const Message& msg) override;
-
-  Mode mode_;
-  bool waiting_connect_;
-
-  // The pipe used for communication.
-  int pipe_;
-
-  // We use a thread for reading, so that we can simply block on reading and
-  // post the received data back to the main thread to be properly interleaved
-  // with other tasks in the MessagePump.
-  //
-  // imc_recvmsg supports non-blocking reads, but there's no easy way to be
-  // informed when a write or read can be done without blocking (this is handled
-  // by libevent in Posix).
-  std::unique_ptr<ReaderThreadRunner> reader_thread_runner_;
-  std::unique_ptr<base::DelegateSimpleThread> reader_thread_;
-
-  // IPC::ChannelReader expects to be able to call ReadData on us to
-  // synchronously read data waiting in the pipe's buffer without blocking.
-  // Since we can't do that (see 1 and 2 above), the reader thread does blocking
-  // reads and posts the data over to the main thread in MessageContents. Each
-  // MessageContents object is the result of one call to "imc_recvmsg".
-  // DidRecvMsg breaks the MessageContents out in to the data and the file
-  // descriptors, and puts them on these two queues.
-  // TODO(dmichael): There's probably a more efficient way to emulate this with
-  //                 a circular buffer or something, so we don't have to do so
-  //                 many heap allocations. But it maybe isn't worth
-  //                 the trouble given that we probably want to implement 1 and
-  //                 2 above in NaCl eventually.
-  // When ReadData is called, it pulls the bytes out of this queue in order.
-  base::circular_deque<std::unique_ptr<std::vector<char>>> read_queue_;
-  // Queue of file descriptor attachments extracted from imc_recvmsg messages.
-  std::vector<scoped_refptr<MessageAttachment>> input_attachments_;
-
-  // This queue is used when a message is sent prior to Connect having been
-  // called. Normally after we're connected, the queue is empty.
-  base::circular_deque<std::unique_ptr<Message>> output_queue_;
-
-  base::WeakPtrFactory<ChannelNacl> weak_ptr_factory_;
-
-  DISALLOW_IMPLICIT_CONSTRUCTORS(ChannelNacl);
-};
-
-}  // namespace IPC
-
-#endif  // IPC_IPC_CHANNEL_NACL_H_
diff --git a/ipc/ipc_test.mojom b/ipc/ipc_test.mojom
new file mode 100644
index 0000000..fc24e37
--- /dev/null
+++ b/ipc/ipc_test.mojom
@@ -0,0 +1,54 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module IPC.mojom;
+
+interface SimpleTestDriver {
+  ExpectValue(int32 value);
+
+  [Sync]
+  GetExpectedValue() => (int32 value);
+
+  [Sync]
+  RequestValue() => (int32 value);
+
+  RequestQuit() => ();
+};
+
+interface SimpleTestClient {
+  [Sync]
+  RequestValue() => (int32 value);
+};
+
+interface PingReceiver {
+  Ping() => ();
+};
+
+struct TestStruct {};
+
+interface TestStructPasser {
+  Pass(TestStruct s);
+};
+
+interface IndirectTestDriver {
+  GetPingReceiver(associated PingReceiver& request);
+};
+
+interface Reflector {
+  Ping(string value) => (string value);
+  [Sync]
+  SyncPing(string value) => (string response);
+  Quit();
+};
+
+interface AssociatedInterfaceVendor {
+  GetTestInterface(associated SimpleTestDriver& interface_reqest);
+};
+
+interface InterfacePassingTestDriver {
+  Init() => ();
+  GetPingReceiver(array<PingReceiver&> request) => ();
+  GetAssociatedPingReceiver(array<associated PingReceiver&> request) => ();
+  Quit();
+};
diff --git a/libchrome_tools/files_not_built b/libchrome_tools/files_not_built
new file mode 100755
index 0000000..64ad444
--- /dev/null
+++ b/libchrome_tools/files_not_built
@@ -0,0 +1,16 @@
+#!/bin/bash
+# Copyright 2020 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+#
+# List cc files not in BUILD.gn, and not excluded by patterns in BUILD.IGNORE
+# This list can be run by human for sanity check that no imporant things are
+# ignored after each uprev.
+
+cd $(dirname $0)/..
+find . -name '*.cc' \
+ | sed -e 's/^\.\///g' \
+ | xargs -n 1 -P 1 bash -c \
+   'for i in $(cat BUILD.IGNORE); do grep $i <(echo $0) >/dev/null && exit; done; echo $0' \
+ | xargs -n 1 -P 1 sh -c 'grep $0 BUILD.gn >/dev/null || echo $0'
+
diff --git a/mojo/BUILD.gn b/mojo/BUILD.gn
deleted file mode 100644
index f6cd5b6..0000000
--- a/mojo/BUILD.gn
+++ /dev/null
@@ -1,69 +0,0 @@
-# Copyright 2014 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//build/config/ui.gni")
-import("//testing/test.gni")
-
-group("mojo") {
-  # Meta-target, don't link into production code.
-  testonly = true
-  deps = [
-    ":tests",
-  ]
-
-  if (!(is_linux && current_cpu == "x86")) {
-    deps += [ "//mojo/public" ]
-  }
-
-  if (is_android) {
-    deps += [ "//mojo/public/java/system" ]
-  }
-
-  deps += [ "//services/service_manager:all" ]
-}
-
-group("tests") {
-  testonly = true
-  deps = [
-    ":mojo_perftests",
-    ":mojo_unittests",
-    "//ipc:ipc_tests",
-    "//services/service_manager/tests",
-  ]
-}
-
-test("mojo_unittests") {
-  deps = [
-    "//mojo/core:test_sources",
-    "//mojo/core/test:run_all_unittests",
-    "//mojo/public/cpp/base:tests",
-    "//mojo/public/cpp/bindings/tests",
-    "//mojo/public/cpp/platform/tests",
-    "//mojo/public/cpp/system/tests",
-  ]
-}
-
-test("mojo_perftests") {
-  deps = [
-    "//mojo/core/test:run_all_perftests",
-    "//mojo/core/test:test_support",
-    "//mojo/public/c/system/tests:perftests",
-    "//mojo/public/cpp/bindings/tests:perftests",
-  ]
-
-  if (!is_ios) {
-    sources = [
-      "//mojo/core/message_pipe_perftest.cc",
-    ]
-
-    deps += [
-      "//base",
-      "//base/test:test_support",
-      "//mojo/core:embedder_internal",
-      "//mojo/core:test_utils",
-      "//mojo/core/embedder",
-      "//testing/gtest",
-    ]
-  }
-}
diff --git a/mojo/PRESUBMIT.py b/mojo/PRESUBMIT.py
deleted file mode 100644
index 2766055..0000000
--- a/mojo/PRESUBMIT.py
+++ /dev/null
@@ -1,44 +0,0 @@
-# Copyright 2014 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-"""Presubmit script for mojo
-
-See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
-for more details about the presubmit API built into depot_tools.
-"""
-
-import os.path
-
-def CheckChangeOnUpload(input_api, output_api):
-  # Additional python module paths (we're in src/mojo/); not everyone needs
-  # them, but it's easiest to add them to everyone's path.
-  # For ply and jinja2:
-  third_party_path = os.path.join(
-      input_api.PresubmitLocalPath(), "..", "third_party")
-  # For the bindings generator:
-  mojo_public_bindings_pylib_path = os.path.join(
-      input_api.PresubmitLocalPath(), "public", "tools", "bindings", "pylib")
-  # For the python bindings:
-  mojo_python_bindings_path = os.path.join(
-      input_api.PresubmitLocalPath(), "public", "python")
-  # TODO(vtl): Don't lint these files until the (many) problems are fixed
-  # (possibly by deleting/rewriting some files).
-  temporary_black_list = input_api.DEFAULT_BLACK_LIST + \
-      (r".*\bpublic[\\\/]tools[\\\/]bindings[\\\/]pylib[\\\/]mojom[\\\/]"
-           r"generate[\\\/].+\.py$",
-       r".*\bpublic[\\\/]tools[\\\/]bindings[\\\/]generators[\\\/].+\.py$",
-       r".*\bspy[\\\/]ui[\\\/].+\.py$",
-       r".*\btools[\\\/]pylib[\\\/]transitive_hash\.py$",
-       r".*\btools[\\\/]test_runner\.py$")
-
-  results = []
-  pylint_extra_paths = [
-      third_party_path,
-      mojo_public_bindings_pylib_path,
-      mojo_python_bindings_path,
-  ]
-  results += input_api.canned_checks.RunPylint(
-      input_api, output_api, extra_paths_list=pylint_extra_paths,
-      black_list=temporary_black_list)
-  return results
diff --git a/mojo/core/BUILD.gn b/mojo/core/BUILD.gn
deleted file mode 100644
index 49a537b..0000000
--- a/mojo/core/BUILD.gn
+++ /dev/null
@@ -1,325 +0,0 @@
-# Copyright 2018 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//build/config/nacl/config.gni")
-import("//testing/test.gni")
-
-component("embedder_internal") {
-  output_name = "mojo_core_embedder_internal"
-  public_deps = [
-    ":impl_for_embedder",
-  ]
-  visibility = [
-    ":test_sources",
-    "//mojo:*",
-    "//mojo/core/embedder",
-  ]
-}
-
-# Bits of the EDK library which do not depend on public API linkage. It is
-# not allowed for this target or any of its transitive dependencies to depend
-# on anything under //mojo/public beyond strict C type definitions.
-#
-# This is templated because it's consumed by both the ":embedder_internal"
-# component library as well as the ":mojo_core" shared library. In the former
-# case we want to export symbols, but in the latter case we don't. The template
-# stamps out two nearly identical targets which differ only in what symbols they
-# export.
-template("core_impl_source_set") {
-  source_set(target_name) {
-    if (invoker.for_shared_library) {
-      visibility = [ ":shared_library" ]
-    } else {
-      visibility = [ ":embedder_internal" ]
-    }
-
-    public = [
-      "channel.h",
-      "configuration.h",
-      "connection_params.h",
-      "core.h",
-      "data_pipe_consumer_dispatcher.h",
-      "data_pipe_control_message.h",
-      "data_pipe_producer_dispatcher.h",
-      "dispatcher.h",
-      "embedder/configuration.h",
-      "embedder/process_error_callback.h",
-      "entrypoints.h",
-      "handle_signals_state.h",
-      "handle_table.h",
-      "invitation_dispatcher.h",
-      "message_pipe_dispatcher.h",
-      "node_controller.h",
-      "options_validation.h",
-      "platform_handle_dispatcher.h",
-      "platform_handle_utils.h",
-      "platform_shared_memory_mapping.h",
-      "request_context.h",
-      "scoped_process_handle.h",
-      "shared_buffer_dispatcher.h",
-      "user_message_impl.h",
-    ]
-
-    sources = [
-      "atomic_flag.h",
-      "broker.h",
-      "broker_win.cc",
-      "channel.cc",
-      "channel_win.cc",
-      "configuration.cc",
-      "connection_params.cc",
-      "core.cc",
-      "data_pipe_consumer_dispatcher.cc",
-      "data_pipe_control_message.cc",
-      "data_pipe_producer_dispatcher.cc",
-      "dispatcher.cc",
-      "entrypoints.cc",
-      "handle_table.cc",
-      "invitation_dispatcher.cc",
-      "message_pipe_dispatcher.cc",
-      "node_channel.cc",
-      "node_channel.h",
-      "node_controller.cc",
-      "platform_handle_dispatcher.cc",
-      "platform_handle_in_transit.cc",
-      "platform_handle_in_transit.h",
-      "platform_handle_utils.cc",
-      "platform_shared_memory_mapping.cc",
-      "request_context.cc",
-      "scoped_process_handle.cc",
-      "shared_buffer_dispatcher.cc",
-      "user_message_impl.cc",
-      "watch.cc",
-      "watch.h",
-      "watcher_dispatcher.cc",
-      "watcher_dispatcher.h",
-      "watcher_set.cc",
-      "watcher_set.h",
-    ]
-
-    public_deps = [
-      "//base",
-      "//mojo/core/ports",
-      "//mojo/public/c/system:headers",
-      "//mojo/public/cpp/platform",
-    ]
-
-    if (is_fuchsia) {
-      sources += [ "channel_fuchsia.cc" ]
-
-      public_deps += [ "//third_party/fuchsia-sdk:fdio" ]
-    }
-
-    if (is_posix) {
-      if (!is_nacl || is_nacl_nonsfi) {
-        sources += [
-          "broker_posix.cc",
-          "channel_posix.cc",
-        ]
-      }
-    }
-
-    if (is_mac && !is_ios) {
-      sources += [
-        "mach_port_relay.cc",
-        "mach_port_relay.h",
-      ]
-    }
-
-    if (!is_nacl || is_nacl_nonsfi) {
-      sources += [
-        "broker_host.cc",
-        "broker_host.h",
-      ]
-    }
-
-    defines = []
-    if (invoker.for_shared_library) {
-      defines += [ "MOJO_CORE_SHARED_LIBRARY" ]
-    } else {
-      defines += [ "MOJO_SYSTEM_IMPL_IMPLEMENTATION" ]
-    }
-
-    deps = []
-    if (is_android) {
-      deps += [ "//third_party/ashmem" ]
-    }
-    if (!is_nacl) {
-      deps += [ "//crypto" ]
-    }
-
-    if (is_win) {
-      cflags = [ "/wd4324" ]  # Structure was padded due to __declspec(align()),
-                              # which is uninteresting.
-    }
-
-    # Use target_os == "chromeos" instead of is_chromeos because we need to
-    # build NaCl targets (i.e. IRT) for ChromeOS the same as the rest of ChromeOS.
-    if (is_android || target_os == "chromeos") {
-      defines += [ "MOJO_CORE_LEGACY_PROTOCOL" ]
-    }
-  }
-}
-
-core_impl_source_set("impl_for_embedder") {
-  for_shared_library = false
-}
-
-if (is_chromeos || is_linux || is_android || is_win) {
-  core_impl_source_set("impl_for_shared_library") {
-    for_shared_library = true
-  }
-
-  shared_library("shared_library") {
-    output_name = "mojo_core"
-    sources = [
-      "mojo_core.cc",
-    ]
-    defines = [ "MOJO_CORE_SHARED_LIBRARY" ]
-    deps = [
-      ":impl_for_shared_library",
-      "//mojo/public/c/system:headers",
-    ]
-    if (is_win) {
-      inputs = [
-        "mojo_core.def",
-      ]
-      ldflags = [ "/DEF:" + rebase_path("mojo_core.def", root_build_dir) ]
-    } else {
-      configs += [ ":export_only_thunks_api" ]
-    }
-  }
-
-  if (is_chromeos) {
-    if (target_cpu == "arm" || target_cpu == "arm64") {
-      android32_toolchain = "android_clang_arm"
-      android64_toolchain = "android_clang_arm64"
-    } else {
-      android32_toolchain = "android_clang_x86"
-      android64_toolchain = "android_clang_x64"
-    }
-
-    group("shared_libraries_for_arc") {
-      deps = [
-        ":shared_library_arc32",
-        ":shared_library_arc64",
-      ]
-    }
-
-    copy("shared_library_arc32") {
-      sources = [
-        "${root_out_dir}/${android32_toolchain}/libmojo_core.so",
-      ]
-      outputs = [
-        "${root_out_dir}/libmojo_core_arc32.so",
-      ]
-      deps = [
-        ":shared_library(//build/toolchain/android:${android32_toolchain})",
-      ]
-    }
-
-    copy("shared_library_arc64") {
-      sources = [
-        "${root_out_dir}/${android64_toolchain}/libmojo_core.so",
-      ]
-      outputs = [
-        "${root_out_dir}/libmojo_core_arc64.so",
-      ]
-      deps = [
-        ":shared_library(//build/toolchain/android:${android64_toolchain})",
-      ]
-    }
-  }
-
-  config("export_only_thunks_api") {
-    ldflags = [ "-Wl,--version-script=" +
-                rebase_path("//mojo/core/export_only_thunks_api.lst",
-                            root_build_dir) ]
-  }
-
-  if (is_chromeos || is_linux || is_win) {
-    test("mojo_core_unittests") {
-      sources = [
-        "mojo_core_unittest.cc",
-        "run_all_core_unittests.cc",
-      ]
-
-      deps = [
-        "//base",
-        "//base/test:test_support",
-        "//mojo/public/c/system",
-        "//testing/gtest",
-      ]
-
-      data_deps = [
-        ":shared_library",
-      ]
-    }
-  }
-}
-
-source_set("test_utils") {
-  testonly = true
-
-  sources = [
-    "test_utils.cc",
-    "test_utils.h",
-  ]
-
-  public_deps = [
-    "//mojo/public/c/system",
-    "//mojo/public/cpp/system",
-  ]
-
-  deps = [
-    "//base",
-    "//base/test:test_support",
-    "//mojo/core/test:test_support",
-    "//testing/gtest:gtest",
-  ]
-}
-
-source_set("test_sources") {
-  testonly = true
-  sources = [
-    "channel_unittest.cc",
-    "core_test_base.cc",
-    "core_test_base.h",
-    "core_unittest.cc",
-    "embedder_unittest.cc",
-    "handle_table_unittest.cc",
-    "message_pipe_unittest.cc",
-    "message_unittest.cc",
-    "options_validation_unittest.cc",
-    "platform_handle_dispatcher_unittest.cc",
-    "quota_unittest.cc",
-    "shared_buffer_dispatcher_unittest.cc",
-    "shared_buffer_unittest.cc",
-    "signals_unittest.cc",
-    "trap_unittest.cc",
-  ]
-
-  if (!is_ios) {
-    sources += [
-      "data_pipe_unittest.cc",
-      "invitation_unittest.cc",
-      "multiprocess_message_pipe_unittest.cc",
-      "platform_wrapper_unittest.cc",
-    ]
-  }
-
-  deps = [
-    ":test_utils",
-    "//base",
-    "//base/test:test_support",
-    "//mojo/core:embedder_internal",
-    "//mojo/core/embedder",
-    "//mojo/core/ports:tests",
-    "//mojo/core/test:run_all_unittests",
-    "//mojo/core/test:test_support",
-    "//mojo/public/cpp/system",
-    "//testing/gmock",
-    "//testing/gtest",
-  ]
-}
diff --git a/mojo/core/broker_win.cc b/mojo/core/broker_win.cc
deleted file mode 100644
index 3ebc883..0000000
--- a/mojo/core/broker_win.cc
+++ /dev/null
@@ -1,162 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <windows.h>
-
-#include <limits>
-#include <utility>
-
-#include "base/debug/alias.h"
-#include "base/memory/platform_shared_memory_region.h"
-#include "base/numerics/safe_conversions.h"
-#include "base/strings/string_piece.h"
-#include "mojo/core/broker.h"
-#include "mojo/core/broker_messages.h"
-#include "mojo/core/channel.h"
-#include "mojo/core/platform_handle_utils.h"
-#include "mojo/public/cpp/platform/named_platform_channel.h"
-
-namespace mojo {
-namespace core {
-
-namespace {
-
-// 256 bytes should be enough for anyone!
-const size_t kMaxBrokerMessageSize = 256;
-
-bool TakeHandlesFromBrokerMessage(Channel::Message* message,
-                                  size_t num_handles,
-                                  PlatformHandle* out_handles) {
-  if (message->num_handles() != num_handles) {
-    DLOG(ERROR) << "Received unexpected number of handles in broker message";
-    return false;
-  }
-
-  std::vector<PlatformHandleInTransit> handles = message->TakeHandles();
-  DCHECK_EQ(handles.size(), num_handles);
-  DCHECK(out_handles);
-
-  for (size_t i = 0; i < num_handles; ++i)
-    out_handles[i] = handles[i].TakeHandle();
-  return true;
-}
-
-Channel::MessagePtr WaitForBrokerMessage(HANDLE pipe_handle,
-                                         BrokerMessageType expected_type) {
-  char buffer[kMaxBrokerMessageSize];
-  DWORD bytes_read = 0;
-  BOOL result = ::ReadFile(pipe_handle, buffer, kMaxBrokerMessageSize,
-                           &bytes_read, nullptr);
-  if (!result) {
-    // The pipe may be broken if the browser side has been closed, e.g. during
-    // browser shutdown. In that case the ReadFile call will fail and we
-    // shouldn't continue waiting.
-    PLOG(ERROR) << "Error reading broker pipe";
-    return nullptr;
-  }
-
-  Channel::MessagePtr message =
-      Channel::Message::Deserialize(buffer, static_cast<size_t>(bytes_read));
-  if (!message || message->payload_size() < sizeof(BrokerMessageHeader)) {
-    LOG(ERROR) << "Invalid broker message";
-
-    base::debug::Alias(&buffer[0]);
-    base::debug::Alias(&bytes_read);
-    CHECK(false);
-    return nullptr;
-  }
-
-  const BrokerMessageHeader* header =
-      reinterpret_cast<const BrokerMessageHeader*>(message->payload());
-  if (header->type != expected_type) {
-    LOG(ERROR) << "Unexpected broker message type";
-
-    base::debug::Alias(&buffer[0]);
-    base::debug::Alias(&bytes_read);
-    CHECK(false);
-    return nullptr;
-  }
-
-  return message;
-}
-
-}  // namespace
-
-Broker::Broker(PlatformHandle handle) : sync_channel_(std::move(handle)) {
-  CHECK(sync_channel_.is_valid());
-  Channel::MessagePtr message = WaitForBrokerMessage(
-      sync_channel_.GetHandle().Get(), BrokerMessageType::INIT);
-
-  // If we fail to read a message (broken pipe), just return early. The inviter
-  // handle will be null and callers must handle this gracefully.
-  if (!message)
-    return;
-
-  PlatformHandle endpoint_handle;
-  if (TakeHandlesFromBrokerMessage(message.get(), 1, &endpoint_handle)) {
-    inviter_endpoint_ = PlatformChannelEndpoint(std::move(endpoint_handle));
-  } else {
-    // If the message has no handles, we expect it to carry pipe name instead.
-    const BrokerMessageHeader* header =
-        static_cast<const BrokerMessageHeader*>(message->payload());
-    CHECK_GE(message->payload_size(),
-             sizeof(BrokerMessageHeader) + sizeof(InitData));
-    const InitData* data = reinterpret_cast<const InitData*>(header + 1);
-    CHECK_EQ(message->payload_size(),
-             sizeof(BrokerMessageHeader) + sizeof(InitData) +
-                 data->pipe_name_length * sizeof(base::char16));
-    const base::char16* name_data =
-        reinterpret_cast<const base::char16*>(data + 1);
-    CHECK(data->pipe_name_length);
-    inviter_endpoint_ = NamedPlatformChannel::ConnectToServer(
-        base::StringPiece16(name_data, data->pipe_name_length).as_string());
-  }
-}
-
-Broker::~Broker() {}
-
-PlatformChannelEndpoint Broker::GetInviterEndpoint() {
-  return std::move(inviter_endpoint_);
-}
-
-base::WritableSharedMemoryRegion Broker::GetWritableSharedMemoryRegion(
-    size_t num_bytes) {
-  base::AutoLock lock(lock_);
-  BufferRequestData* buffer_request;
-  Channel::MessagePtr out_message = CreateBrokerMessage(
-      BrokerMessageType::BUFFER_REQUEST, 0, 0, &buffer_request);
-  buffer_request->size = base::checked_cast<uint32_t>(num_bytes);
-  DWORD bytes_written = 0;
-  BOOL result =
-      ::WriteFile(sync_channel_.GetHandle().Get(), out_message->data(),
-                  static_cast<DWORD>(out_message->data_num_bytes()),
-                  &bytes_written, nullptr);
-  if (!result ||
-      static_cast<size_t>(bytes_written) != out_message->data_num_bytes()) {
-    PLOG(ERROR) << "Error sending sync broker message";
-    return base::WritableSharedMemoryRegion();
-  }
-
-  PlatformHandle handle;
-  Channel::MessagePtr response = WaitForBrokerMessage(
-      sync_channel_.GetHandle().Get(), BrokerMessageType::BUFFER_RESPONSE);
-  if (response && TakeHandlesFromBrokerMessage(response.get(), 1, &handle)) {
-    BufferResponseData* data;
-    if (!GetBrokerMessageData(response.get(), &data))
-      return base::WritableSharedMemoryRegion();
-    return base::WritableSharedMemoryRegion::Deserialize(
-        base::subtle::PlatformSharedMemoryRegion::Take(
-            CreateSharedMemoryRegionHandleFromPlatformHandles(std::move(handle),
-                                                              PlatformHandle()),
-            base::subtle::PlatformSharedMemoryRegion::Mode::kWritable,
-            num_bytes,
-            base::UnguessableToken::Deserialize(data->guid_high,
-                                                data->guid_low)));
-  }
-
-  return base::WritableSharedMemoryRegion();
-}
-
-}  // namespace core
-}  // namespace mojo
diff --git a/mojo/core/channel_fuchsia.cc b/mojo/core/channel_fuchsia.cc
deleted file mode 100644
index 4386b20..0000000
--- a/mojo/core/channel_fuchsia.cc
+++ /dev/null
@@ -1,466 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "mojo/core/channel.h"
-
-#include <lib/fdio/limits.h>
-#include <lib/fdio/util.h>
-#include <lib/zx/channel.h>
-#include <lib/zx/handle.h>
-#include <zircon/processargs.h>
-#include <zircon/status.h>
-#include <zircon/syscalls.h>
-#include <algorithm>
-
-#include "base/bind.h"
-#include "base/containers/circular_deque.h"
-#include "base/files/scoped_file.h"
-#include "base/fuchsia/fuchsia_logging.h"
-#include "base/location.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/message_loop/message_loop_current.h"
-#include "base/message_loop/message_pump_for_io.h"
-#include "base/stl_util.h"
-#include "base/synchronization/lock.h"
-#include "base/task_runner.h"
-#include "mojo/core/platform_handle_in_transit.h"
-
-namespace mojo {
-namespace core {
-
-namespace {
-
-const size_t kMaxBatchReadCapacity = 256 * 1024;
-
-bool UnwrapPlatformHandle(PlatformHandleInTransit handle,
-                          Channel::Message::HandleInfoEntry* info_out,
-                          std::vector<PlatformHandleInTransit>* handles_out) {
-  DCHECK(handle.handle().is_valid());
-
-  if (!handle.handle().is_valid_fd()) {
-    *info_out = {0u, 0u};
-    handles_out->emplace_back(std::move(handle));
-    return true;
-  }
-
-  // Each FDIO file descriptor is implemented using one or more native resources
-  // and can be un-wrapped into a set of |handle| and |info| pairs, with |info|
-  // consisting of an FDIO-defined type & arguments (see zircon/processargs.h).
-  //
-  // We try to transfer the FD, but if that fails (for example if the file has
-  // already been dup()d into another FD) we may need to clone.
-  zx_handle_t handles[FDIO_MAX_HANDLES] = {};
-  uint32_t info[FDIO_MAX_HANDLES] = {};
-  zx_status_t result =
-      fdio_transfer_fd(handle.handle().GetFD().get(), 0, handles, info);
-  if (result > 0) {
-    // On success, the fd in |handle| has been transferred and is no longer
-    // valid. Release from the PlatformHandle to avoid close()ing an invalid
-    // an invalid handle.
-    handle.CompleteTransit();
-  } else if (result == ZX_ERR_UNAVAILABLE) {
-    // No luck, try cloning instead.
-    result = fdio_clone_fd(handle.handle().GetFD().get(), 0, handles, info);
-  }
-
-  if (result <= 0) {
-    ZX_DLOG(ERROR, result) << "fdio_transfer_fd("
-                           << handle.handle().GetFD().get() << ")";
-    return false;
-  }
-  DCHECK_LE(result, FDIO_MAX_HANDLES);
-
-  // We assume here that only the |PA_HND_TYPE| of the |info| really matters,
-  // and that that is the same for all the underlying handles.
-  *info_out = {PA_HND_TYPE(info[0]), result};
-  for (int i = 0; i < result; ++i) {
-    DCHECK_EQ(PA_HND_TYPE(info[0]), PA_HND_TYPE(info[i]));
-    DCHECK_EQ(0u, PA_HND_SUBTYPE(info[i]));
-    handles_out->emplace_back(
-        PlatformHandleInTransit(PlatformHandle(zx::handle(handles[i]))));
-  }
-
-  return true;
-}
-
-PlatformHandle WrapPlatformHandles(Channel::Message::HandleInfoEntry info,
-                                   base::circular_deque<zx::handle>* handles) {
-  PlatformHandle out_handle;
-  if (!info.type) {
-    out_handle = PlatformHandle(std::move(handles->front()));
-    handles->pop_front();
-  } else {
-    if (info.count > FDIO_MAX_HANDLES)
-      return PlatformHandle();
-
-    // Fetch the required number of handles from |handles| and set up type info.
-    zx_handle_t fd_handles[FDIO_MAX_HANDLES] = {};
-    uint32_t fd_infos[FDIO_MAX_HANDLES] = {};
-    for (int i = 0; i < info.count; ++i) {
-      fd_handles[i] = (*handles)[i].get();
-      fd_infos[i] = PA_HND(info.type, 0);
-    }
-
-    // Try to wrap the handles into an FDIO file descriptor.
-    base::ScopedFD out_fd;
-    zx_status_t result =
-        fdio_create_fd(fd_handles, fd_infos, info.count, out_fd.receive());
-    if (result != ZX_OK) {
-      ZX_DLOG(ERROR, result) << "fdio_create_fd";
-      return PlatformHandle();
-    }
-
-    // The handles are owned by FDIO now, so |release()| them before removing
-    // the entries from |handles|.
-    for (int i = 0; i < info.count; ++i) {
-      ignore_result(handles->front().release());
-      handles->pop_front();
-    }
-
-    out_handle = PlatformHandle(std::move(out_fd));
-  }
-  return out_handle;
-}
-
-// A view over a Channel::Message object. The write queue uses these since
-// large messages may need to be sent in chunks.
-class MessageView {
- public:
-  // Owns |message|. |offset| indexes the first unsent byte in the message.
-  MessageView(Channel::MessagePtr message, size_t offset)
-      : message_(std::move(message)),
-        offset_(offset),
-        handles_(message_->TakeHandlesForTransport()) {
-    DCHECK_GT(message_->data_num_bytes(), offset_);
-  }
-
-  MessageView(MessageView&& other) { *this = std::move(other); }
-
-  MessageView& operator=(MessageView&& other) {
-    message_ = std::move(other.message_);
-    offset_ = other.offset_;
-    handles_ = std::move(other.handles_);
-    return *this;
-  }
-
-  ~MessageView() {}
-
-  const void* data() const {
-    return static_cast<const char*>(message_->data()) + offset_;
-  }
-
-  size_t data_num_bytes() const { return message_->data_num_bytes() - offset_; }
-
-  size_t data_offset() const { return offset_; }
-  void advance_data_offset(size_t num_bytes) {
-    DCHECK_GT(message_->data_num_bytes(), offset_ + num_bytes);
-    offset_ += num_bytes;
-  }
-
-  std::vector<PlatformHandleInTransit> TakeHandles() {
-    if (handles_.empty())
-      return std::vector<PlatformHandleInTransit>();
-
-    // We can only pass Fuchsia handles via IPC, so unwrap any FDIO file-
-    // descriptors in |handles_| into the underlying handles, and serialize the
-    // metadata, if any, into the extra header.
-    auto* handles_info = reinterpret_cast<Channel::Message::HandleInfoEntry*>(
-        message_->mutable_extra_header());
-    memset(handles_info, 0, message_->extra_header_size());
-
-    std::vector<PlatformHandleInTransit> in_handles = std::move(handles_);
-    handles_.reserve(in_handles.size());
-    for (size_t i = 0; i < in_handles.size(); i++) {
-      if (!UnwrapPlatformHandle(std::move(in_handles[i]), &handles_info[i],
-                                &handles_))
-        return std::vector<PlatformHandleInTransit>();
-    }
-    return std::move(handles_);
-  }
-
- private:
-  Channel::MessagePtr message_;
-  size_t offset_;
-  std::vector<PlatformHandleInTransit> handles_;
-
-  DISALLOW_COPY_AND_ASSIGN(MessageView);
-};
-
-class ChannelFuchsia : public Channel,
-                       public base::MessageLoopCurrent::DestructionObserver,
-                       public base::MessagePumpForIO::ZxHandleWatcher {
- public:
-  ChannelFuchsia(Delegate* delegate,
-                 ConnectionParams connection_params,
-                 scoped_refptr<base::TaskRunner> io_task_runner)
-      : Channel(delegate),
-        self_(this),
-        handle_(
-            connection_params.TakeEndpoint().TakePlatformHandle().TakeHandle()),
-        io_task_runner_(io_task_runner) {
-    CHECK(handle_.is_valid());
-  }
-
-  void Start() override {
-    if (io_task_runner_->RunsTasksInCurrentSequence()) {
-      StartOnIOThread();
-    } else {
-      io_task_runner_->PostTask(
-          FROM_HERE, base::BindOnce(&ChannelFuchsia::StartOnIOThread, this));
-    }
-  }
-
-  void ShutDownImpl() override {
-    // Always shut down asynchronously when called through the public interface.
-    io_task_runner_->PostTask(
-        FROM_HERE, base::BindOnce(&ChannelFuchsia::ShutDownOnIOThread, this));
-  }
-
-  void Write(MessagePtr message) override {
-    bool write_error = false;
-    {
-      base::AutoLock lock(write_lock_);
-      if (reject_writes_)
-        return;
-      if (!WriteNoLock(MessageView(std::move(message), 0)))
-        reject_writes_ = write_error = true;
-    }
-    if (write_error) {
-      // Do not synchronously invoke OnWriteError(). Write() may have been
-      // called by the delegate and we don't want to re-enter it.
-      io_task_runner_->PostTask(
-          FROM_HERE, base::BindOnce(&ChannelFuchsia::OnWriteError, this,
-                                    Error::kDisconnected));
-    }
-  }
-
-  void LeakHandle() override {
-    DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
-    leak_handle_ = true;
-  }
-
-  bool GetReadPlatformHandles(const void* payload,
-                              size_t payload_size,
-                              size_t num_handles,
-                              const void* extra_header,
-                              size_t extra_header_size,
-                              std::vector<PlatformHandle>* handles,
-                              bool* deferred) override {
-    DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
-    if (num_handles > std::numeric_limits<uint16_t>::max())
-      return false;
-
-    // Locate the handle info and verify there is enough of it.
-    if (!extra_header)
-      return false;
-    const auto* handles_info =
-        reinterpret_cast<const Channel::Message::HandleInfoEntry*>(
-            extra_header);
-    size_t handles_info_size = sizeof(handles_info[0]) * num_handles;
-    if (handles_info_size > extra_header_size)
-      return false;
-
-    // Some caller-supplied handles may be FDIO file-descriptors, which were
-    // un-wrapped to more than one native platform resource handle for transfer.
-    // We may therefore need to expect more than |num_handles| handles to have
-    // been accumulated in |incoming_handles_|, based on the handle info.
-    size_t num_raw_handles = 0u;
-    for (size_t i = 0; i < num_handles; ++i)
-      num_raw_handles += handles_info[i].type ? handles_info[i].count : 1;
-
-    // If there are too few handles then we're not ready yet, so return true
-    // indicating things are OK, but leave |handles| empty.
-    if (incoming_handles_.size() < num_raw_handles)
-      return true;
-
-    handles->reserve(num_handles);
-    for (size_t i = 0; i < num_handles; ++i) {
-      handles->emplace_back(
-          WrapPlatformHandles(handles_info[i], &incoming_handles_));
-    }
-    return true;
-  }
-
- private:
-  ~ChannelFuchsia() override { DCHECK(!read_watch_); }
-
-  void StartOnIOThread() {
-    DCHECK(!read_watch_);
-
-    base::MessageLoopCurrent::Get()->AddDestructionObserver(this);
-
-    read_watch_.reset(
-        new base::MessagePumpForIO::ZxHandleWatchController(FROM_HERE));
-    base::MessageLoopCurrentForIO::Get()->WatchZxHandle(
-        handle_.get(), true /* persistent */,
-        ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED, read_watch_.get(), this);
-  }
-
-  void ShutDownOnIOThread() {
-    base::MessageLoopCurrent::Get()->RemoveDestructionObserver(this);
-
-    read_watch_.reset();
-    if (leak_handle_)
-      ignore_result(handle_.release());
-    handle_.reset();
-
-    // May destroy the |this| if it was the last reference.
-    self_ = nullptr;
-  }
-
-  // base::MessageLoopCurrent::DestructionObserver:
-  void WillDestroyCurrentMessageLoop() override {
-    DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
-    if (self_)
-      ShutDownOnIOThread();
-  }
-
-  // base::MessagePumpForIO::ZxHandleWatcher:
-  void OnZxHandleSignalled(zx_handle_t handle, zx_signals_t signals) override {
-    DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
-    CHECK_EQ(handle, handle_.get());
-    DCHECK((ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED) & signals);
-
-    // We always try to read message(s), even if ZX_CHANNEL_PEER_CLOSED, since
-    // the peer may have closed while messages were still unread, in the pipe.
-
-    bool validation_error = false;
-    bool read_error = false;
-    size_t next_read_size = 0;
-    size_t buffer_capacity = 0;
-    size_t total_bytes_read = 0;
-    do {
-      buffer_capacity = next_read_size;
-      char* buffer = GetReadBuffer(&buffer_capacity);
-      DCHECK_GT(buffer_capacity, 0u);
-
-      uint32_t bytes_read = 0;
-      uint32_t handles_read = 0;
-      zx_handle_t handles[ZX_CHANNEL_MAX_MSG_HANDLES] = {};
-
-      zx_status_t read_result =
-          handle_.read(0, buffer, buffer_capacity, &bytes_read, handles,
-                       base::size(handles), &handles_read);
-      if (read_result == ZX_OK) {
-        for (size_t i = 0; i < handles_read; ++i) {
-          incoming_handles_.emplace_back(handles[i]);
-        }
-        total_bytes_read += bytes_read;
-        if (!OnReadComplete(bytes_read, &next_read_size)) {
-          read_error = true;
-          validation_error = true;
-          break;
-        }
-      } else if (read_result == ZX_ERR_BUFFER_TOO_SMALL) {
-        DCHECK_LE(handles_read, base::size(handles));
-        next_read_size = bytes_read;
-      } else if (read_result == ZX_ERR_SHOULD_WAIT) {
-        break;
-      } else {
-        ZX_DLOG_IF(ERROR, read_result != ZX_ERR_PEER_CLOSED, read_result)
-            << "zx_channel_read";
-        read_error = true;
-        break;
-      }
-    } while (total_bytes_read < kMaxBatchReadCapacity && next_read_size > 0);
-    if (read_error) {
-      // Stop receiving read notifications.
-      read_watch_.reset();
-      if (validation_error)
-        OnError(Error::kReceivedMalformedData);
-      else
-        OnError(Error::kDisconnected);
-    }
-  }
-
-  // Attempts to write a message directly to the channel. If the full message
-  // cannot be written, it's queued and a wait is initiated to write the message
-  // ASAP on the I/O thread.
-  bool WriteNoLock(MessageView message_view) {
-    uint32_t write_bytes = 0;
-    do {
-      message_view.advance_data_offset(write_bytes);
-
-      std::vector<PlatformHandleInTransit> outgoing_handles =
-          message_view.TakeHandles();
-      zx_handle_t handles[ZX_CHANNEL_MAX_MSG_HANDLES] = {};
-      size_t handles_count = outgoing_handles.size();
-
-      DCHECK_LE(handles_count, base::size(handles));
-      for (size_t i = 0; i < handles_count; ++i) {
-        DCHECK(outgoing_handles[i].handle().is_valid());
-        handles[i] = outgoing_handles[i].handle().GetHandle().get();
-      }
-
-      write_bytes = std::min(message_view.data_num_bytes(),
-                             static_cast<size_t>(ZX_CHANNEL_MAX_MSG_BYTES));
-      zx_status_t result = handle_.write(0, message_view.data(), write_bytes,
-                                         handles, handles_count);
-      // zx_channel_write() consumes |handles| whether or not it succeeds, so
-      // release() our copies now, to avoid them being double-closed.
-      for (auto& outgoing_handle : outgoing_handles)
-        outgoing_handle.CompleteTransit();
-
-      if (result != ZX_OK) {
-        // TODO(fuchsia): Handle ZX_ERR_SHOULD_WAIT flow-control errors, once
-        // the platform starts generating them. See https://crbug.com/754084.
-        ZX_DLOG_IF(ERROR, result != ZX_ERR_PEER_CLOSED, result)
-            << "WriteNoLock(zx_channel_write)";
-        return false;
-      }
-
-    } while (write_bytes < message_view.data_num_bytes());
-
-    return true;
-  }
-
-  void OnWriteError(Error error) {
-    DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
-    DCHECK(reject_writes_);
-
-    if (error == Error::kDisconnected) {
-      // If we can't write because the pipe is disconnected then continue
-      // reading to fetch any in-flight messages, relying on end-of-stream to
-      // signal the actual disconnection.
-      if (read_watch_) {
-        // TODO: When we add flow-control for writes, we also need to reset the
-        // write-watcher here.
-        return;
-      }
-    }
-
-    OnError(error);
-  }
-
-  // Keeps the Channel alive at least until explicit shutdown on the IO thread.
-  scoped_refptr<Channel> self_;
-
-  zx::channel handle_;
-  scoped_refptr<base::TaskRunner> io_task_runner_;
-
-  // These members are only used on the IO thread.
-  std::unique_ptr<base::MessagePumpForIO::ZxHandleWatchController> read_watch_;
-  base::circular_deque<zx::handle> incoming_handles_;
-  bool leak_handle_ = false;
-
-  base::Lock write_lock_;
-  bool reject_writes_ = false;
-
-  DISALLOW_COPY_AND_ASSIGN(ChannelFuchsia);
-};
-
-}  // namespace
-
-// static
-scoped_refptr<Channel> Channel::Create(
-    Delegate* delegate,
-    ConnectionParams connection_params,
-    scoped_refptr<base::TaskRunner> io_task_runner) {
-  return new ChannelFuchsia(delegate, std::move(connection_params),
-                            std::move(io_task_runner));
-}
-
-}  // namespace core
-}  // namespace mojo
diff --git a/mojo/core/channel_win.cc b/mojo/core/channel_win.cc
deleted file mode 100644
index 30a1486..0000000
--- a/mojo/core/channel_win.cc
+++ /dev/null
@@ -1,377 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "mojo/core/channel.h"
-
-#include <stdint.h>
-#include <windows.h>
-
-#include <algorithm>
-#include <limits>
-#include <memory>
-
-#include "base/bind.h"
-#include "base/containers/queue.h"
-#include "base/location.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/message_loop/message_loop_current.h"
-#include "base/message_loop/message_pump_for_io.h"
-#include "base/process/process_handle.h"
-#include "base/synchronization/lock.h"
-#include "base/task_runner.h"
-#include "base/win/scoped_handle.h"
-#include "base/win/win_util.h"
-
-namespace mojo {
-namespace core {
-
-namespace {
-
-class ChannelWin : public Channel,
-                   public base::MessageLoopCurrent::DestructionObserver,
-                   public base::MessagePumpForIO::IOHandler {
- public:
-  ChannelWin(Delegate* delegate,
-             ConnectionParams connection_params,
-             scoped_refptr<base::TaskRunner> io_task_runner)
-      : Channel(delegate), self_(this), io_task_runner_(io_task_runner) {
-    if (connection_params.server_endpoint().is_valid()) {
-      handle_ = connection_params.TakeServerEndpoint()
-                    .TakePlatformHandle()
-                    .TakeHandle();
-      needs_connection_ = true;
-    } else {
-      handle_ =
-          connection_params.TakeEndpoint().TakePlatformHandle().TakeHandle();
-    }
-
-    CHECK(handle_.IsValid());
-  }
-
-  void Start() override {
-    io_task_runner_->PostTask(
-        FROM_HERE, base::BindOnce(&ChannelWin::StartOnIOThread, this));
-  }
-
-  void ShutDownImpl() override {
-    // Always shut down asynchronously when called through the public interface.
-    io_task_runner_->PostTask(
-        FROM_HERE, base::BindOnce(&ChannelWin::ShutDownOnIOThread, this));
-  }
-
-  void Write(MessagePtr message) override {
-    if (remote_process().is_valid()) {
-      // If we know the remote process handle, we transfer all outgoing handles
-      // to the process now rewriting them in the message.
-      std::vector<PlatformHandleInTransit> handles = message->TakeHandles();
-      for (auto& handle : handles) {
-        if (handle.handle().is_valid())
-          handle.TransferToProcess(remote_process().Clone());
-      }
-      message->SetHandles(std::move(handles));
-    }
-
-    bool write_error = false;
-    {
-      base::AutoLock lock(write_lock_);
-      if (reject_writes_)
-        return;
-
-      bool write_now = !delay_writes_ && outgoing_messages_.empty();
-      outgoing_messages_.emplace_back(std::move(message));
-      if (write_now && !WriteNoLock(outgoing_messages_.front()))
-        reject_writes_ = write_error = true;
-    }
-    if (write_error) {
-      // Do not synchronously invoke OnWriteError(). Write() may have been
-      // called by the delegate and we don't want to re-enter it.
-      io_task_runner_->PostTask(FROM_HERE,
-                                base::BindOnce(&ChannelWin::OnWriteError, this,
-                                               Error::kDisconnected));
-    }
-  }
-
-  void LeakHandle() override {
-    DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
-    leak_handle_ = true;
-  }
-
-  bool GetReadPlatformHandles(const void* payload,
-                              size_t payload_size,
-                              size_t num_handles,
-                              const void* extra_header,
-                              size_t extra_header_size,
-                              std::vector<PlatformHandle>* handles,
-                              bool* deferred) override {
-    DCHECK(extra_header);
-    if (num_handles > std::numeric_limits<uint16_t>::max())
-      return false;
-    using HandleEntry = Channel::Message::HandleEntry;
-    size_t handles_size = sizeof(HandleEntry) * num_handles;
-    if (handles_size > extra_header_size)
-      return false;
-    handles->reserve(num_handles);
-    const HandleEntry* extra_header_handles =
-        reinterpret_cast<const HandleEntry*>(extra_header);
-    for (size_t i = 0; i < num_handles; i++) {
-      HANDLE handle_value =
-          base::win::Uint32ToHandle(extra_header_handles[i].handle);
-      if (remote_process().is_valid()) {
-        // If we know the remote process's handle, we assume it doesn't know
-        // ours; that means any handle values still belong to that process, and
-        // we need to transfer them to this process.
-        handle_value = PlatformHandleInTransit::TakeIncomingRemoteHandle(
-                           handle_value, remote_process().get())
-                           .ReleaseHandle();
-      }
-      handles->emplace_back(base::win::ScopedHandle(std::move(handle_value)));
-    }
-    return true;
-  }
-
- private:
-  // May run on any thread.
-  ~ChannelWin() override {}
-
-  void StartOnIOThread() {
-    base::MessageLoopCurrent::Get()->AddDestructionObserver(this);
-    base::MessageLoopCurrentForIO::Get()->RegisterIOHandler(handle_.Get(),
-                                                            this);
-
-    if (needs_connection_) {
-      BOOL ok = ::ConnectNamedPipe(handle_.Get(), &connect_context_.overlapped);
-      if (ok) {
-        PLOG(ERROR) << "Unexpected success while waiting for pipe connection";
-        OnError(Error::kConnectionFailed);
-        return;
-      }
-
-      const DWORD err = GetLastError();
-      switch (err) {
-        case ERROR_PIPE_CONNECTED:
-          break;
-        case ERROR_IO_PENDING:
-          is_connect_pending_ = true;
-          AddRef();
-          return;
-        case ERROR_NO_DATA:
-        default:
-          OnError(Error::kConnectionFailed);
-          return;
-      }
-    }
-
-    // Now that we have registered our IOHandler, we can start writing.
-    {
-      base::AutoLock lock(write_lock_);
-      if (delay_writes_) {
-        delay_writes_ = false;
-        WriteNextNoLock();
-      }
-    }
-
-    // Keep this alive in case we synchronously run shutdown, via OnError(),
-    // as a result of a ReadFile() failure on the channel.
-    scoped_refptr<ChannelWin> keep_alive(this);
-    ReadMore(0);
-  }
-
-  void ShutDownOnIOThread() {
-    base::MessageLoopCurrent::Get()->RemoveDestructionObserver(this);
-
-    // TODO(https://crbug.com/583525): This function is expected to be called
-    // once, and |handle_| should be valid at this point.
-    CHECK(handle_.IsValid());
-    CancelIo(handle_.Get());
-    if (leak_handle_)
-      ignore_result(handle_.Take());
-    else
-      handle_.Close();
-
-    // Allow |this| to be destroyed as soon as no IO is pending.
-    self_ = nullptr;
-  }
-
-  // base::MessageLoopCurrent::DestructionObserver:
-  void WillDestroyCurrentMessageLoop() override {
-    DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
-    if (self_)
-      ShutDownOnIOThread();
-  }
-
-  // base::MessageLoop::IOHandler:
-  void OnIOCompleted(base::MessagePumpForIO::IOContext* context,
-                     DWORD bytes_transfered,
-                     DWORD error) override {
-    if (error != ERROR_SUCCESS) {
-      if (context == &write_context_) {
-        {
-          base::AutoLock lock(write_lock_);
-          reject_writes_ = true;
-        }
-        OnWriteError(Error::kDisconnected);
-      } else {
-        OnError(Error::kDisconnected);
-      }
-    } else if (context == &connect_context_) {
-      DCHECK(is_connect_pending_);
-      is_connect_pending_ = false;
-      ReadMore(0);
-
-      base::AutoLock lock(write_lock_);
-      if (delay_writes_) {
-        delay_writes_ = false;
-        WriteNextNoLock();
-      }
-    } else if (context == &read_context_) {
-      OnReadDone(static_cast<size_t>(bytes_transfered));
-    } else {
-      CHECK(context == &write_context_);
-      OnWriteDone(static_cast<size_t>(bytes_transfered));
-    }
-    Release();
-  }
-
-  void OnReadDone(size_t bytes_read) {
-    DCHECK(is_read_pending_);
-    is_read_pending_ = false;
-
-    if (bytes_read > 0) {
-      size_t next_read_size = 0;
-      if (OnReadComplete(bytes_read, &next_read_size)) {
-        ReadMore(next_read_size);
-      } else {
-        OnError(Error::kReceivedMalformedData);
-      }
-    } else if (bytes_read == 0) {
-      OnError(Error::kDisconnected);
-    }
-  }
-
-  void OnWriteDone(size_t bytes_written) {
-    if (bytes_written == 0)
-      return;
-
-    bool write_error = false;
-    {
-      base::AutoLock lock(write_lock_);
-
-      DCHECK(is_write_pending_);
-      is_write_pending_ = false;
-      DCHECK(!outgoing_messages_.empty());
-
-      Channel::MessagePtr message = std::move(outgoing_messages_.front());
-      outgoing_messages_.pop_front();
-
-      // Invalidate all the scoped handles so we don't attempt to close them.
-      std::vector<PlatformHandleInTransit> handles = message->TakeHandles();
-      for (auto& handle : handles)
-        handle.CompleteTransit();
-
-      // Overlapped WriteFile() to a pipe should always fully complete.
-      if (message->data_num_bytes() != bytes_written)
-        reject_writes_ = write_error = true;
-      else if (!WriteNextNoLock())
-        reject_writes_ = write_error = true;
-    }
-    if (write_error)
-      OnWriteError(Error::kDisconnected);
-  }
-
-  void ReadMore(size_t next_read_size_hint) {
-    DCHECK(!is_read_pending_);
-
-    size_t buffer_capacity = next_read_size_hint;
-    char* buffer = GetReadBuffer(&buffer_capacity);
-    DCHECK_GT(buffer_capacity, 0u);
-
-    BOOL ok =
-        ::ReadFile(handle_.Get(), buffer, static_cast<DWORD>(buffer_capacity),
-                   NULL, &read_context_.overlapped);
-    if (ok || GetLastError() == ERROR_IO_PENDING) {
-      is_read_pending_ = true;
-      AddRef();
-    } else {
-      OnError(Error::kDisconnected);
-    }
-  }
-
-  // Attempts to write a message directly to the channel. If the full message
-  // cannot be written, it's queued and a wait is initiated to write the message
-  // ASAP on the I/O thread.
-  bool WriteNoLock(const Channel::MessagePtr& message) {
-    BOOL ok = WriteFile(handle_.Get(), message->data(),
-                        static_cast<DWORD>(message->data_num_bytes()), NULL,
-                        &write_context_.overlapped);
-    if (ok || GetLastError() == ERROR_IO_PENDING) {
-      is_write_pending_ = true;
-      AddRef();
-      return true;
-    }
-    return false;
-  }
-
-  bool WriteNextNoLock() {
-    if (outgoing_messages_.empty())
-      return true;
-    return WriteNoLock(outgoing_messages_.front());
-  }
-
-  void OnWriteError(Error error) {
-    DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
-    DCHECK(reject_writes_);
-
-    if (error == Error::kDisconnected) {
-      // If we can't write because the pipe is disconnected then continue
-      // reading to fetch any in-flight messages, relying on end-of-stream to
-      // signal the actual disconnection.
-      if (is_read_pending_ || is_connect_pending_)
-        return;
-    }
-
-    OnError(error);
-  }
-
-  // Keeps the Channel alive at least until explicit shutdown on the IO thread.
-  scoped_refptr<Channel> self_;
-
-  // The pipe handle this Channel uses for communication.
-  base::win::ScopedHandle handle_;
-
-  // Indicates whether |handle_| must wait for a connection.
-  bool needs_connection_ = false;
-
-  const scoped_refptr<base::TaskRunner> io_task_runner_;
-
-  base::MessagePumpForIO::IOContext connect_context_;
-  base::MessagePumpForIO::IOContext read_context_;
-  bool is_connect_pending_ = false;
-  bool is_read_pending_ = false;
-
-  // Protects all fields potentially accessed on multiple threads via Write().
-  base::Lock write_lock_;
-  base::MessagePumpForIO::IOContext write_context_;
-  base::circular_deque<Channel::MessagePtr> outgoing_messages_;
-  bool delay_writes_ = true;
-  bool reject_writes_ = false;
-  bool is_write_pending_ = false;
-
-  bool leak_handle_ = false;
-
-  DISALLOW_COPY_AND_ASSIGN(ChannelWin);
-};
-
-}  // namespace
-
-// static
-scoped_refptr<Channel> Channel::Create(
-    Delegate* delegate,
-    ConnectionParams connection_params,
-    scoped_refptr<base::TaskRunner> io_task_runner) {
-  return new ChannelWin(delegate, std::move(connection_params), io_task_runner);
-}
-
-}  // namespace core
-}  // namespace mojo
diff --git a/mojo/core/embedder/BUILD.gn b/mojo/core/embedder/BUILD.gn
deleted file mode 100644
index 47f1c39..0000000
--- a/mojo/core/embedder/BUILD.gn
+++ /dev/null
@@ -1,29 +0,0 @@
-# Copyright 2018 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-component("embedder") {
-  output_name = "mojo_core_embedder"
-
-  public = [
-    "configuration.h",
-    "embedder.h",
-    "scoped_ipc_support.h",
-  ]
-
-  sources = [
-    "embedder.cc",
-    "scoped_ipc_support.cc",
-  ]
-
-  defines = [ "IS_MOJO_CORE_EMBEDDER_IMPL" ]
-
-  public_deps = [
-    "//base",
-  ]
-
-  deps = [
-    "//mojo/core:embedder_internal",
-    "//mojo/public/c/system",
-  ]
-}
diff --git a/mojo/core/ports/BUILD.gn b/mojo/core/ports/BUILD.gn
deleted file mode 100644
index 68dce3f..0000000
--- a/mojo/core/ports/BUILD.gn
+++ /dev/null
@@ -1,59 +0,0 @@
-# Copyright 2016 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//testing/test.gni")
-
-component("ports") {
-  output_name = "mojo_core_ports"
-
-  sources = [
-    "event.cc",
-    "event.h",
-    "message_filter.h",
-    "message_queue.cc",
-    "message_queue.h",
-    "name.cc",
-    "name.h",
-    "node.cc",
-    "node.h",
-    "node_delegate.h",
-    "port.cc",
-    "port.h",
-    "port_locker.cc",
-    "port_locker.h",
-    "port_ref.cc",
-    "port_ref.h",
-    "user_data.h",
-    "user_message.cc",
-    "user_message.h",
-  ]
-
-  defines = [ "IS_MOJO_CORE_PORTS_IMPL" ]
-
-  public_deps = [
-    "//base",
-  ]
-
-  if (!is_nacl) {
-    deps = [
-      "//crypto",
-    ]
-  }
-}
-
-source_set("tests") {
-  testonly = true
-
-  sources = [
-    "name_unittest.cc",
-    "ports_unittest.cc",
-  ]
-
-  deps = [
-    ":ports",
-    "//base",
-    "//base/test:test_support",
-    "//testing/gtest",
-  ]
-}
diff --git a/mojo/core/test/BUILD.gn b/mojo/core/test/BUILD.gn
deleted file mode 100644
index 6fad6fe..0000000
--- a/mojo/core/test/BUILD.gn
+++ /dev/null
@@ -1,88 +0,0 @@
-# Copyright 2014 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-static_library("test_support") {
-  testonly = true
-  sources = [
-    "mojo_test_base.cc",
-    "mojo_test_base.h",
-    "test_utils.h",
-    "test_utils_win.cc",
-  ]
-
-  if (!is_ios) {
-    sources += [
-      "multiprocess_test_helper.cc",
-      "multiprocess_test_helper.h",
-    ]
-  }
-
-  if (is_fuchsia || is_posix) {
-    sources += [ "test_utils.cc" ]
-  }
-
-  public_deps = [
-    "//base",
-    "//base/test:test_support",
-    "//mojo/core/embedder",
-    "//mojo/public/cpp/system",
-    "//testing/gtest",
-  ]
-}
-
-source_set("run_all_unittests") {
-  testonly = true
-  sources = [
-    "run_all_unittests.cc",
-  ]
-
-  deps = [
-    ":test_support",
-    ":test_support_impl",
-    "//base",
-    "//base/test:test_support",
-    "//mojo/core/embedder",
-    "//mojo/public/c/test_support",
-    "//testing/gtest",
-  ]
-
-  if (is_linux && !is_component_build) {
-    public_configs = [ "//build/config/gcc:rpath_for_built_shared_libraries" ]
-  }
-}
-
-source_set("run_all_perftests") {
-  testonly = true
-  deps = [
-    ":test_support_impl",
-    "//base",
-    "//base/test:test_support",
-    "//mojo/core/embedder",
-    "//mojo/core/test:test_support",
-    "//mojo/public/c/test_support",
-  ]
-
-  sources = [
-    "run_all_perftests.cc",
-  ]
-
-  if (is_linux && !is_component_build) {
-    public_configs = [ "//build/config/gcc:rpath_for_built_shared_libraries" ]
-  }
-}
-
-static_library("test_support_impl") {
-  testonly = true
-  deps = [
-    "//base",
-    "//base/test:test_support",
-    "//mojo/public/c/test_support",
-    "//mojo/public/cpp/system",
-  ]
-
-  sources = [
-    "test_support_impl.cc",
-    "test_support_impl.h",
-  ]
-}
diff --git a/mojo/core/test/test_utils_win.cc b/mojo/core/test/test_utils_win.cc
deleted file mode 100644
index 9adfb7a..0000000
--- a/mojo/core/test/test_utils_win.cc
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "mojo/core/test/test_utils.h"
-
-#include <fcntl.h>
-#include <io.h>
-#include <stddef.h>
-#include <string.h>
-#include <windows.h>
-
-namespace mojo {
-namespace core {
-namespace test {
-
-PlatformHandle PlatformHandleFromFILE(base::ScopedFILE fp) {
-  CHECK(fp);
-
-  HANDLE rv = INVALID_HANDLE_VALUE;
-  PCHECK(DuplicateHandle(
-      GetCurrentProcess(),
-      reinterpret_cast<HANDLE>(_get_osfhandle(_fileno(fp.get()))),
-      GetCurrentProcess(), &rv, 0, TRUE, DUPLICATE_SAME_ACCESS))
-      << "DuplicateHandle";
-  return PlatformHandle(base::win::ScopedHandle(rv));
-}
-
-base::ScopedFILE FILEFromPlatformHandle(PlatformHandle h, const char* mode) {
-  CHECK(h.is_valid());
-  // Microsoft's documentation for |_open_osfhandle()| only discusses these
-  // flags (and |_O_WTEXT|). Hmmm.
-  int flags = 0;
-  if (strchr(mode, 'a'))
-    flags |= _O_APPEND;
-  if (strchr(mode, 'r'))
-    flags |= _O_RDONLY;
-  if (strchr(mode, 't'))
-    flags |= _O_TEXT;
-  base::ScopedFILE rv(_fdopen(
-      _open_osfhandle(reinterpret_cast<intptr_t>(h.ReleaseHandle()), flags),
-      mode));
-  PCHECK(rv) << "_fdopen";
-  return rv;
-}
-
-}  // namespace test
-}  // namespace core
-}  // namespace mojo
diff --git a/mojo/public/BUILD.gn b/mojo/public/BUILD.gn
deleted file mode 100644
index bd094c8..0000000
--- a/mojo/public/BUILD.gn
+++ /dev/null
@@ -1,33 +0,0 @@
-# Copyright 2014 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-group("public") {
-  # Meta-target, don't link into production code.
-  testonly = true
-  deps = [
-    ":sdk",
-    "cpp/bindings",
-    "interfaces/bindings/tests:test_interfaces",
-  ]
-
-  if (is_android) {
-    deps += [
-      "java:bindings_java",
-      "java:system_java",
-    ]
-  }
-}
-
-group("sdk") {
-  deps = [
-    "c/system",
-    "cpp/bindings",
-  ]
-}
-
-group("fuzzers") {
-  deps = [
-    "tools/fuzzers",
-  ]
-}
diff --git a/mojo/public/c/system/BUILD.gn b/mojo/public/c/system/BUILD.gn
deleted file mode 100644
index 6cc2b02..0000000
--- a/mojo/public/c/system/BUILD.gn
+++ /dev/null
@@ -1,38 +0,0 @@
-# Copyright 2014 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-component("system") {
-  output_name = "mojo_public_system"
-
-  sources = [
-    "thunks.cc",
-  ]
-
-  defines = [ "MOJO_SYSTEM_IMPLEMENTATION" ]
-
-  public_deps = [
-    ":headers",
-  ]
-
-  deps = [
-    "//base",
-  ]
-}
-
-source_set("headers") {
-  public = [
-    "buffer.h",
-    "core.h",
-    "data_pipe.h",
-    "functions.h",
-    "invitation.h",
-    "macros.h",
-    "message_pipe.h",
-    "platform_handle.h",
-    "system_export.h",
-    "thunks.h",
-    "trap.h",
-    "types.h",
-  ]
-}
diff --git a/mojo/public/c/system/tests/BUILD.gn b/mojo/public/c/system/tests/BUILD.gn
deleted file mode 100644
index f116cc6..0000000
--- a/mojo/public/c/system/tests/BUILD.gn
+++ /dev/null
@@ -1,38 +0,0 @@
-# Copyright 2014 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-source_set("tests") {
-  testonly = true
-
-  visibility = [
-    "//mojo/public/cpp/system/tests:mojo_unittests",
-    "//mojo/public/cpp/system/tests:tests",
-  ]
-
-  sources = [
-    "core_api_unittest.cc",
-    "core_unittest_pure_c.c",
-    "macros_unittest.cc",
-  ]
-
-  deps = [
-    "//mojo/public/c/system",
-    "//mojo/public/cpp/system",
-    "//testing/gtest",
-  ]
-}
-
-source_set("perftests") {
-  testonly = true
-
-  sources = [
-    "core_perftest.cc",
-  ]
-
-  deps = [
-    "//mojo/public/cpp/system",
-    "//mojo/public/cpp/test_support:test_utils",
-    "//testing/gtest",
-  ]
-}
diff --git a/mojo/public/c/test_support/BUILD.gn b/mojo/public/c/test_support/BUILD.gn
deleted file mode 100644
index e2abd58..0000000
--- a/mojo/public/c/test_support/BUILD.gn
+++ /dev/null
@@ -1,15 +0,0 @@
-# Copyright 2014 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-static_library("test_support") {
-  output_name = "mojo_public_test_support"
-
-  sources = [
-    "test_support.h",
-
-    # TODO(vtl): Convert this to thunks http://crbug.com/386799
-    "../../tests/test_support_private.cc",
-    "../../tests/test_support_private.h",
-  ]
-}
diff --git a/mojo/public/cpp/base/BUILD.gn b/mojo/public/cpp/base/BUILD.gn
deleted file mode 100644
index c57ac0b..0000000
--- a/mojo/public/cpp/base/BUILD.gn
+++ /dev/null
@@ -1,85 +0,0 @@
-# Copyright 2018 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//mojo/public/tools/bindings/mojom.gni")
-
-component("base") {
-  output_name = "mojo_base_lib"
-
-  sources = [
-    "big_buffer.cc",
-    "big_buffer.h",
-  ]
-
-  defines = [ "IS_MOJO_BASE_IMPL" ]
-
-  public_deps = [
-    "//base",
-    "//mojo/public/cpp/bindings",
-    "//mojo/public/cpp/system",
-  ]
-}
-
-# Normally typemap traits sources should be build directly into mojom targets
-# via the typemap file. This target is for typemapped mojo_base types whose
-# traits are shared between chromium and blink variants.
-component("shared_typemap_traits") {
-  output_name = "mojo_base_shared_typemap_traits"
-
-  sources = [
-    "big_buffer_mojom_traits.cc",
-    "big_buffer_mojom_traits.h",
-    "file_info_mojom_traits.cc",
-    "file_info_mojom_traits.h",
-    "file_path_mojom_traits.cc",
-    "file_path_mojom_traits.h",
-    "shared_memory_mojom_traits.cc",
-    "shared_memory_mojom_traits.h",
-    "time_mojom_traits.cc",
-    "time_mojom_traits.h",
-    "values_mojom_traits.cc",
-    "values_mojom_traits.h",
-  ]
-
-  defines = [ "IS_MOJO_BASE_SHARED_TRAITS_IMPL" ]
-
-  public_deps = [
-    ":base",
-    "//base:i18n",
-    "//mojo/public/mojom/base:base_shared",
-  ]
-}
-
-source_set("tests") {
-  testonly = true
-
-  sources = [
-    "big_buffer_unittest.cc",
-    "big_string_unittest.cc",
-    "file_path_unittest.cc",
-    "file_unittest.cc",
-    "memory_allocator_dump_cross_process_uid_unittest.cc",
-    "process_id_unittest.cc",
-    "read_only_buffer_unittest.cc",
-    "ref_counted_memory_unittest.cc",
-    "shared_memory_unittest.cc",
-    "string16_unittest.cc",
-    "text_direction_unittest.cc",
-    "thread_priority_unittest.cc",
-    "time_unittest.cc",
-    "unguessable_token_unittest.cc",
-    "values_unittest.cc",
-  ]
-
-  public_deps = [
-    ":base",
-    ":shared_typemap_traits",
-    "//base",
-    "//base/test:test_support",
-    "//mojo/public/cpp/test_support:test_utils",
-    "//mojo/public/mojom/base",
-    "//mojo/public/mojom/base:read_only_buffer",
-    "//testing/gtest",
-  ]
-}
diff --git a/mojo/public/cpp/base/logfont_win.typemap b/mojo/public/cpp/base/logfont_win.typemap
deleted file mode 100644
index daaf6fc..0000000
--- a/mojo/public/cpp/base/logfont_win.typemap
+++ /dev/null
@@ -1,18 +0,0 @@
-# Copyright 2017 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-mojom = "//mojo/public/mojom/base/logfont_win.mojom"
-public_headers = [
-  "//base/win/windows_full.h",
-  "//base/win/windows_types.h",
-]
-traits_headers = [ "//mojo/public/cpp/base/logfont_win_mojom_traits.h" ]
-public_deps = [
-  "//base",
-]
-sources = [
-  "//mojo/public/cpp/base/logfont_win_mojom_traits.cc",
-  "//mojo/public/cpp/base/logfont_win_mojom_traits.h",
-]
-type_mappings = [ "mojo_base.mojom.LOGFONT=::LOGFONT" ]
diff --git a/mojo/public/cpp/base/logfont_win_mojom_traits.cc b/mojo/public/cpp/base/logfont_win_mojom_traits.cc
deleted file mode 100644
index 3c73701..0000000
--- a/mojo/public/cpp/base/logfont_win_mojom_traits.cc
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "mojo/public/cpp/base/logfont_win_mojom_traits.h"
-
-#include <tchar.h>
-
-#include "base/logging.h"
-#include "base/numerics/safe_conversions.h"
-
-namespace mojo {
-
-// static
-base::span<const uint8_t>
-StructTraits<mojo_base::mojom::LOGFONTDataView, ::LOGFONT>::bytes(
-    const ::LOGFONT& input) {
-  return base::make_span(reinterpret_cast<const uint8_t*>(&input),
-                         sizeof(::LOGFONT));
-}
-
-// static
-bool StructTraits<mojo_base::mojom::LOGFONTDataView, ::LOGFONT>::Read(
-    mojo_base::mojom::LOGFONTDataView data,
-    ::LOGFONT* out) {
-  ArrayDataView<uint8_t> bytes_view;
-  data.GetBytesDataView(&bytes_view);
-  if (bytes_view.size() != sizeof(::LOGFONT))
-    return false;
-
-  const ::LOGFONT* font = reinterpret_cast<const ::LOGFONT*>(bytes_view.data());
-  if (_tcsnlen(font->lfFaceName, LF_FACESIZE) >= LF_FACESIZE)
-    return false;
-
-  memcpy(out, font, sizeof(::LOGFONT));
-  return true;
-}
-
-}  // namespace mojo
diff --git a/mojo/public/cpp/base/logfont_win_mojom_traits.h b/mojo/public/cpp/base/logfont_win_mojom_traits.h
deleted file mode 100644
index 09a9fbb..0000000
--- a/mojo/public/cpp/base/logfont_win_mojom_traits.h
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef MOJO_PUBLIC_CPP_BASE_LOGFONT_WIN_MOJOM_TRAITS_H_
-#define MOJO_PUBLIC_CPP_BASE_LOGFONT_WIN_MOJOM_TRAITS_H_
-
-#include <windows.h>
-
-#include <cstdint>
-
-#include "base/component_export.h"
-#include "base/containers/span.h"
-#include "base/macros.h"
-#include "base/win/windows_types.h"
-#include "mojo/public/cpp/bindings/struct_traits.h"
-#include "mojo/public/mojom/base/logfont_win.mojom-shared.h"
-
-namespace mojo {
-
-template <>
-struct COMPONENT_EXPORT(MOJO_BASE_MOJOM)
-    StructTraits<mojo_base::mojom::LOGFONTDataView, ::LOGFONT> {
-  static base::span<const uint8_t> bytes(const ::LOGFONT& input);
-  static bool Read(mojo_base::mojom::LOGFONTDataView data, ::LOGFONT* out);
-};
-
-}  // namespace mojo
-
-#endif  // MOJO_PUBLIC_CPP_BASE_LOGFONT_WIN_MOJOM_TRAITS_H_
diff --git a/mojo/public/cpp/bindings/BUILD.gn b/mojo/public/cpp/bindings/BUILD.gn
deleted file mode 100644
index 2715283..0000000
--- a/mojo/public/cpp/bindings/BUILD.gn
+++ /dev/null
@@ -1,228 +0,0 @@
-# Copyright 2014 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//build/buildflag_header.gni")
-import("//build/config/nacl/config.gni")
-import("//tools/ipc_fuzzer/ipc_fuzzer.gni")
-
-declare_args() {
-  enable_mojo_tracing = false
-}
-
-buildflag_header("mojo_buildflags") {
-  header = "mojo_buildflags.h"
-
-  flags = [ "MOJO_TRACE_ENABLED=$enable_mojo_tracing" ]
-}
-
-# Headers and sources which generated bindings can depend upon. No need for
-# other targets to depend on this directly: just use the "bindings" target.
-component("bindings_base") {
-  sources = [
-    "array_data_view.h",
-    "array_traits.h",
-    "array_traits_span.h",
-    "array_traits_stl.h",
-    "associated_group.h",
-    "associated_group_controller.h",
-    "clone_traits.h",
-    "disconnect_reason.h",
-    "enum_traits.h",
-    "equals_traits.h",
-    "interface_data_view.h",
-    "interface_id.h",
-    "lib/array_internal.cc",
-    "lib/array_internal.h",
-    "lib/array_serialization.h",
-    "lib/associated_group.cc",
-    "lib/associated_group_controller.cc",
-    "lib/bindings_internal.h",
-    "lib/buffer.cc",
-    "lib/buffer.h",
-    "lib/fixed_buffer.cc",
-    "lib/fixed_buffer.h",
-    "lib/handle_serialization.h",
-    "lib/hash_util.h",
-    "lib/map_data_internal.h",
-    "lib/map_serialization.h",
-    "lib/may_auto_lock.h",
-    "lib/message.cc",
-    "lib/message_header_validator.cc",
-    "lib/message_internal.cc",
-    "lib/message_internal.h",
-    "lib/scoped_interface_endpoint_handle.cc",
-    "lib/serialization.h",
-    "lib/serialization.h",
-    "lib/serialization_context.cc",
-    "lib/serialization_context.h",
-    "lib/serialization_forward.h",
-    "lib/serialization_util.h",
-    "lib/string_serialization.h",
-    "lib/template_util.h",
-    "lib/unserialized_message_context.cc",
-    "lib/unserialized_message_context.h",
-    "lib/validate_params.h",
-    "lib/validation_context.cc",
-    "lib/validation_context.h",
-    "lib/validation_errors.cc",
-    "lib/validation_errors.h",
-    "lib/validation_util.cc",
-    "lib/validation_util.h",
-    "map.h",
-    "map_data_view.h",
-    "map_traits.h",
-    "map_traits_flat_map.h",
-    "map_traits_stl.h",
-    "message.h",
-    "message_header_validator.h",
-    "scoped_interface_endpoint_handle.h",
-    "string_data_view.h",
-    "string_traits.h",
-    "string_traits_stl.h",
-    "string_traits_string_piece.h",
-    "struct_ptr.h",
-    "struct_traits.h",
-    "type_converter.h",
-    "union_traits.h",
-  ]
-
-  defines = [ "IS_MOJO_CPP_BINDINGS_BASE_IMPL" ]
-
-  public_deps = [
-    ":mojo_buildflags",
-    "//base",
-    "//mojo/public/cpp/system",
-  ]
-
-  if (enable_ipc_fuzzer) {
-    all_dependent_configs = [ "//tools/ipc_fuzzer:ipc_fuzzer_config" ]
-  }
-}
-
-component("bindings") {
-  sources = [
-    "associated_binding.h",
-    "associated_binding_set.h",
-    "associated_interface_ptr.h",
-    "associated_interface_ptr_info.h",
-    "associated_interface_request.h",
-    "binding.h",
-    "binding_set.h",
-    "bindings_export.h",
-    "callback_helpers.h",
-    "connection_error_callback.h",
-    "connector.h",
-    "filter_chain.h",
-    "interface_endpoint_client.h",
-    "interface_endpoint_controller.h",
-    "interface_ptr.h",
-    "interface_ptr_info.h",
-    "interface_ptr_set.h",
-    "interface_request.h",
-    "lib/associated_binding.cc",
-    "lib/associated_interface_ptr.cc",
-    "lib/associated_interface_ptr_state.cc",
-    "lib/associated_interface_ptr_state.h",
-    "lib/binding_state.cc",
-    "lib/binding_state.h",
-    "lib/connector.cc",
-    "lib/control_message_handler.cc",
-    "lib/control_message_handler.h",
-    "lib/control_message_proxy.cc",
-    "lib/control_message_proxy.h",
-    "lib/filter_chain.cc",
-    "lib/interface_endpoint_client.cc",
-    "lib/interface_ptr_state.cc",
-    "lib/interface_ptr_state.h",
-    "lib/interface_serialization.h",
-    "lib/multiplex_router.cc",
-    "lib/multiplex_router.h",
-    "lib/native_enum_data.h",
-    "lib/native_enum_serialization.h",
-    "lib/native_struct_serialization.cc",
-    "lib/native_struct_serialization.h",
-    "lib/pipe_control_message_handler.cc",
-    "lib/pipe_control_message_proxy.cc",
-    "lib/sequence_local_sync_event_watcher.cc",
-    "lib/sync_call_restrictions.cc",
-    "lib/sync_event_watcher.cc",
-    "lib/sync_handle_registry.cc",
-    "lib/sync_handle_watcher.cc",
-    "lib/task_runner_helper.cc",
-    "lib/task_runner_helper.h",
-    "native_enum.h",
-    "pipe_control_message_handler.h",
-    "pipe_control_message_handler_delegate.h",
-    "pipe_control_message_proxy.h",
-    "raw_ptr_impl_ref_traits.h",
-    "sequence_local_sync_event_watcher.h",
-    "strong_associated_binding.h",
-    "strong_binding.h",
-    "strong_binding_set.h",
-    "sync_call_restrictions.h",
-    "sync_event_watcher.h",
-    "sync_handle_registry.h",
-    "sync_handle_watcher.h",
-    "thread_safe_interface_ptr.h",
-    "unique_ptr_impl_ref_traits.h",
-  ]
-
-  if (enable_ipc_fuzzer && !is_nacl_nonsfi) {
-    sources += [
-      "lib/message_dumper.cc",
-      "message_dumper.h",
-    ]
-  }
-
-  public_deps = [
-    ":bindings_base",
-    ":struct_traits",
-    "//base",
-    "//ipc:message_support",
-    "//ipc:param_traits",
-    "//mojo/public/cpp/system",
-    "//mojo/public/interfaces/bindings",
-  ]
-
-  deps = [
-    "//ipc:native_handle_type_converters",
-  ]
-
-  defines = [ "MOJO_CPP_BINDINGS_IMPLEMENTATION" ]
-}
-
-source_set("struct_traits") {
-  sources = [
-    "array_traits.h",
-    "enum_traits.h",
-    "lib/template_util.h",
-    "map_traits.h",
-    "string_traits.h",
-    "struct_traits.h",
-    "union_traits.h",
-  ]
-}
-
-if (!is_ios) {
-  # TODO(yzshen): crbug.com/617718 Consider moving this into blink.
-  source_set("wtf_support") {
-    sources = [
-      "array_traits_wtf_vector.h",
-      "lib/string_traits_wtf.cc",
-      "lib/wtf_clone_equals_util.h",
-      "lib/wtf_hash_util.h",
-      "lib/wtf_serialization.h",
-      "map_traits_wtf_hash_map.h",
-      "string_traits_wtf.h",
-    ]
-
-    public_deps = [
-      ":bindings",
-      "//third_party/blink/renderer/platform:platform_export",
-      "//third_party/blink/renderer/platform/wtf",
-    ]
-
-    public_configs = [ "//third_party/blink/renderer:config" ]
-  }
-}
diff --git a/mojo/public/cpp/bindings/tests/BUILD.gn b/mojo/public/cpp/bindings/tests/BUILD.gn
deleted file mode 100644
index 82038a1..0000000
--- a/mojo/public/cpp/bindings/tests/BUILD.gn
+++ /dev/null
@@ -1,156 +0,0 @@
-# Copyright 2014 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-source_set("tests") {
-  testonly = true
-
-  sources = [
-    "associated_interface_unittest.cc",
-    "bind_task_runner_unittest.cc",
-    "binding_callback_unittest.cc",
-    "binding_set_unittest.cc",
-    "binding_unittest.cc",
-    "bindings_test_base.cc",
-    "bindings_test_base.h",
-    "buffer_unittest.cc",
-    "callback_helpers_unittest.cc",
-    "connector_unittest.cc",
-    "constant_unittest.cc",
-    "container_test_util.cc",
-    "container_test_util.h",
-    "data_view_unittest.cc",
-    "equals_unittest.cc",
-    "handle_passing_unittest.cc",
-    "hash_unittest.cc",
-    "interface_ptr_unittest.cc",
-    "lazy_serialization_unittest.cc",
-    "map_unittest.cc",
-    "message_queue.cc",
-    "message_queue.h",
-    "multiplex_router_unittest.cc",
-    "native_struct_unittest.cc",
-    "report_bad_message_unittest.cc",
-    "request_response_unittest.cc",
-    "router_test_util.cc",
-    "router_test_util.h",
-    "sample_service_unittest.cc",
-    "serialization_warning_unittest.cc",
-    "struct_unittest.cc",
-    "sync_handle_registry_unittest.cc",
-    "sync_method_unittest.cc",
-    "test_helpers_unittest.cc",
-    "type_conversion_unittest.cc",
-    "union_unittest.cc",
-    "validation_context_unittest.cc",
-    "validation_unittest.cc",
-    "variant_test_util.h",
-  ]
-
-  deps = [
-    ":mojo_public_bindings_test_utils",
-    "//base/test:test_support",
-    "//mojo/core/embedder",
-    "//mojo/public/cpp/bindings",
-    "//mojo/public/cpp/system",
-    "//mojo/public/cpp/test_support:test_utils",
-    "//mojo/public/interfaces/bindings/tests:test_associated_interfaces",
-    "//mojo/public/interfaces/bindings/tests:test_export_component",
-    "//mojo/public/interfaces/bindings/tests:test_export_component2",
-    "//mojo/public/interfaces/bindings/tests:test_exported_import",
-    "//mojo/public/interfaces/bindings/tests:test_interfaces",
-    "//mojo/public/interfaces/bindings/tests:test_struct_traits_interfaces",
-    "//testing/gtest",
-  ]
-
-  data = [
-    "//mojo/public/interfaces/bindings/tests/data/validation/",
-  ]
-
-  if (is_ios) {
-    assert_no_deps = [ "//third_party/WebKit/*" ]
-  } else {
-    sources += [
-      "pickle_unittest.cc",
-      "struct_traits_unittest.cc",
-    ]
-
-    deps += [
-      "//mojo/public/cpp/bindings/tests:struct_with_traits_impl",
-      "//mojo/public/interfaces/bindings/tests:test_interfaces_blink",
-    ]
-  }
-}
-
-if (!is_ios) {
-  source_set("for_blink_tests") {
-    testonly = true
-
-    sources = [
-      "container_test_util.cc",
-      "container_test_util.h",
-      "variant_test_util.h",
-      "wtf_hash_unittest.cc",
-      "wtf_map_unittest.cc",
-      "wtf_types_unittest.cc",
-    ]
-
-    deps = [
-      "//mojo/public/cpp/bindings",
-      "//mojo/public/cpp/system",
-      "//mojo/public/interfaces/bindings/tests:test_export_blink_component",
-      "//mojo/public/interfaces/bindings/tests:test_exported_import_blink",
-      "//mojo/public/interfaces/bindings/tests:test_interfaces",
-      "//mojo/public/interfaces/bindings/tests:test_interfaces_blink",
-      "//mojo/public/interfaces/bindings/tests:test_wtf_types",
-      "//mojo/public/interfaces/bindings/tests:test_wtf_types_blink",
-      "//testing/gtest",
-    ]
-  }
-}
-
-source_set("struct_with_traits_impl") {
-  sources = [
-    "struct_with_traits_impl.cc",
-    "struct_with_traits_impl.h",
-  ]
-
-  deps = [
-    "//base",
-    "//mojo/public/cpp/system:system",
-  ]
-}
-
-source_set("perftests") {
-  testonly = true
-
-  sources = [
-    "bindings_perftest.cc",
-  ]
-
-  if (!is_ios) {
-    sources += [ "e2e_perftest.cc" ]
-  }
-
-  deps = [
-    "//base/test:test_support",
-    "//mojo/core/embedder",
-    "//mojo/core/test:test_support",
-    "//mojo/public/cpp/bindings",
-    "//mojo/public/cpp/system",
-    "//mojo/public/cpp/test_support:test_utils",
-    "//mojo/public/interfaces/bindings/tests:test_interfaces",
-    "//testing/gtest",
-  ]
-}
-
-source_set("mojo_public_bindings_test_utils") {
-  sources = [
-    "validation_test_input_parser.cc",
-    "validation_test_input_parser.h",
-  ]
-
-  deps = [
-    "//mojo/public/c/system",
-  ]
-}
diff --git a/mojo/public/cpp/platform/BUILD.gn b/mojo/public/cpp/platform/BUILD.gn
deleted file mode 100644
index b0aa90e..0000000
--- a/mojo/public/cpp/platform/BUILD.gn
+++ /dev/null
@@ -1,50 +0,0 @@
-# Copyright 2018 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//build/config/nacl/config.gni")
-
-component("platform") {
-  output_name = "mojo_cpp_platform"
-
-  public = [
-    "named_platform_channel.h",
-    "platform_channel.h",
-    "platform_channel_endpoint.h",
-    "platform_channel_server_endpoint.h",
-    "platform_handle.h",
-  ]
-
-  sources = [
-    "named_platform_channel.cc",
-    "named_platform_channel_win.cc",
-    "platform_channel.cc",
-    "platform_channel_endpoint.cc",
-    "platform_channel_server_endpoint.cc",
-    "platform_handle.cc",
-  ]
-
-  if (is_posix && (!is_nacl || is_nacl_nonsfi)) {
-    public += [ "socket_utils_posix.h" ]
-    sources += [ "socket_utils_posix.cc" ]
-  }
-
-  public_deps = [
-    "//base",
-    "//mojo/public/c/system:headers",
-  ]
-
-  if (is_posix && (!is_nacl && !is_fuchsia)) {
-    sources += [ "named_platform_channel_posix.cc" ]
-  }
-
-  if (is_fuchsia) {
-    sources += [ "named_platform_channel_fuchsia.cc" ]
-    public_deps += [
-      "//third_party/fuchsia-sdk:fdio",
-      "//third_party/fuchsia-sdk:zx",
-    ]
-  }
-
-  defines = [ "IS_MOJO_CPP_PLATFORM_IMPL" ]
-}
diff --git a/mojo/public/cpp/platform/named_platform_channel_fuchsia.cc b/mojo/public/cpp/platform/named_platform_channel_fuchsia.cc
deleted file mode 100644
index 44ae4af..0000000
--- a/mojo/public/cpp/platform/named_platform_channel_fuchsia.cc
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "mojo/public/cpp/platform/named_platform_channel.h"
-
-namespace mojo {
-
-// static
-PlatformChannelServerEndpoint NamedPlatformChannel::CreateServerEndpoint(
-    const Options& options,
-    ServerName* server_name) {
-  // TODO(https://crbug.com/754038): Implement, or remove dependencies.
-  NOTREACHED();
-  return {};
-}
-
-// static
-PlatformChannelEndpoint NamedPlatformChannel::CreateClientEndpoint(
-    const ServerName& server_name) {
-  // TODO(https://crbug.com/754038): Implement, or remove dependencies.
-  NOTREACHED();
-  return {};
-}
-
-}  // namespace mojo
diff --git a/mojo/public/cpp/platform/named_platform_channel_win.cc b/mojo/public/cpp/platform/named_platform_channel_win.cc
deleted file mode 100644
index 9c329bd..0000000
--- a/mojo/public/cpp/platform/named_platform_channel_win.cc
+++ /dev/null
@@ -1,110 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "mojo/public/cpp/platform/named_platform_channel.h"
-
-#include <windows.h>
-#include <memory>
-
-// NOTE: This needs to be included *after* windows.h.
-#include <sddl.h>
-
-#include "base/rand_util.h"
-#include "base/strings/stringprintf.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/win/scoped_handle.h"
-#include "base/win/windows_version.h"
-
-namespace mojo {
-
-namespace {
-
-// A DACL to grant:
-// GA = Generic All
-// access to:
-// SY = LOCAL_SYSTEM
-// BA = BUILTIN_ADMINISTRATORS
-// OW = OWNER_RIGHTS
-constexpr base::char16 kDefaultSecurityDescriptor[] =
-    L"D:(A;;GA;;;SY)(A;;GA;;;BA)(A;;GA;;;OW)";
-
-NamedPlatformChannel::ServerName GenerateRandomServerName() {
-  return base::UTF8ToUTF16(
-      base::StringPrintf("%lu.%lu.%I64u", ::GetCurrentProcessId(),
-                         ::GetCurrentThreadId(), base::RandUint64()));
-}
-
-base::string16 GetPipeNameFromServerName(
-    const NamedPlatformChannel::ServerName& server_name) {
-  return L"\\\\.\\pipe\\mojo." + server_name;
-}
-
-}  // namespace
-
-// static
-PlatformChannelServerEndpoint NamedPlatformChannel::CreateServerEndpoint(
-    const Options& options,
-    ServerName* server_name) {
-  ServerName name = options.server_name;
-  if (name.empty())
-    name = GenerateRandomServerName();
-
-  PSECURITY_DESCRIPTOR security_desc = nullptr;
-  ULONG security_desc_len = 0;
-  PCHECK(::ConvertStringSecurityDescriptorToSecurityDescriptor(
-      options.security_descriptor.empty() ? kDefaultSecurityDescriptor
-                                          : options.security_descriptor.c_str(),
-      SDDL_REVISION_1, &security_desc, &security_desc_len));
-  std::unique_ptr<void, decltype(::LocalFree)*> p(security_desc, ::LocalFree);
-  SECURITY_ATTRIBUTES security_attributes = {sizeof(SECURITY_ATTRIBUTES),
-                                             security_desc, FALSE};
-
-  const DWORD kOpenMode = options.enforce_uniqueness
-                              ? PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED |
-                                    FILE_FLAG_FIRST_PIPE_INSTANCE
-                              : PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED;
-  const DWORD kPipeMode =
-      PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_REJECT_REMOTE_CLIENTS;
-
-  base::string16 pipe_name = GetPipeNameFromServerName(name);
-  PlatformHandle handle(base::win::ScopedHandle(::CreateNamedPipeW(
-      pipe_name.c_str(), kOpenMode, kPipeMode,
-      options.enforce_uniqueness ? 1 : 255,  // Max instances.
-      4096,                                  // Out buffer size.
-      4096,                                  // In buffer size.
-      5000,                                  // Timeout in milliseconds.
-      &security_attributes)));
-
-  *server_name = name;
-  return PlatformChannelServerEndpoint(std::move(handle));
-}
-
-// static
-PlatformChannelEndpoint NamedPlatformChannel::CreateClientEndpoint(
-    const ServerName& server_name) {
-  base::string16 pipe_name = GetPipeNameFromServerName(server_name);
-
-  // Note: This may block.
-  if (!::WaitNamedPipeW(pipe_name.c_str(), NMPWAIT_USE_DEFAULT_WAIT))
-    return PlatformChannelEndpoint();
-
-  const DWORD kDesiredAccess = GENERIC_READ | GENERIC_WRITE;
-  // The SECURITY_ANONYMOUS flag means that the server side cannot impersonate
-  // the client.
-  const DWORD kFlags =
-      SECURITY_SQOS_PRESENT | SECURITY_ANONYMOUS | FILE_FLAG_OVERLAPPED;
-  PlatformHandle handle(base::win::ScopedHandle(
-      ::CreateFileW(pipe_name.c_str(), kDesiredAccess, 0, nullptr,
-                    OPEN_EXISTING, kFlags, nullptr)));
-
-  // The server may have stopped accepting a connection between the
-  // WaitNamedPipe() and CreateFile(). If this occurs, an invalid handle is
-  // returned.
-  DPLOG_IF(ERROR, !handle.is_valid())
-      << "Named pipe " << pipe_name
-      << " could not be opened after WaitNamedPipe succeeded";
-  return PlatformChannelEndpoint(std::move(handle));
-}
-
-}  // namespace mojo
diff --git a/mojo/public/cpp/platform/tests/BUILD.gn b/mojo/public/cpp/platform/tests/BUILD.gn
deleted file mode 100644
index d90e9f6..0000000
--- a/mojo/public/cpp/platform/tests/BUILD.gn
+++ /dev/null
@@ -1,19 +0,0 @@
-# Copyright 2018 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-source_set("tests") {
-  testonly = true
-
-  sources = [
-    "platform_handle_unittest.cc",
-  ]
-
-  deps = [
-    "//base",
-    "//mojo/public/c/system",
-    "//mojo/public/cpp/platform",
-    "//mojo/public/cpp/system",
-    "//testing/gtest",
-  ]
-}
diff --git a/mojo/public/cpp/system/BUILD.gn b/mojo/public/cpp/system/BUILD.gn
deleted file mode 100644
index 155ac14..0000000
--- a/mojo/public/cpp/system/BUILD.gn
+++ /dev/null
@@ -1,75 +0,0 @@
-# Copyright 2014 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-# Deletes libsystem.dylib from the build dir, since it shadows
-# /usr/lib/libSystem.dylib on macOS.
-# TODO(thakis): Remove this after a while.
-action("clean_up_old_dylib") {
-  script = "//build/rm.py"
-  stamp = "$target_gen_dir/clean_up_stamp"
-  outputs = [
-    stamp,
-  ]
-  args = [
-    "--stamp",
-    rebase_path(stamp, root_build_dir),
-    "-f",
-    "libsystem.dylib",
-  ]
-}
-
-component("system") {
-  output_name = "mojo_public_system_cpp"
-
-  sources = [
-    "buffer.cc",
-    "buffer.h",
-    "core.h",
-    "data_pipe.h",
-    "data_pipe_drainer.cc",
-    "data_pipe_drainer.h",
-    "data_pipe_utils.cc",
-    "data_pipe_utils.h",
-    "file_data_pipe_producer.cc",
-    "file_data_pipe_producer.h",
-    "functions.h",
-    "handle.h",
-    "handle_signal_tracker.cc",
-    "handle_signal_tracker.h",
-    "handle_signals_state.h",
-    "invitation.cc",
-    "invitation.h",
-    "isolated_connection.cc",
-    "isolated_connection.h",
-    "message.h",
-    "message_pipe.cc",
-    "message_pipe.h",
-    "platform_handle.cc",
-    "platform_handle.h",
-    "scope_to_message_pipe.cc",
-    "scope_to_message_pipe.h",
-    "simple_watcher.cc",
-    "simple_watcher.h",
-    "string_data_pipe_producer.cc",
-    "string_data_pipe_producer.h",
-    "system_export.h",
-    "trap.cc",
-    "trap.h",
-    "wait.cc",
-    "wait.h",
-    "wait_set.cc",
-    "wait_set.h",
-  ]
-
-  public_deps = [
-    "//base",
-    "//mojo/public/c/system",
-    "//mojo/public/cpp/platform",
-  ]
-  deps = [
-    ":clean_up_old_dylib",
-  ]
-
-  defines = [ "MOJO_CPP_SYSTEM_IMPLEMENTATION" ]
-}
diff --git a/mojo/public/cpp/system/tests/BUILD.gn b/mojo/public/cpp/system/tests/BUILD.gn
deleted file mode 100644
index c08f3c1..0000000
--- a/mojo/public/cpp/system/tests/BUILD.gn
+++ /dev/null
@@ -1,35 +0,0 @@
-# Copyright 2014 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-source_set("tests") {
-  testonly = true
-
-  sources = [
-    "core_unittest.cc",
-    "data_pipe_drainer_unittest.cc",
-    "file_data_pipe_producer_unittest.cc",
-    "handle_signal_tracker_unittest.cc",
-    "handle_signals_state_unittest.cc",
-    "scope_to_message_pipe_unittest.cc",
-    "simple_watcher_unittest.cc",
-    "string_data_pipe_producer_unittest.cc",
-    "wait_set_unittest.cc",
-    "wait_unittest.cc",
-  ]
-
-  if (!is_ios) {
-    sources += [ "invitation_unittest.cc" ]
-  }
-
-  deps = [
-    "//base",
-    "//base/test:test_support",
-    "//mojo/core/test:test_support",
-    "//mojo/public/c/system/tests",
-    "//mojo/public/cpp/platform",
-    "//mojo/public/cpp/system",
-    "//mojo/public/cpp/test_support:test_utils",
-    "//testing/gtest",
-  ]
-}
diff --git a/mojo/public/cpp/test_support/BUILD.gn b/mojo/public/cpp/test_support/BUILD.gn
deleted file mode 100644
index 3312a37..0000000
--- a/mojo/public/cpp/test_support/BUILD.gn
+++ /dev/null
@@ -1,20 +0,0 @@
-# Copyright 2014 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-static_library("test_utils") {
-  testonly = true
-
-  sources = [
-    "lib/test_support.cc",
-    "lib/test_utils.cc",
-    "test_utils.h",
-  ]
-
-  deps = [
-    "//mojo/public/c/test_support",
-    "//mojo/public/cpp/bindings",
-    "//mojo/public/cpp/system",
-    "//testing/gtest",
-  ]
-}
diff --git a/mojo/public/interfaces/BUILD.gn b/mojo/public/interfaces/BUILD.gn
deleted file mode 100644
index fb11ec2..0000000
--- a/mojo/public/interfaces/BUILD.gn
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright 2015 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-group("interfaces") {
-  deps = [
-    "bindings",
-  ]
-}
diff --git a/mojo/public/interfaces/bindings/BUILD.gn b/mojo/public/interfaces/bindings/BUILD.gn
deleted file mode 100644
index eca88c6..0000000
--- a/mojo/public/interfaces/bindings/BUILD.gn
+++ /dev/null
@@ -1,24 +0,0 @@
-# Copyright 2015 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("../../tools/bindings/mojom.gni")
-
-mojom_component("bindings") {
-  output_prefix = "mojo_mojom_bindings"
-  macro_prefix = "MOJO_MOJOM_BINDINGS"
-
-  visibility = [
-    "//mojo/public/cpp/bindings",
-    "//ipc:mojom",
-  ]
-
-  sources = [
-    "interface_control_messages.mojom",
-    "native_struct.mojom",
-    "pipe_control_messages.mojom",
-  ]
-
-  disallow_native_types = true
-  disallow_interfaces = true
-}
diff --git a/mojo/public/interfaces/bindings/tests/BUILD.gn b/mojo/public/interfaces/bindings/tests/BUILD.gn
deleted file mode 100644
index 939c837..0000000
--- a/mojo/public/interfaces/bindings/tests/BUILD.gn
+++ /dev/null
@@ -1,266 +0,0 @@
-# Copyright 2014 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("../../../tools/bindings/mojom.gni")
-
-group("test_data_deps") {
-  testonly = true
-  data_deps = [
-    ":test_interfaces_js_data_deps",
-    ":test_associated_interfaces_js_data_deps",
-    ":validation_test_data",
-    ":validation_test_data_list",
-  ]
-}
-
-copy("validation_test_data") {
-  testonly = true
-  sources = [
-    "data/validation",
-  ]
-  outputs = [
-    "$root_gen_dir/layout_test_data/{{source_root_relative_dir}}/{{source_file_part}}",
-  ]
-}
-
-action_foreach("validation_test_data_list") {
-  testonly = true
-  script = "//mojo/public/tools/bindings/gen_data_files_list.py"
-  inputs = mojom_generator_sources
-  sources = [
-    "data/validation",
-  ]
-  outputs = [
-    "$root_gen_dir/layout_test_data/{{source_root_relative_dir}}/{{source_file_part}}_index.txt",
-  ]
-  args = [
-    "-d",
-    rebase_path(sources[0], root_build_dir),
-    "-o",
-    rebase_path(outputs[0], root_build_dir),
-  ]
-}
-
-mojom("test_interfaces") {
-  testonly = true
-  sources = [
-    "math_calculator.mojom",
-    "no_module.mojom",
-    "ping_service.mojom",
-    "rect.mojom",
-    "regression_tests.mojom",
-    "sample_factory.mojom",
-    "sample_interfaces.mojom",
-    "sample_service.mojom",
-    "scoping.mojom",
-    "serialization_test_structs.mojom",
-    "test_bad_messages.mojom",
-    "test_constants.mojom",
-    "test_data_view.mojom",
-    "test_name_generator.mojom",
-    "test_native_types.mojom",
-    "test_structs.mojom",
-    "test_sync_methods.mojom",
-    "test_unions.mojom",
-    "validation_test_interfaces.mojom",
-  ]
-  public_deps = [
-    ":echo",
-    ":test_mojom_import",
-    ":test_mojom_import2",
-  ]
-
-  # TODO(crbug.com/714018): Convert the implementation to use OnceCallback.
-  use_once_callback = false
-
-  support_lazy_serialization = true
-
-  # Validation tests require precise message content matching, so we avoid
-  # scrambling message IDs for test interfaces.
-  scramble_message_ids = false
-}
-
-component("test_export_component") {
-  testonly = true
-  deps = [
-    ":test_export",
-  ]
-}
-
-if (!is_ios) {
-  component("test_export_blink_component") {
-    testonly = true
-    deps = [
-      ":test_export_blink",
-    ]
-  }
-}
-
-mojom("test_export") {
-  testonly = true
-  sources = [
-    "test_export.mojom",
-  ]
-  export_class_attribute = "MOJO_TEST_EXPORT"
-  export_define = "MOJO_TEST_IMPLEMENTATION=1"
-  export_header = "mojo/public/cpp/bindings/tests/mojo_test_export.h"
-  if (!is_ios) {
-    export_class_attribute_blink = "MOJO_TEST_BLINK_EXPORT"
-    export_define_blink = "MOJO_TEST_BLINK_IMPLEMENTATION=1"
-    export_header_blink =
-        "mojo/public/cpp/bindings/tests/mojo_test_blink_export.h"
-  }
-  visibility = [ ":test_export_component" ]
-  if (!is_ios) {
-    visibility_blink = [ ":test_export_blink_component" ]
-  }
-}
-
-mojom("test_exported_import") {
-  testonly = true
-  sources = [
-    "test_import.mojom",
-  ]
-  public_deps = [
-    ":test_export",
-  ]
-
-  overridden_deps = [ ":test_export" ]
-  component_deps = [ ":test_export_component" ]
-  if (!is_ios) {
-    overridden_deps_blink = [ ":test_export" ]
-    component_deps_blink = [ ":test_export_blink_component" ]
-  }
-}
-
-# Used to test that it is okay to call mojom::Foo::Serialize()/Deserialize()
-# even if the mojom target is linked into another component.
-#
-# We don't use |test_export_component| for this test because
-# //mojo/public/cpp/bindings/tests depends on both |test_export_component| and
-# |test_exported_import| and therefore actually get the shared cpp sources of
-# test_export.mojom from |test_exported_import|.
-component("test_export_component2") {
-  testonly = true
-  public_deps = [
-    ":test_export2",
-  ]
-}
-
-mojom("test_export2") {
-  testonly = true
-  sources = [
-    "test_export2.mojom",
-  ]
-  export_class_attribute = "MOJO_TEST_EXPORT"
-  export_define = "MOJO_TEST_IMPLEMENTATION=1"
-  export_header = "mojo/public/cpp/bindings/tests/mojo_test_export.h"
-  visibility = [ ":test_export_component2" ]
-}
-
-mojom("test_mojom_import") {
-  testonly = true
-  sources = [
-    "sample_import.mojom",
-  ]
-}
-
-mojom("test_mojom_import_wrapper") {
-  testonly = true
-  public_deps = [
-    ":test_mojom_import",
-  ]
-}
-
-mojom("test_mojom_import_wrapper_wrapper") {
-  testonly = true
-  public_deps = [
-    ":test_mojom_import_wrapper",
-  ]
-}
-
-mojom("test_mojom_import2") {
-  testonly = true
-  sources = [
-    "sample_import2.mojom",
-  ]
-  public_deps = [
-    ":test_mojom_import",
-    ":test_mojom_import_wrapper_wrapper",
-  ]
-}
-
-mojom("test_struct_traits_interfaces") {
-  testonly = true
-  sources = [
-    "struct_with_traits.mojom",
-  ]
-
-  # TODO(crbug.com/714018): Convert the implementation to use OnceCallback.
-  use_once_callback = false
-
-  support_lazy_serialization = true
-}
-
-mojom("test_associated_interfaces") {
-  # These files are not included in the test_interfaces target because
-  # associated interfaces are not supported by all bindings languages yet.
-  testonly = true
-  sources = [
-    "test_associated_interfaces.mojom",
-    "validation_test_associated_interfaces.mojom",
-  ]
-
-  public_deps = [
-    ":test_interfaces",
-  ]
-
-  # TODO(crbug.com/714018): Convert the implementation to use OnceCallback.
-  use_once_callback = false
-
-  # Validation tests require precise message content matching, so we avoid
-  # scrambling message IDs for test interfaces.
-  scramble_message_ids = false
-}
-
-mojom("versioning_test_service_interfaces") {
-  testonly = true
-  sources = [
-    "versioning_test_service.mojom",
-  ]
-}
-
-mojom("versioning_test_client_interfaces") {
-  testonly = true
-  sources = [
-    "versioning_test_client.mojom",
-  ]
-}
-
-mojom("test_wtf_types") {
-  testonly = true
-
-  sources = [
-    "test_wtf_types.mojom",
-  ]
-
-  # TODO(crbug.com/714018): Convert the implementation to use OnceCallback.
-  use_once_callback = false
-}
-
-mojom("test_no_sources") {
-  testonly = true
-
-  public_deps = [
-    ":test_interfaces",
-  ]
-}
-
-mojom("echo") {
-  testonly = true
-  sources = [
-    "echo.mojom",
-    "echo_import/echo_import.mojom",
-  ]
-}
diff --git a/mojo/public/java/BUILD.gn b/mojo/public/java/BUILD.gn
deleted file mode 100644
index c6159ab..0000000
--- a/mojo/public/java/BUILD.gn
+++ /dev/null
@@ -1,79 +0,0 @@
-# Copyright 2014 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//build/config/android/rules.gni")
-
-android_library("system_java") {
-  java_files = [
-    "system/src/org/chromium/mojo/system/Core.java",
-    "system/src/org/chromium/mojo/system/DataPipe.java",
-    "system/src/org/chromium/mojo/system/Flags.java",
-    "system/src/org/chromium/mojo/system/Handle.java",
-    "system/src/org/chromium/mojo/system/InvalidHandle.java",
-    "system/src/org/chromium/mojo/system/MessagePipeHandle.java",
-    "system/src/org/chromium/mojo/system/MojoException.java",
-    "system/src/org/chromium/mojo/system/MojoResult.java",
-    "system/src/org/chromium/mojo/system/Pair.java",
-    "system/src/org/chromium/mojo/system/ResultAnd.java",
-    "system/src/org/chromium/mojo/system/SharedBufferHandle.java",
-    "system/src/org/chromium/mojo/system/UntypedHandle.java",
-    "system/src/org/chromium/mojo/system/RunLoop.java",
-    "system/src/org/chromium/mojo/system/Watcher.java",
-  ]
-
-  deps = [
-    "//base:base_java",
-  ]
-}
-
-android_library("bindings_java") {
-  java_files = [
-    "bindings/src/org/chromium/mojo/bindings/AssociatedInterfaceNotSupported.java",
-    "bindings/src/org/chromium/mojo/bindings/AssociatedInterfaceRequestNotSupported.java",
-    "bindings/src/org/chromium/mojo/bindings/AutoCloseableRouter.java",
-    "bindings/src/org/chromium/mojo/bindings/BindingsHelper.java",
-    "bindings/src/org/chromium/mojo/bindings/Callbacks.java",
-    "bindings/src/org/chromium/mojo/bindings/ConnectionErrorHandler.java",
-    "bindings/src/org/chromium/mojo/bindings/Connector.java",
-    "bindings/src/org/chromium/mojo/bindings/DataHeader.java",
-    "bindings/src/org/chromium/mojo/bindings/Decoder.java",
-    "bindings/src/org/chromium/mojo/bindings/DelegatingConnectionErrorHandler.java",
-    "bindings/src/org/chromium/mojo/bindings/DeserializationException.java",
-    "bindings/src/org/chromium/mojo/bindings/Encoder.java",
-    "bindings/src/org/chromium/mojo/bindings/ExceptionHandler.java",
-    "bindings/src/org/chromium/mojo/bindings/ExecutorFactory.java",
-    "bindings/src/org/chromium/mojo/bindings/HandleOwner.java",
-    "bindings/src/org/chromium/mojo/bindings/InterfaceControlMessagesHelper.java",
-    "bindings/src/org/chromium/mojo/bindings/Interface.java",
-    "bindings/src/org/chromium/mojo/bindings/InterfaceRequest.java",
-    "bindings/src/org/chromium/mojo/bindings/MessageHeader.java",
-    "bindings/src/org/chromium/mojo/bindings/Message.java",
-    "bindings/src/org/chromium/mojo/bindings/MessageReceiver.java",
-    "bindings/src/org/chromium/mojo/bindings/MessageReceiverWithResponder.java",
-    "bindings/src/org/chromium/mojo/bindings/RouterImpl.java",
-    "bindings/src/org/chromium/mojo/bindings/Router.java",
-    "bindings/src/org/chromium/mojo/bindings/SerializationException.java",
-    "bindings/src/org/chromium/mojo/bindings/ServiceMessage.java",
-    "bindings/src/org/chromium/mojo/bindings/SideEffectFreeCloseable.java",
-    "bindings/src/org/chromium/mojo/bindings/Struct.java",
-    "bindings/src/org/chromium/mojo/bindings/Union.java",
-  ]
-
-  deps = [
-    ":system_java",
-    "//base:base_java",
-  ]
-
-  srcjar_deps = [ "../interfaces/bindings:bindings_java_sources" ]
-}
-
-android_library("base_java") {
-  java_files = [ "base/src/org/chromium/mojo_base/BigBufferUtil.java" ]
-
-  deps = [
-    ":system_java",
-    "//mojo/public/java/system:system_impl_java",
-    "//mojo/public/mojom/base:base_java",
-  ]
-}
diff --git a/mojo/public/java/system/BUILD.gn b/mojo/public/java/system/BUILD.gn
deleted file mode 100644
index 0025f55..0000000
--- a/mojo/public/java/system/BUILD.gn
+++ /dev/null
@@ -1,177 +0,0 @@
-# Copyright 2014 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//build/config/android/rules.gni")
-
-group("system") {
-  testonly = true
-  deps = [
-    ":mojo_javatests",
-    ":mojo_test_apk",
-    ":system_impl_java",
-  ]
-}
-
-generate_jni("jni_headers") {
-  sources = [
-    "javatests/src/org/chromium/mojo/MojoTestRule.java",
-    "javatests/src/org/chromium/mojo/bindings/ValidationTestUtil.java",
-  ]
-  public_deps = [
-    ":system_impl_java_jni_headers",
-  ]
-
-  jni_package = "mojo"
-}
-
-generate_jni("system_impl_java_jni_headers") {
-  sources = [
-    "src/org/chromium/mojo/system/impl/BaseRunLoop.java",
-    "src/org/chromium/mojo/system/impl/CoreImpl.java",
-    "src/org/chromium/mojo/system/impl/WatcherImpl.java",
-  ]
-
-  jni_package = "mojo"
-}
-
-source_set("native_support") {
-  sources = [
-    "base_run_loop.cc",
-    "core_impl.cc",
-    "watcher_impl.cc",
-  ]
-
-  deps = [
-    ":system_impl_java_jni_headers",
-    "//base",
-    "//mojo/public/c/system",
-    "//mojo/public/cpp/system",
-  ]
-}
-
-android_library("system_impl_java") {
-  java_files = [
-    "src/org/chromium/mojo/system/impl/BaseRunLoop.java",
-    "src/org/chromium/mojo/system/impl/CoreImpl.java",
-    "src/org/chromium/mojo/system/impl/DataPipeConsumerHandleImpl.java",
-    "src/org/chromium/mojo/system/impl/DataPipeProducerHandleImpl.java",
-    "src/org/chromium/mojo/system/impl/HandleBase.java",
-    "src/org/chromium/mojo/system/impl/MessagePipeHandleImpl.java",
-    "src/org/chromium/mojo/system/impl/SharedBufferHandleImpl.java",
-    "src/org/chromium/mojo/system/impl/UntypedHandleImpl.java",
-    "src/org/chromium/mojo/system/impl/WatcherImpl.java",
-  ]
-
-  deps = [
-    "//base:base_java",
-    "//mojo/public/java:system_java",
-  ]
-}
-
-# Targets should also depend on :test_support for the native side.
-android_library("test_support_java") {
-  testonly = true
-  java_files = [ "javatests/src/org/chromium/mojo/MojoTestRule.java" ]
-  deps = [
-    "//base:base_java",
-    "//third_party/junit",
-  ]
-}
-
-source_set("test_support") {
-  testonly = true
-  sources = [
-    "javatests/mojo_test_rule.cc",
-  ]
-  deps = [
-    ":jni_headers",
-    "//base",
-    "//base/test:test_support",
-    "//mojo/core/embedder",
-  ]
-  defines = [ "UNIT_TEST" ]
-}
-
-android_library("mojo_javatests") {
-  testonly = true
-  java_files = [
-    "javatests/src/org/chromium/mojo/HandleMock.java",
-    "javatests/src/org/chromium/mojo/TestUtils.java",
-    "javatests/src/org/chromium/mojo/bindings/BindingsHelperTest.java",
-    "javatests/src/org/chromium/mojo/bindings/BindingsTest.java",
-    "javatests/src/org/chromium/mojo/bindings/BindingsTestUtils.java",
-    "javatests/src/org/chromium/mojo/bindings/BindingsVersioningTest.java",
-    "javatests/src/org/chromium/mojo/bindings/CallbacksTest.java",
-    "javatests/src/org/chromium/mojo/bindings/ConnectorTest.java",
-    "javatests/src/org/chromium/mojo/bindings/ExecutorFactoryTest.java",
-    "javatests/src/org/chromium/mojo/bindings/InterfacesTest.java",
-    "javatests/src/org/chromium/mojo/bindings/MessageHeaderTest.java",
-    "javatests/src/org/chromium/mojo/bindings/NameGeneratorTest.java",
-    "javatests/src/org/chromium/mojo/bindings/ReadAndDispatchMessageTest.java",
-    "javatests/src/org/chromium/mojo/bindings/RouterTest.java",
-    "javatests/src/org/chromium/mojo/bindings/SerializationTest.java",
-    "javatests/src/org/chromium/mojo/bindings/test/mojom/mojo/IntegrationTestInterfaceTestHelper.java",
-    "javatests/src/org/chromium/mojo/bindings/ValidationTest.java",
-    "javatests/src/org/chromium/mojo/bindings/ValidationTestUtil.java",
-    "javatests/src/org/chromium/mojo/bindings/ValidationTestUtilTest.java",
-    "javatests/src/org/chromium/mojo/system/impl/CoreImplTest.java",
-    "javatests/src/org/chromium/mojo/system/impl/WatcherImplTest.java",
-  ]
-
-  deps = [
-    ":system_impl_java",
-    ":test_support_java",
-    "//base:base_java",
-    "//base:base_java_test_support",
-    "//mojo/public/interfaces/bindings/tests:test_interfaces_java",
-    "//mojo/public/interfaces/bindings/tests:test_mojom_import2_java",
-    "//mojo/public/interfaces/bindings/tests:test_mojom_import_java",
-    "//mojo/public/java:bindings_java",
-    "//mojo/public/java:system_java",
-    "//third_party/android_support_test_runner:runner_java",
-    "//third_party/junit",
-  ]
-
-  data = [
-    "//mojo/public/interfaces/bindings/tests/data/validation/",
-  ]
-}
-
-shared_library("mojo_java_unittests") {
-  testonly = true
-
-  sources = [
-    "javatests/init_library.cc",
-    "javatests/validation_test_util.cc",
-  ]
-
-  deps = [
-    ":jni_headers",
-    ":native_support",
-    ":system_impl_java_jni_headers",
-    ":test_support",
-    "//base",
-    "//base/test:test_support",
-    "//mojo/core/embedder",
-    "//mojo/public/cpp/bindings/tests:mojo_public_bindings_test_utils",
-    "//mojo/public/cpp/test_support:test_utils",
-  ]
-  defines = [ "UNIT_TEST" ]
-  configs -= [ "//build/config/android:hide_all_but_jni_onload" ]
-  configs += [ "//build/config/android:hide_all_but_jni" ]
-}
-
-instrumentation_test_apk("mojo_test_apk") {
-  deps = [
-    ":mojo_javatests",
-    ":system_impl_java",
-    "//base:base_java",
-    "//mojo/public/interfaces/bindings/tests:test_interfaces",
-    "//mojo/public/java:bindings_java",
-    "//third_party/android_support_test_runner:runner_java",
-  ]
-  shared_libraries = [ ":mojo_java_unittests" ]
-  apk_name = "MojoTest"
-  android_manifest = "javatests/AndroidManifest.xml"
-}
diff --git a/mojo/public/js/BUILD.gn b/mojo/public/js/BUILD.gn
deleted file mode 100644
index d6b97bd..0000000
--- a/mojo/public/js/BUILD.gn
+++ /dev/null
@@ -1,72 +0,0 @@
-# Copyright 2014 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//tools/grit/grit_rule.gni")
-
-interfaces_bindings_gen_dir = "$root_gen_dir/mojo/public/interfaces/bindings"
-
-action("bindings") {
-  bindings_js_files = [
-    # This must be the first file in the list, because it initializes global
-    # variable |mojo| that the others need to refer to.
-    "base.js",
-
-    "bindings.js",
-    "interface_types.js",
-    "lib/buffer.js",
-    "lib/codec.js",
-    "lib/connector.js",
-    "lib/control_message_handler.js",
-    "lib/control_message_proxy.js",
-    "lib/interface_endpoint_client.js",
-    "lib/interface_endpoint_handle.js",
-    "lib/pipe_control_message_handler.js",
-    "lib/pipe_control_message_proxy.js",
-    "lib/router.js",
-    "lib/unicode.js",
-    "lib/validator.js",
-
-    # These two needs to refer to codec.js.
-    "$interfaces_bindings_gen_dir/interface_control_messages.mojom.js",
-    "$interfaces_bindings_gen_dir/pipe_control_messages.mojom.js",
-  ]
-  compiled_file = "$target_gen_dir/mojo_bindings.js"
-
-  # TODO(yzshen): Eventually we would like to use Closure Compiler to minify the
-  # bindings instead of simply concatenating the files.
-  script = "//v8/tools/concatenate-files.py"
-
-  sources = bindings_js_files
-  outputs = [
-    compiled_file,
-  ]
-
-  args = rebase_path(bindings_js_files, root_build_dir)
-  args += [ rebase_path(compiled_file, root_build_dir) ]
-
-  deps = [
-    "//mojo/public/interfaces/bindings:bindings_js__generator",
-  ]
-}
-
-grit("resources") {
-  source = "mojo_bindings_resources.grd"
-
-  # The .grd contains references to generated files.
-  source_is_generated = true
-
-  outputs = [
-    "grit/mojo_bindings_resources.h",
-    "grit/mojo_bindings_resources_map.cc",
-    "grit/mojo_bindings_resources_map.h",
-    "mojo_bindings_resources.pak",
-  ]
-  grit_flags = [
-    "-E",
-    "root_gen_dir=" + rebase_path(root_gen_dir, root_build_dir),
-  ]
-  deps = [
-    ":bindings",
-  ]
-}
diff --git a/mojo/public/mojom/base/BUILD.gn b/mojo/public/mojom/base/BUILD.gn
deleted file mode 100644
index 807d4b9..0000000
--- a/mojo/public/mojom/base/BUILD.gn
+++ /dev/null
@@ -1,45 +0,0 @@
-# Copyright 2018 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//mojo/public/tools/bindings/mojom.gni")
-
-mojom_component("base") {
-  sources = [
-    "big_buffer.mojom",
-    "big_string.mojom",
-    "file.mojom",
-    "file_error.mojom",
-    "file_info.mojom",
-    "file_path.mojom",
-    "memory_allocator_dump_cross_process_uid.mojom",
-    "process_id.mojom",
-    "ref_counted_memory.mojom",
-    "shared_memory.mojom",
-    "string16.mojom",
-    "text_direction.mojom",
-    "thread_priority.mojom",
-    "time.mojom",
-    "unguessable_token.mojom",
-    "values.mojom",
-  ]
-
-  if (is_win) {
-    sources += [ "logfont_win.mojom" ]
-  }
-  enabled_features = []
-  if (is_win) {
-    enabled_features += [ "file_path_is_string16" ]
-  } else {
-    enabled_features += [ "file_path_is_string" ]
-  }
-
-  output_prefix = "mojo_base_mojom"
-  macro_prefix = "MOJO_BASE_MOJOM"
-}
-
-mojom("read_only_buffer") {
-  sources = [
-    "read_only_buffer.mojom",
-  ]
-}
diff --git a/mojo/public/mojom/base/logfont_win.mojom b/mojo/public/mojom/base/logfont_win.mojom
deleted file mode 100644
index 27922b8..0000000
--- a/mojo/public/mojom/base/logfont_win.mojom
+++ /dev/null
@@ -1,11 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-module mojo_base.mojom;
-
-// Native Windows struct.  Its typemap only exists for windows builds.
-[EnableIf=is_win]
-struct LOGFONT {
-  array<uint8> bytes;
-};
diff --git a/mojo/public/tools/bindings/BUILD.gn b/mojo/public/tools/bindings/BUILD.gn
deleted file mode 100644
index 4979617..0000000
--- a/mojo/public/tools/bindings/BUILD.gn
+++ /dev/null
@@ -1,86 +0,0 @@
-# Copyright 2016 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//mojo/public/tools/bindings/mojom.gni")
-import("//third_party/jinja2/jinja2.gni")
-
-action("precompile_templates") {
-  sources = mojom_generator_sources
-  sources += [
-    "$mojom_generator_root/generators/cpp_templates/enum_macros.tmpl",
-    "$mojom_generator_root/generators/cpp_templates/enum_serialization_declaration.tmpl",
-    "$mojom_generator_root/generators/cpp_templates/interface_declaration.tmpl",
-    "$mojom_generator_root/generators/cpp_templates/interface_definition.tmpl",
-    "$mojom_generator_root/generators/cpp_templates/interface_macros.tmpl",
-    "$mojom_generator_root/generators/cpp_templates/interface_proxy_declaration.tmpl",
-    "$mojom_generator_root/generators/cpp_templates/interface_request_validator_declaration.tmpl",
-    "$mojom_generator_root/generators/cpp_templates/interface_response_validator_declaration.tmpl",
-    "$mojom_generator_root/generators/cpp_templates/interface_stub_declaration.tmpl",
-    "$mojom_generator_root/generators/cpp_templates/module-shared-internal.h.tmpl",
-    "$mojom_generator_root/generators/cpp_templates/module-shared-message-ids.h.tmpl",
-    "$mojom_generator_root/generators/cpp_templates/module-shared.cc.tmpl",
-    "$mojom_generator_root/generators/cpp_templates/module-shared.h.tmpl",
-    "$mojom_generator_root/generators/cpp_templates/module.cc.tmpl",
-    "$mojom_generator_root/generators/cpp_templates/module.h.tmpl",
-    "$mojom_generator_root/generators/cpp_templates/struct_data_view_declaration.tmpl",
-    "$mojom_generator_root/generators/cpp_templates/struct_data_view_definition.tmpl",
-    "$mojom_generator_root/generators/cpp_templates/struct_declaration.tmpl",
-    "$mojom_generator_root/generators/cpp_templates/struct_definition.tmpl",
-    "$mojom_generator_root/generators/cpp_templates/struct_macros.tmpl",
-    "$mojom_generator_root/generators/cpp_templates/struct_serialization_declaration.tmpl",
-    "$mojom_generator_root/generators/cpp_templates/struct_traits_declaration.tmpl",
-    "$mojom_generator_root/generators/cpp_templates/struct_traits_definition.tmpl",
-    "$mojom_generator_root/generators/cpp_templates/struct_unserialized_message_context.tmpl",
-    "$mojom_generator_root/generators/cpp_templates/union_data_view_declaration.tmpl",
-    "$mojom_generator_root/generators/cpp_templates/union_data_view_definition.tmpl",
-    "$mojom_generator_root/generators/cpp_templates/union_declaration.tmpl",
-    "$mojom_generator_root/generators/cpp_templates/union_definition.tmpl",
-    "$mojom_generator_root/generators/cpp_templates/union_serialization_declaration.tmpl",
-    "$mojom_generator_root/generators/cpp_templates/union_traits_declaration.tmpl",
-    "$mojom_generator_root/generators/cpp_templates/union_traits_definition.tmpl",
-    "$mojom_generator_root/generators/cpp_templates/validation_macros.tmpl",
-    "$mojom_generator_root/generators/cpp_templates/wrapper_class_declaration.tmpl",
-    "$mojom_generator_root/generators/cpp_templates/wrapper_class_definition.tmpl",
-    "$mojom_generator_root/generators/cpp_templates/wrapper_class_template_definition.tmpl",
-    "$mojom_generator_root/generators/cpp_templates/wrapper_union_class_declaration.tmpl",
-    "$mojom_generator_root/generators/cpp_templates/wrapper_union_class_definition.tmpl",
-    "$mojom_generator_root/generators/cpp_templates/wrapper_union_class_template_definition.tmpl",
-    "$mojom_generator_root/generators/java_templates/constant_definition.tmpl",
-    "$mojom_generator_root/generators/java_templates/constants.java.tmpl",
-    "$mojom_generator_root/generators/java_templates/data_types_definition.tmpl",
-    "$mojom_generator_root/generators/java_templates/enum.java.tmpl",
-    "$mojom_generator_root/generators/java_templates/enum_definition.tmpl",
-    "$mojom_generator_root/generators/java_templates/header.java.tmpl",
-    "$mojom_generator_root/generators/java_templates/interface.java.tmpl",
-    "$mojom_generator_root/generators/java_templates/interface_definition.tmpl",
-    "$mojom_generator_root/generators/java_templates/interface_internal.java.tmpl",
-    "$mojom_generator_root/generators/java_templates/struct.java.tmpl",
-    "$mojom_generator_root/generators/java_templates/union.java.tmpl",
-    "$mojom_generator_root/generators/js_templates/enum_definition.tmpl",
-    "$mojom_generator_root/generators/js_templates/externs/interface_definition.tmpl",
-    "$mojom_generator_root/generators/js_templates/externs/module.externs.tmpl",
-    "$mojom_generator_root/generators/js_templates/externs/struct_definition.tmpl",
-    "$mojom_generator_root/generators/js_templates/fuzzing.tmpl",
-    "$mojom_generator_root/generators/js_templates/interface_definition.tmpl",
-    "$mojom_generator_root/generators/js_templates/module.amd.tmpl",
-    "$mojom_generator_root/generators/js_templates/module_definition.tmpl",
-    "$mojom_generator_root/generators/js_templates/struct_definition.tmpl",
-    "$mojom_generator_root/generators/js_templates/union_definition.tmpl",
-    "$mojom_generator_root/generators/js_templates/validation_macros.tmpl",
-  ]
-  script = mojom_generator_script
-
-  inputs = jinja2_sources
-  outputs = [
-    "$target_gen_dir/cpp_templates.zip",
-    "$target_gen_dir/java_templates.zip",
-    "$target_gen_dir/js_templates.zip",
-  ]
-  args = [
-    "--use_bundled_pylibs",
-    "precompile",
-    "-o",
-    rebase_path(target_gen_dir, root_build_dir),
-  ]
-}
diff --git a/mojo/public/tools/fuzzers/BUILD.gn b/mojo/public/tools/fuzzers/BUILD.gn
deleted file mode 100644
index 6953155..0000000
--- a/mojo/public/tools/fuzzers/BUILD.gn
+++ /dev/null
@@ -1,67 +0,0 @@
-# Copyright 2017 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-# Mojo fuzzing tools
-
-import("//build/config/features.gni")
-import("//mojo/public/tools/bindings/mojom.gni")
-import("//testing/libfuzzer/fuzzer_test.gni")
-import("//third_party/protobuf/proto_library.gni")
-
-# mojo/public BUILD depends on this target. Needed for package discovery
-group("fuzzers") {
-}
-
-mojom("fuzz_mojom") {
-  sources = [
-    "fuzz.mojom",
-  ]
-}
-
-fuzzer_test("mojo_parse_message_fuzzer") {
-  sources = [
-    "fuzz_impl.cc",
-    "mojo_parse_message_fuzzer.cc",
-  ]
-  deps = [
-    ":fuzz_mojom",
-    "//mojo/core/embedder",
-  ]
-  seed_corpus = "//mojo/public/tools/fuzzers/message_corpus"
-}
-
-# MessageDumper is not meant to work on Windows.
-if (!is_win) {
-  executable("mojo_fuzzer_message_dump") {
-    sources = [
-      "fuzz_impl.cc",
-      "mojo_fuzzer_message_dump.cc",
-    ]
-    deps = [
-      ":fuzz_mojom",
-      "//base",
-      "//mojo/core/embedder",
-    ]
-  }
-}
-
-fuzzer_test("mojo_parse_message_proto_fuzzer") {
-  sources = [
-    "fuzz_impl.cc",
-    "mojo_parse_message_proto_fuzzer.cc",
-  ]
-  deps = [
-    ":fuzz_mojom",
-    ":mojo_fuzzer_proto",
-    "//mojo/core/embedder",
-    "//third_party/libprotobuf-mutator",
-  ]
-  seed_corpus = "//mojo/public/tools/fuzzers/mojo_parse_message_proto_corpus"
-}
-
-proto_library("mojo_fuzzer_proto") {
-  sources = [
-    "mojo_fuzzer.proto",
-  ]
-}
diff --git a/testing/empty_main.cc b/testing/empty_main.cc
new file mode 100644
index 0000000..759687f
--- /dev/null
+++ b/testing/empty_main.cc
@@ -0,0 +1,8 @@
+// Copyright (c) 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Used by bots that want to check that compiler command lines still work.
+int main(int argc, char** argv) {
+  return 0;
+}
diff --git a/testing/gmock_mutant.h b/testing/gmock_mutant.h
new file mode 100644
index 0000000..2a41cf5
--- /dev/null
+++ b/testing/gmock_mutant.h
@@ -0,0 +1,130 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TESTING_GMOCK_MUTANT_H_
+#define TESTING_GMOCK_MUTANT_H_
+
+// The intention of this file is to make possible using GMock actions in
+// all of its syntactic beauty.
+//
+//
+// Sample usage with gMock:
+//
+// struct Mock : public ObjectDelegate {
+//   MOCK_METHOD2(string, OnRequest(int n, const string& request));
+//   MOCK_METHOD1(void, OnQuit(int exit_code));
+//   MOCK_METHOD2(void, LogMessage(int level, const string& message));
+//
+//   string HandleFlowers(const string& reply, int n, const string& request) {
+//     string result = SStringPrintf("In request of %d %s ", n, request);
+//     for (int i = 0; i < n; ++i) result.append(reply)
+//     return result;
+//   }
+//
+//   void DoLogMessage(int level, const string& message) {
+//   }
+//
+//   void QuitMessageLoop(int seconds) {
+//     base::MessageLoop* loop = base::MessageLoop::current();
+//     loop->PostDelayedTask(FROM_HERE,
+//                           base::MessageLoop::QuitWhenIdleClosure(),
+//                           1000 * seconds);
+//   }
+// };
+//
+// Mock mock;
+// // Will invoke mock.HandleFlowers("orchids", n, request)
+// // "orchids" is a pre-bound argument, and <n> and <request> are call-time
+// // arguments - they are not known until the OnRequest mock is invoked.
+// EXPECT_CALL(mock, OnRequest(Ge(5), base::StartsWith("flower"))
+//   .Times(1)
+//   .WillOnce(Invoke(CreateFunctor(
+//       &Mock::HandleFlowers, base::Unretained(&mock), string("orchids"))));
+//
+//
+// // No pre-bound arguments, two call-time arguments passed
+// // directly to DoLogMessage
+// EXPECT_CALL(mock, OnLogMessage(_, _))
+//   .Times(AnyNumber())
+//   .WillAlways(Invoke(CreateFunctor(
+//       &Mock::DoLogMessage, base::Unretained(&mock))));
+//
+//
+// // In this case we have a single pre-bound argument - 3. We ignore
+// // all of the arguments of OnQuit.
+// EXCEPT_CALL(mock, OnQuit(_))
+//   .Times(1)
+//   .WillOnce(InvokeWithoutArgs(CreateFunctor(
+//       &Mock::QuitMessageLoop, base::Unretained(&mock), 3)));
+//
+// MessageLoop loop;
+// loop.Run();
+//
+//
+//  // Here is another example of how we can set an action that invokes
+//  // method of an object that is not yet created.
+// struct Mock : public ObjectDelegate {
+//   MOCK_METHOD1(void, DemiurgeCreated(Demiurge*));
+//   MOCK_METHOD2(void, OnRequest(int count, const string&));
+//
+//   void StoreDemiurge(Demiurge* w) {
+//     demiurge_ = w;
+//   }
+//
+//   Demiurge* demiurge;
+// }
+//
+// EXPECT_CALL(mock, DemiurgeCreated(_)).Times(1)
+//    .WillOnce(Invoke(CreateFunctor(
+//        &Mock::StoreDemiurge, base::Unretained(&mock))));
+//
+// EXPECT_CALL(mock, OnRequest(_, StrEq("Moby Dick")))
+//    .Times(AnyNumber())
+//    .WillAlways(WithArgs<0>(Invoke(CreateFunctor(
+//        &Demiurge::DecreaseMonsters, base::Unretained(&mock->demiurge_)))));
+//
+
+#include "base/bind.h"
+
+namespace testing {
+
+template <typename Signature>
+class CallbackToFunctorHelper;
+
+template <typename R, typename... Args>
+class CallbackToFunctorHelper<R(Args...)> {
+ public:
+  explicit CallbackToFunctorHelper(const base::Callback<R(Args...)>& cb)
+      : cb_(cb) {}
+
+  template <typename... RunArgs>
+  R operator()(RunArgs&&... args) {
+    return cb_.Run(std::forward<RunArgs>(args)...);
+  }
+
+ private:
+  base::Callback<R(Args...)> cb_;
+};
+
+template <typename Signature>
+CallbackToFunctorHelper<Signature>
+CallbackToFunctor(const base::Callback<Signature>& cb) {
+  return CallbackToFunctorHelper<Signature>(cb);
+}
+
+template <typename Functor>
+CallbackToFunctorHelper<typename Functor::RunType> CreateFunctor(
+    Functor functor) {
+  return CallbackToFunctor(functor);
+}
+
+template <typename Functor, typename... BoundArgs>
+CallbackToFunctorHelper<base::MakeUnboundRunType<Functor, BoundArgs...>>
+CreateFunctor(Functor functor, const BoundArgs&... args) {
+  return CallbackToFunctor(base::Bind(functor, args...));
+}
+
+}  // namespace testing
+
+#endif  // TESTING_GMOCK_MUTANT_H_
diff --git a/ui/gfx/geometry/angle_conversions.h b/ui/gfx/geometry/angle_conversions.h
new file mode 100644
index 0000000..68e9209
--- /dev/null
+++ b/ui/gfx/geometry/angle_conversions.h
@@ -0,0 +1,29 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_GFX_GEOMETRY_ANGLE_CONVERSIONS_H_
+#define UI_GFX_GEOMETRY_ANGLE_CONVERSIONS_H_
+
+#include "base/numerics/math_constants.h"
+#include "ui/gfx/gfx_export.h"
+
+namespace gfx {
+
+GFX_EXPORT constexpr double DegToRad(double deg) {
+  return deg * base::kPiDouble / 180.0;
+}
+GFX_EXPORT constexpr float DegToRad(float deg) {
+  return deg * base::kPiFloat / 180.0f;
+}
+
+GFX_EXPORT constexpr double RadToDeg(double rad) {
+  return rad * 180.0 / base::kPiDouble;
+}
+GFX_EXPORT constexpr float RadToDeg(float rad) {
+  return rad * 180.0f / base::kPiFloat;
+}
+
+}  // namespace gfx
+
+#endif  // UI_GFX_GEOMETRY_ANGLE_CONVERSIONS_H_
diff --git a/ui/gfx/geometry/axis_transform2d.cc b/ui/gfx/geometry/axis_transform2d.cc
new file mode 100644
index 0000000..5b7d86a
--- /dev/null
+++ b/ui/gfx/geometry/axis_transform2d.cc
@@ -0,0 +1,16 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/gfx/geometry/axis_transform2d.h"
+
+#include "base/strings/stringprintf.h"
+
+namespace gfx {
+
+std::string AxisTransform2d::ToString() const {
+  return base::StringPrintf("[%f, %s]", scale_,
+                            translation_.ToString().c_str());
+}
+
+}  // namespace gfx
\ No newline at end of file
diff --git a/ui/gfx/geometry/axis_transform2d.h b/ui/gfx/geometry/axis_transform2d.h
new file mode 100644
index 0000000..1829bf6
--- /dev/null
+++ b/ui/gfx/geometry/axis_transform2d.h
@@ -0,0 +1,138 @@
+// Copyright (c) 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_GFX_GEOMETRY_AXIS_TRANSFORM2D_H_
+#define UI_GFX_GEOMETRY_AXIS_TRANSFORM2D_H_
+
+#include "ui/gfx/geometry/rect_f.h"
+#include "ui/gfx/geometry/vector2d_f.h"
+#include "ui/gfx/gfx_export.h"
+
+namespace gfx {
+
+// This class implements the subset of 2D linear transforms that only
+// translation and uniform scaling are allowed.
+// Internally this is stored as a scalar pre-scale factor, and a vector
+// for post-translation. The class constructor and member accessor follows
+// the same convention.
+class GFX_EXPORT AxisTransform2d {
+ public:
+  constexpr AxisTransform2d() = default;
+  constexpr AxisTransform2d(float scale, const Vector2dF& translation)
+      : scale_(scale), translation_(translation) {}
+
+  bool operator==(const AxisTransform2d& other) const {
+    return scale_ == other.scale_ && translation_ == other.translation_;
+  }
+  bool operator!=(const AxisTransform2d& other) const {
+    return !(*this == other);
+  }
+
+  void PreScale(float scale) { scale_ *= scale; }
+  void PostScale(float scale) {
+    scale_ *= scale;
+    translation_.Scale(scale);
+  }
+  void PreTranslate(const Vector2dF& translation) {
+    translation_ += ScaleVector2d(translation, scale_);
+  }
+  void PostTranslate(const Vector2dF& translation) {
+    translation_ += translation;
+  }
+
+  void PreConcat(const AxisTransform2d& pre) {
+    PreTranslate(pre.translation_);
+    PreScale(pre.scale_);
+  }
+  void PostConcat(const AxisTransform2d& post) {
+    PostScale(post.scale_);
+    PostTranslate(post.translation_);
+  }
+
+  void Invert() {
+    DCHECK(scale_);
+    scale_ = 1.f / scale_;
+    translation_.Scale(-scale_);
+  }
+
+  PointF MapPoint(const PointF& p) const {
+    return ScalePoint(p, scale_) + translation_;
+  }
+  PointF InverseMapPoint(const PointF& p) const {
+    return ScalePoint(p - translation_, 1.f / scale_);
+  }
+
+  RectF MapRect(const RectF& r) const {
+    DCHECK(scale_ >= 0.f);
+    return ScaleRect(r, scale_) + translation_;
+  }
+  RectF InverseMapRect(const RectF& r) const {
+    DCHECK(scale_ > 0.f);
+    return ScaleRect(r - translation_, 1.f / scale_);
+  }
+
+  float scale() const { return scale_; }
+  const Vector2dF& translation() const { return translation_; }
+
+  std::string ToString() const;
+
+ private:
+  // Scale is applied before translation, i.e.
+  // this->Transform(p) == scale_ * p + translation_
+  float scale_ = 1.f;
+  Vector2dF translation_;
+};
+
+static inline AxisTransform2d PreScaleAxisTransform2d(const AxisTransform2d& t,
+                                                      float scale) {
+  AxisTransform2d result(t);
+  result.PreScale(scale);
+  return result;
+}
+
+static inline AxisTransform2d PostScaleAxisTransform2d(const AxisTransform2d& t,
+                                                       float scale) {
+  AxisTransform2d result(t);
+  result.PostScale(scale);
+  return result;
+}
+
+static inline AxisTransform2d PreTranslateAxisTransform2d(
+    const AxisTransform2d& t,
+    const Vector2dF& translation) {
+  AxisTransform2d result(t);
+  result.PreTranslate(translation);
+  return result;
+}
+
+static inline AxisTransform2d PostTranslateAxisTransform2d(
+    const AxisTransform2d& t,
+    const Vector2dF& translation) {
+  AxisTransform2d result(t);
+  result.PostTranslate(translation);
+  return result;
+}
+
+static inline AxisTransform2d ConcatAxisTransform2d(
+    const AxisTransform2d& post,
+    const AxisTransform2d& pre) {
+  AxisTransform2d result(post);
+  result.PreConcat(pre);
+  return result;
+}
+
+static inline AxisTransform2d InvertAxisTransform2d(const AxisTransform2d& t) {
+  AxisTransform2d result = t;
+  result.Invert();
+  return result;
+}
+
+// This is declared here for use in gtest-based unit tests but is defined in
+// the //ui/gfx:test_support target. Depend on that to use this in your unit
+// test. This should not be used in production code - call ToString() instead.
+void PrintTo(const AxisTransform2d&, ::std::ostream* os);
+
+}  // namespace gfx
+
+#endif  // UI_GFX_GEOMETRY_AXIS_TRANSFORM2D_H_
diff --git a/ui/gfx/geometry/axis_transform2d_unittest.cc b/ui/gfx/geometry/axis_transform2d_unittest.cc
new file mode 100644
index 0000000..b132c69
--- /dev/null
+++ b/ui/gfx/geometry/axis_transform2d_unittest.cc
@@ -0,0 +1,91 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/gfx/geometry/axis_transform2d.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/test/gfx_util.h"
+
+namespace gfx {
+namespace {
+
+TEST(AxisTransform2dTest, Mapping) {
+  AxisTransform2d t(1.25f, Vector2dF(3.75f, 55.f));
+
+  PointF p(150.f, 100.f);
+  EXPECT_EQ(PointF(191.25f, 180.f), t.MapPoint(p));
+  EXPECT_POINTF_EQ(PointF(117.f, 36.f), t.InverseMapPoint(p));
+
+  RectF r(150.f, 100.f, 22.5f, 37.5f);
+  EXPECT_EQ(RectF(191.25f, 180.f, 28.125f, 46.875f), t.MapRect(r));
+  EXPECT_RECTF_EQ(RectF(117.f, 36.f, 18.f, 30.f), t.InverseMapRect(r));
+}
+
+TEST(AxisTransform2dTest, Scaling) {
+  {
+    AxisTransform2d t(1.25f, Vector2dF(3.75f, 55.f));
+    EXPECT_EQ(AxisTransform2d(1.5625f, Vector2dF(3.75f, 55.f)),
+              PreScaleAxisTransform2d(t, 1.25));
+    t.PreScale(1.25);
+    EXPECT_EQ(AxisTransform2d(1.5625f, Vector2dF(3.75f, 55.f)), t);
+  }
+
+  {
+    AxisTransform2d t(1.25f, Vector2dF(3.75f, 55.f));
+    EXPECT_EQ(AxisTransform2d(1.5625f, Vector2dF(4.6875f, 68.75f)),
+              PostScaleAxisTransform2d(t, 1.25));
+    t.PostScale(1.25);
+    EXPECT_EQ(AxisTransform2d(1.5625f, Vector2dF(4.6875f, 68.75f)), t);
+  }
+}
+
+TEST(AxisTransform2dTest, Translating) {
+  Vector2dF tr(3.f, -5.f);
+  {
+    AxisTransform2d t(1.25f, Vector2dF(3.75f, 55.f));
+    EXPECT_EQ(AxisTransform2d(1.25f, Vector2dF(7.5f, 48.75f)),
+              PreTranslateAxisTransform2d(t, tr));
+    t.PreTranslate(tr);
+    EXPECT_EQ(AxisTransform2d(1.25f, Vector2dF(7.5f, 48.75f)), t);
+  }
+
+  {
+    AxisTransform2d t(1.25f, Vector2dF(3.75f, 55.f));
+    EXPECT_EQ(AxisTransform2d(1.25f, Vector2dF(6.75f, 50.f)),
+              PostTranslateAxisTransform2d(t, tr));
+    t.PostTranslate(tr);
+    EXPECT_EQ(AxisTransform2d(1.25f, Vector2dF(6.75f, 50.f)), t);
+  }
+}
+
+TEST(AxisTransform2dTest, Concat) {
+  AxisTransform2d pre(1.25f, Vector2dF(3.75f, 55.f));
+  AxisTransform2d post(0.5f, Vector2dF(10.f, 5.f));
+  AxisTransform2d expectation(0.625f, Vector2dF(11.875f, 32.5f));
+  EXPECT_EQ(expectation, ConcatAxisTransform2d(post, pre));
+
+  AxisTransform2d post_concat = pre;
+  post_concat.PostConcat(post);
+  EXPECT_EQ(expectation, post_concat);
+
+  AxisTransform2d pre_concat = post;
+  pre_concat.PreConcat(pre);
+  EXPECT_EQ(expectation, pre_concat);
+}
+
+TEST(AxisTransform2dTest, Inverse) {
+  AxisTransform2d t(1.25f, Vector2dF(3.75f, 55.f));
+  AxisTransform2d inv_inplace = t;
+  inv_inplace.Invert();
+  AxisTransform2d inv_out_of_place = InvertAxisTransform2d(t);
+
+  EXPECT_AXIS_TRANSFORM2D_EQ(inv_inplace, inv_out_of_place);
+  EXPECT_AXIS_TRANSFORM2D_EQ(AxisTransform2d(),
+                             ConcatAxisTransform2d(t, inv_inplace));
+  EXPECT_AXIS_TRANSFORM2D_EQ(AxisTransform2d(),
+                             ConcatAxisTransform2d(inv_inplace, t));
+}
+
+}  // namespace
+}  // namespace gfx
diff --git a/ui/gfx/geometry/box_f.cc b/ui/gfx/geometry/box_f.cc
new file mode 100644
index 0000000..674bb50
--- /dev/null
+++ b/ui/gfx/geometry/box_f.cc
@@ -0,0 +1,70 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/gfx/geometry/box_f.h"
+
+#include <algorithm>
+
+#include "base/logging.h"
+#include "base/strings/stringprintf.h"
+
+namespace gfx {
+
+std::string BoxF::ToString() const {
+  return base::StringPrintf("%s %fx%fx%f",
+                            origin().ToString().c_str(),
+                            width_,
+                            height_,
+                            depth_);
+}
+
+bool BoxF::IsEmpty() const {
+  return (width_ == 0 && height_ == 0) ||
+         (width_ == 0 && depth_ == 0) ||
+         (height_ == 0 && depth_ == 0);
+}
+
+void BoxF::ExpandTo(const Point3F& min, const Point3F& max) {
+  DCHECK_LE(min.x(), max.x());
+  DCHECK_LE(min.y(), max.y());
+  DCHECK_LE(min.z(), max.z());
+
+  float min_x = std::min(x(), min.x());
+  float min_y = std::min(y(), min.y());
+  float min_z = std::min(z(), min.z());
+  float max_x = std::max(right(), max.x());
+  float max_y = std::max(bottom(), max.y());
+  float max_z = std::max(front(), max.z());
+
+  origin_.SetPoint(min_x, min_y, min_z);
+  width_ = max_x - min_x;
+  height_ = max_y - min_y;
+  depth_ = max_z - min_z;
+}
+
+void BoxF::Union(const BoxF& box) {
+  if (IsEmpty()) {
+    *this = box;
+    return;
+  }
+  if (box.IsEmpty())
+    return;
+  ExpandTo(box);
+}
+
+void BoxF::ExpandTo(const Point3F& point) {
+  ExpandTo(point, point);
+}
+
+void BoxF::ExpandTo(const BoxF& box) {
+  ExpandTo(box.origin(), gfx::Point3F(box.right(), box.bottom(), box.front()));
+}
+
+BoxF UnionBoxes(const BoxF& a, const BoxF& b) {
+  BoxF result = a;
+  result.Union(b);
+  return result;
+}
+
+}  // namespace gfx
diff --git a/ui/gfx/geometry/box_f.h b/ui/gfx/geometry/box_f.h
new file mode 100644
index 0000000..b4d9eff
--- /dev/null
+++ b/ui/gfx/geometry/box_f.h
@@ -0,0 +1,160 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_GFX_GEOMETRY_BOX_F_H_
+#define UI_GFX_GEOMETRY_BOX_F_H_
+
+#include <iosfwd>
+#include <string>
+
+#include "ui/gfx/geometry/point3_f.h"
+#include "ui/gfx/geometry/vector3d_f.h"
+
+namespace gfx {
+
+// A 3d version of gfx::RectF, with the positive z-axis pointed towards
+// the camera.
+class GFX_EXPORT BoxF {
+ public:
+  constexpr BoxF() : BoxF(0, 0, 0) {}
+  constexpr BoxF(float width, float height, float depth)
+      : BoxF(0, 0, 0, width, height, depth) {}
+  constexpr BoxF(float x,
+                 float y,
+                 float z,
+                 float width,
+                 float height,
+                 float depth)
+      : BoxF(Point3F(x, y, z), width, height, depth) {}
+  constexpr BoxF(const Point3F& origin, float width, float height, float depth)
+      : origin_(origin),
+        width_(width >= 0 ? width : 0),
+        height_(height >= 0 ? height : 0),
+        depth_(depth >= 0 ? depth : 0) {}
+
+  // Scales all three axes by the given scale.
+  void Scale(float scale) {
+    Scale(scale, scale, scale);
+  }
+
+  // Scales each axis by the corresponding given scale.
+  void Scale(float x_scale, float y_scale, float z_scale) {
+    origin_.Scale(x_scale, y_scale, z_scale);
+    set_size(width_ * x_scale, height_ * y_scale, depth_ * z_scale);
+  }
+
+  // Moves the box by the specified distance in each dimension.
+  void operator+=(const Vector3dF& offset) {
+    origin_ += offset;
+  }
+
+  // Returns true if the box has no interior points.
+  bool IsEmpty() const;
+
+  // Computes the union of this box with the given box. The union is the
+  // smallest box that contains both boxes.
+  void Union(const BoxF& box);
+
+  std::string ToString() const;
+
+  constexpr float x() const { return origin_.x(); }
+  void set_x(float x) { origin_.set_x(x); }
+
+  constexpr float y() const { return origin_.y(); }
+  void set_y(float y) { origin_.set_y(y); }
+
+  constexpr float z() const { return origin_.z(); }
+  void set_z(float z) { origin_.set_z(z); }
+
+  constexpr float width() const { return width_; }
+  void set_width(float width) { width_ = width < 0 ? 0 : width; }
+
+  constexpr float height() const { return height_; }
+  void set_height(float height) { height_ = height < 0 ? 0 : height; }
+
+  constexpr float depth() const { return depth_; }
+  void set_depth(float depth) { depth_ = depth < 0 ? 0 : depth; }
+
+  constexpr float right() const { return x() + width(); }
+  constexpr float bottom() const { return y() + height(); }
+  constexpr float front() const { return z() + depth(); }
+
+  void set_size(float width, float height, float depth) {
+    width_ = width < 0 ? 0 : width;
+    height_ = height < 0 ? 0 : height;
+    depth_ = depth < 0 ? 0 : depth;
+  }
+
+  constexpr const Point3F& origin() const { return origin_; }
+  void set_origin(const Point3F& origin) { origin_ = origin; }
+
+  // Expands |this| to contain the given point, if necessary. Please note, even
+  // if |this| is empty, after the function |this| will continue to contain its
+  // |origin_|.
+  void ExpandTo(const Point3F& point);
+
+  // Expands |this| to contain the given box, if necessary. Please note, even
+  // if |this| is empty, after the function |this| will continue to contain its
+  // |origin_|.
+  void ExpandTo(const BoxF& box);
+
+ private:
+  // Expands the box to contain the two given points. It is required that each
+  // component of |min| is less than or equal to the corresponding component in
+  // |max|. Precisely, what this function does is ensure that after the function
+  // completes, |this| contains origin_, min, max, and origin_ + (width_,
+  // height_, depth_), even if the box is empty. Emptiness checks are handled in
+  // the public function Union.
+  void ExpandTo(const Point3F& min, const Point3F& max);
+
+  Point3F origin_;
+  float width_;
+  float height_;
+  float depth_;
+};
+
+GFX_EXPORT BoxF UnionBoxes(const BoxF& a, const BoxF& b);
+
+inline BoxF ScaleBox(const BoxF& b,
+                     float x_scale,
+                     float y_scale,
+                     float z_scale) {
+  return BoxF(b.x() * x_scale,
+              b.y() * y_scale,
+              b.z() * z_scale,
+              b.width() * x_scale,
+              b.height() * y_scale,
+              b.depth() * z_scale);
+}
+
+inline BoxF ScaleBox(const BoxF& b, float scale) {
+  return ScaleBox(b, scale, scale, scale);
+}
+
+inline bool operator==(const BoxF& a, const BoxF& b) {
+  return a.origin() == b.origin() && a.width() == b.width() &&
+         a.height() == b.height() && a.depth() == b.depth();
+}
+
+inline bool operator!=(const BoxF& a, const BoxF& b) {
+  return !(a == b);
+}
+
+inline BoxF operator+(const BoxF& b, const Vector3dF& v) {
+  return BoxF(b.x() + v.x(),
+              b.y() + v.y(),
+              b.z() + v.z(),
+              b.width(),
+              b.height(),
+              b.depth());
+}
+
+// This is declared here for use in gtest-based unit tests but is defined in
+// the //ui/gfx:test_support target. Depend on that to use this in your unit
+// test. This should not be used in production code - call ToString() instead.
+void PrintTo(const BoxF& box, ::std::ostream* os);
+
+}  // namespace gfx
+
+#endif  // UI_GFX_GEOMETRY_BOX_F_H_
diff --git a/ui/gfx/geometry/box_unittest.cc b/ui/gfx/geometry/box_unittest.cc
new file mode 100644
index 0000000..990fccc
--- /dev/null
+++ b/ui/gfx/geometry/box_unittest.cc
@@ -0,0 +1,175 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/geometry/box_f.h"
+
+namespace gfx {
+
+TEST(BoxTest, Constructors) {
+  EXPECT_EQ(BoxF(0.f, 0.f, 0.f, 0.f, 0.f, 0.f).ToString(),
+            BoxF().ToString());
+  EXPECT_EQ(BoxF(0.f, 0.f, 0.f, -3.f, -5.f, -7.f).ToString(),
+            BoxF().ToString());
+
+  EXPECT_EQ(BoxF(0.f, 0.f, 0.f, 3.f, 5.f, 7.f).ToString(),
+            BoxF(3.f, 5.f, 7.f).ToString());
+  EXPECT_EQ(BoxF(0.f, 0.f, 0.f, 0.f, 0.f, 0.f).ToString(),
+            BoxF(-3.f, -5.f, -7.f).ToString());
+
+  EXPECT_EQ(BoxF(2.f, 4.f, 6.f, 3.f, 5.f, 7.f).ToString(),
+            BoxF(Point3F(2.f, 4.f, 6.f), 3.f, 5.f, 7.f).ToString());
+  EXPECT_EQ(BoxF(2.f, 4.f, 6.f, 0.f, 0.f, 0.f).ToString(),
+            BoxF(Point3F(2.f, 4.f, 6.f), -3.f, -5.f, -7.f).ToString());
+}
+
+TEST(BoxTest, IsEmpty) {
+  EXPECT_TRUE(BoxF(0.f, 0.f, 0.f, 0.f, 0.f, 0.f).IsEmpty());
+  EXPECT_TRUE(BoxF(1.f, 2.f, 3.f, 0.f, 0.f, 0.f).IsEmpty());
+
+  EXPECT_TRUE(BoxF(0.f, 0.f, 0.f, 2.f, 0.f, 0.f).IsEmpty());
+  EXPECT_TRUE(BoxF(1.f, 2.f, 3.f, 2.f, 0.f, 0.f).IsEmpty());
+  EXPECT_TRUE(BoxF(0.f, 0.f, 0.f, 0.f, 2.f, 0.f).IsEmpty());
+  EXPECT_TRUE(BoxF(1.f, 2.f, 3.f, 0.f, 2.f, 0.f).IsEmpty());
+  EXPECT_TRUE(BoxF(0.f, 0.f, 0.f, 0.f, 0.f, 2.f).IsEmpty());
+  EXPECT_TRUE(BoxF(1.f, 2.f, 3.f, 0.f, 0.f, 2.f).IsEmpty());
+
+  EXPECT_FALSE(BoxF(0.f, 0.f, 0.f, 0.f, 2.f, 2.f).IsEmpty());
+  EXPECT_FALSE(BoxF(1.f, 2.f, 3.f, 0.f, 2.f, 2.f).IsEmpty());
+  EXPECT_FALSE(BoxF(0.f, 0.f, 0.f, 2.f, 0.f, 2.f).IsEmpty());
+  EXPECT_FALSE(BoxF(1.f, 2.f, 3.f, 2.f, 0.f, 2.f).IsEmpty());
+  EXPECT_FALSE(BoxF(0.f, 0.f, 0.f, 2.f, 2.f, 0.f).IsEmpty());
+  EXPECT_FALSE(BoxF(1.f, 2.f, 3.f, 2.f, 2.f, 0.f).IsEmpty());
+
+  EXPECT_FALSE(BoxF(0.f, 0.f, 0.f, 2.f, 2.f, 2.f).IsEmpty());
+  EXPECT_FALSE(BoxF(1.f, 2.f, 3.f, 2.f, 2.f, 2.f).IsEmpty());
+}
+
+TEST(BoxTest, Union) {
+  BoxF empty_box;
+  BoxF box1(0.f, 0.f, 0.f, 1.f, 1.f, 1.f);
+  BoxF box2(0.f, 0.f, 0.f, 4.f, 6.f, 8.f);
+  BoxF box3(3.f, 4.f, 5.f, 6.f, 4.f, 0.f);
+
+  EXPECT_EQ(empty_box.ToString(), UnionBoxes(empty_box, empty_box).ToString());
+  EXPECT_EQ(box1.ToString(), UnionBoxes(empty_box, box1).ToString());
+  EXPECT_EQ(box1.ToString(), UnionBoxes(box1, empty_box).ToString());
+  EXPECT_EQ(box2.ToString(), UnionBoxes(empty_box, box2).ToString());
+  EXPECT_EQ(box2.ToString(), UnionBoxes(box2, empty_box).ToString());
+  EXPECT_EQ(box3.ToString(), UnionBoxes(empty_box, box3).ToString());
+  EXPECT_EQ(box3.ToString(), UnionBoxes(box3, empty_box).ToString());
+
+  // box_1 is contained in box_2
+  EXPECT_EQ(box2.ToString(), UnionBoxes(box1, box2).ToString());
+  EXPECT_EQ(box2.ToString(), UnionBoxes(box2, box1).ToString());
+
+  // box_1 and box_3 are disjoint
+  EXPECT_EQ(BoxF(0.f, 0.f, 0.f, 9.f, 8.f, 5.f).ToString(),
+            UnionBoxes(box1, box3).ToString());
+  EXPECT_EQ(BoxF(0.f, 0.f, 0.f, 9.f, 8.f, 5.f).ToString(),
+            UnionBoxes(box3, box1).ToString());
+
+  // box_2 and box_3 intersect, but neither contains the other
+  EXPECT_EQ(BoxF(0.f, 0.f, 0.f, 9.f, 8.f, 8.f).ToString(),
+            UnionBoxes(box2, box3).ToString());
+  EXPECT_EQ(BoxF(0.f, 0.f, 0.f, 9.f, 8.f, 8.f).ToString(),
+            UnionBoxes(box3, box2).ToString());
+}
+
+TEST(BoxTest, ExpandTo) {
+  BoxF box1;
+  BoxF box2(0.f, 0.f, 0.f, 1.f, 1.f, 1.f);
+  BoxF box3(1.f, 1.f, 1.f, 0.f, 0.f, 0.f);
+
+  Point3F point1(0.5f, 0.5f, 0.5f);
+  Point3F point2(-0.5f, -0.5f, -0.5f);
+
+  BoxF expected1_1(0.f, 0.f, 0.f, 0.5f, 0.5f, 0.5f);
+  BoxF expected1_2(-0.5f, -0.5f, -0.5f, 1.f, 1.f, 1.f);
+
+  BoxF expected2_1 = box2;
+  BoxF expected2_2(-0.5f, -0.5f, -0.5f, 1.5f, 1.5f, 1.5f);
+
+  BoxF expected3_1(0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f);
+  BoxF expected3_2(-0.5f, -0.5f, -0.5f, 1.5f, 1.5f, 1.5f);
+
+  box1.ExpandTo(point1);
+  EXPECT_EQ(expected1_1.ToString(), box1.ToString());
+  box1.ExpandTo(point2);
+  EXPECT_EQ(expected1_2.ToString(), box1.ToString());
+
+  box2.ExpandTo(point1);
+  EXPECT_EQ(expected2_1.ToString(), box2.ToString());
+  box2.ExpandTo(point2);
+  EXPECT_EQ(expected2_2.ToString(), box2.ToString());
+
+  box3.ExpandTo(point1);
+  EXPECT_EQ(expected3_1.ToString(), box3.ToString());
+  box3.ExpandTo(point2);
+  EXPECT_EQ(expected3_2.ToString(), box3.ToString());
+}
+
+TEST(BoxTest, Scale) {
+  BoxF box1(2.f, 3.f, 4.f, 5.f, 6.f, 7.f);
+
+  EXPECT_EQ(BoxF().ToString(), ScaleBox(box1, 0.f).ToString());
+  EXPECT_EQ(box1.ToString(), ScaleBox(box1, 1.f).ToString());
+  EXPECT_EQ(BoxF(4.f, 12.f, 24.f, 10.f, 24.f, 42.f).ToString(),
+            ScaleBox(box1, 2.f, 4.f, 6.f).ToString());
+
+  BoxF box2 = box1;
+  box2.Scale(0.f);
+  EXPECT_EQ(BoxF().ToString(), box2.ToString());
+
+  box2 = box1;
+  box2.Scale(1.f);
+  EXPECT_EQ(box1.ToString(), box2.ToString());
+
+  box2.Scale(2.f, 4.f, 6.f);
+  EXPECT_EQ(BoxF(4.f, 12.f, 24.f, 10.f, 24.f, 42.f).ToString(),
+            box2.ToString());
+}
+
+TEST(BoxTest, Equals) {
+  EXPECT_TRUE(BoxF() == BoxF());
+  EXPECT_TRUE(BoxF(2.f, 3.f, 4.f, 6.f, 8.f, 10.f) ==
+              BoxF(2.f, 3.f, 4.f, 6.f, 8.f, 10.f));
+  EXPECT_FALSE(BoxF() == BoxF(0.f, 0.f, 0.f, 0.f, 0.f, 1.f));
+  EXPECT_FALSE(BoxF() == BoxF(0.f, 0.f, 0.f, 0.f, 1.f, 0.f));
+  EXPECT_FALSE(BoxF() == BoxF(0.f, 0.f, 0.f, 1.f, 0.f, 0.f));
+  EXPECT_FALSE(BoxF() == BoxF(0.f, 0.f, 1.f, 0.f, 0.f, 0.f));
+  EXPECT_FALSE(BoxF() == BoxF(0.f, 1.f, 0.f, 0.f, 0.f, 0.f));
+  EXPECT_FALSE(BoxF() == BoxF(1.f, 0.f, 0.f, 0.f, 0.f, 0.f));
+}
+
+TEST(BoxTest, NotEquals) {
+  EXPECT_FALSE(BoxF() != BoxF());
+  EXPECT_FALSE(BoxF(2.f, 3.f, 4.f, 6.f, 8.f, 10.f) !=
+               BoxF(2.f, 3.f, 4.f, 6.f, 8.f, 10.f));
+  EXPECT_TRUE(BoxF() != BoxF(0.f, 0.f, 0.f, 0.f, 0.f, 1.f));
+  EXPECT_TRUE(BoxF() != BoxF(0.f, 0.f, 0.f, 0.f, 1.f, 0.f));
+  EXPECT_TRUE(BoxF() != BoxF(0.f, 0.f, 0.f, 1.f, 0.f, 0.f));
+  EXPECT_TRUE(BoxF() != BoxF(0.f, 0.f, 1.f, 0.f, 0.f, 0.f));
+  EXPECT_TRUE(BoxF() != BoxF(0.f, 1.f, 0.f, 0.f, 0.f, 0.f));
+  EXPECT_TRUE(BoxF() != BoxF(1.f, 0.f, 0.f, 0.f, 0.f, 0.f));
+}
+
+
+TEST(BoxTest, Offset) {
+  BoxF box1(2.f, 3.f, 4.f, 5.f, 6.f, 7.f);
+
+  EXPECT_EQ(box1.ToString(), (box1 + Vector3dF(0.f, 0.f, 0.f)).ToString());
+  EXPECT_EQ(BoxF(3.f, 1.f, 0.f, 5.f, 6.f, 7.f).ToString(),
+            (box1 + Vector3dF(1.f, -2.f, -4.f)).ToString());
+
+  BoxF box2 = box1;
+  box2 += Vector3dF(0.f, 0.f, 0.f);
+  EXPECT_EQ(box1.ToString(), box2.ToString());
+
+  box2 += Vector3dF(1.f, -2.f, -4.f);
+  EXPECT_EQ(BoxF(3.f, 1.f, 0.f, 5.f, 6.f, 7.f).ToString(),
+            box2.ToString());
+}
+
+}  // namespace gfx
diff --git a/ui/gfx/geometry/cubic_bezier.cc b/ui/gfx/geometry/cubic_bezier.cc
new file mode 100644
index 0000000..e42d893
--- /dev/null
+++ b/ui/gfx/geometry/cubic_bezier.cc
@@ -0,0 +1,213 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/gfx/geometry/cubic_bezier.h"
+
+#include <algorithm>
+#include <cmath>
+
+#include "base/logging.h"
+
+namespace gfx {
+
+static const double kBezierEpsilon = 1e-7;
+
+CubicBezier::CubicBezier(double p1x, double p1y, double p2x, double p2y) {
+  InitCoefficients(p1x, p1y, p2x, p2y);
+  InitGradients(p1x, p1y, p2x, p2y);
+  InitRange(p1y, p2y);
+}
+
+CubicBezier::CubicBezier(const CubicBezier& other) = default;
+
+void CubicBezier::InitCoefficients(double p1x,
+                                   double p1y,
+                                   double p2x,
+                                   double p2y) {
+  // Calculate the polynomial coefficients, implicit first and last control
+  // points are (0,0) and (1,1).
+  cx_ = 3.0 * p1x;
+  bx_ = 3.0 * (p2x - p1x) - cx_;
+  ax_ = 1.0 - cx_ - bx_;
+
+  cy_ = 3.0 * p1y;
+  by_ = 3.0 * (p2y - p1y) - cy_;
+  ay_ = 1.0 - cy_ - by_;
+}
+
+void CubicBezier::InitGradients(double p1x,
+                                double p1y,
+                                double p2x,
+                                double p2y) {
+  // End-point gradients are used to calculate timing function results
+  // outside the range [0, 1].
+  //
+  // There are three possibilities for the gradient at each end:
+  // (1) the closest control point is not horizontally coincident with regard to
+  //     (0, 0) or (1, 1). In this case the line between the end point and
+  //     the control point is tangent to the bezier at the end point.
+  // (2) the closest control point is coincident with the end point. In
+  //     this case the line between the end point and the far control
+  //     point is tangent to the bezier at the end point.
+  // (3) the closest control point is horizontally coincident with the end
+  //     point, but vertically distinct. In this case the gradient at the
+  //     end point is Infinite. However, this causes issues when
+  //     interpolating. As a result, we break down to a simple case of
+  //     0 gradient under these conditions.
+
+  if (p1x > 0)
+    start_gradient_ = p1y / p1x;
+  else if (!p1y && p2x > 0)
+    start_gradient_ = p2y / p2x;
+  else
+    start_gradient_ = 0;
+
+  if (p2x < 1)
+    end_gradient_ = (p2y - 1) / (p2x - 1);
+  else if (p2x == 1 && p1x < 1)
+    end_gradient_ = (p1y - 1) / (p1x - 1);
+  else
+    end_gradient_ = 0;
+}
+
+// This works by taking taking the derivative of the cubic bezier, on the y
+// axis. We can then solve for where the derivative is zero to find the min
+// and max distance along the line. We the have to solve those in terms of time
+// rather than distance on the x-axis
+void CubicBezier::InitRange(double p1y, double p2y) {
+  range_min_ = 0;
+  range_max_ = 1;
+  if (0 <= p1y && p1y < 1 && 0 <= p2y && p2y <= 1)
+    return;
+
+  const double epsilon = kBezierEpsilon;
+
+  // Represent the function's derivative in the form at^2 + bt + c
+  // as in sampleCurveDerivativeY.
+  // (Technically this is (dy/dt)*(1/3), which is suitable for finding zeros
+  // but does not actually give the slope of the curve.)
+  const double a = 3.0 * ay_;
+  const double b = 2.0 * by_;
+  const double c = cy_;
+
+  // Check if the derivative is constant.
+  if (std::abs(a) < epsilon && std::abs(b) < epsilon)
+    return;
+
+  // Zeros of the function's derivative.
+  double t1 = 0;
+  double t2 = 0;
+
+  if (std::abs(a) < epsilon) {
+    // The function's derivative is linear.
+    t1 = -c / b;
+  } else {
+    // The function's derivative is a quadratic. We find the zeros of this
+    // quadratic using the quadratic formula.
+    double discriminant = b * b - 4 * a * c;
+    if (discriminant < 0)
+      return;
+    double discriminant_sqrt = sqrt(discriminant);
+    t1 = (-b + discriminant_sqrt) / (2 * a);
+    t2 = (-b - discriminant_sqrt) / (2 * a);
+  }
+
+  double sol1 = 0;
+  double sol2 = 0;
+
+  // If the solution is in the range [0,1] then we include it, otherwise we
+  // ignore it.
+
+  // An interesting fact about these beziers is that they are only
+  // actually evaluated in [0,1]. After that we take the tangent at that point
+  // and linearly project it out.
+  if (0 < t1 && t1 < 1)
+    sol1 = SampleCurveY(t1);
+
+  if (0 < t2 && t2 < 1)
+    sol2 = SampleCurveY(t2);
+
+  range_min_ = std::min(std::min(range_min_, sol1), sol2);
+  range_max_ = std::max(std::max(range_max_, sol1), sol2);
+}
+
+double CubicBezier::GetDefaultEpsilon() {
+  return kBezierEpsilon;
+}
+
+double CubicBezier::SolveCurveX(double x, double epsilon) const {
+  DCHECK_GE(x, 0.0);
+  DCHECK_LE(x, 1.0);
+
+  double t0;
+  double t1;
+  double t2;
+  double x2;
+  double d2;
+  int i;
+
+  // First try a few iterations of Newton's method -- normally very fast.
+  for (t2 = x, i = 0; i < 8; i++) {
+    x2 = SampleCurveX(t2) - x;
+    if (fabs(x2) < epsilon)
+      return t2;
+    d2 = SampleCurveDerivativeX(t2);
+    if (fabs(d2) < 1e-6)
+      break;
+    t2 = t2 - x2 / d2;
+  }
+
+  // Fall back to the bisection method for reliability.
+  t0 = 0.0;
+  t1 = 1.0;
+  t2 = x;
+
+  while (t0 < t1) {
+    x2 = SampleCurveX(t2);
+    if (fabs(x2 - x) < epsilon)
+      return t2;
+    if (x > x2)
+      t0 = t2;
+    else
+      t1 = t2;
+    t2 = (t1 - t0) * .5 + t0;
+  }
+
+  // Failure.
+  return t2;
+}
+
+double CubicBezier::Solve(double x) const {
+  return SolveWithEpsilon(x, kBezierEpsilon);
+}
+
+double CubicBezier::SlopeWithEpsilon(double x, double epsilon) const {
+  x = std::min(std::max(x, 0.0), 1.0);
+  double t = SolveCurveX(x, epsilon);
+  double dx = SampleCurveDerivativeX(t);
+  double dy = SampleCurveDerivativeY(t);
+  return dy / dx;
+}
+
+double CubicBezier::Slope(double x) const {
+  return SlopeWithEpsilon(x, kBezierEpsilon);
+}
+
+double CubicBezier::GetX1() const {
+  return cx_ / 3.0;
+}
+
+double CubicBezier::GetY1() const {
+  return cy_ / 3.0;
+}
+
+double CubicBezier::GetX2() const {
+  return (bx_ + cx_) / 3.0 + GetX1();
+}
+
+double CubicBezier::GetY2() const {
+  return (by_ + cy_) / 3.0 + GetY1();
+}
+
+}  // namespace gfx
diff --git a/ui/gfx/geometry/cubic_bezier.h b/ui/gfx/geometry/cubic_bezier.h
new file mode 100644
index 0000000..0131239
--- /dev/null
+++ b/ui/gfx/geometry/cubic_bezier.h
@@ -0,0 +1,96 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_GFX_GEOMETRY_CUBIC_BEZIER_H_
+#define UI_GFX_GEOMETRY_CUBIC_BEZIER_H_
+
+#include "base/macros.h"
+#include "ui/gfx/gfx_export.h"
+
+namespace gfx {
+
+class GFX_EXPORT CubicBezier {
+ public:
+  CubicBezier(double p1x, double p1y, double p2x, double p2y);
+  CubicBezier(const CubicBezier& other);
+
+  double SampleCurveX(double t) const {
+    // `ax t^3 + bx t^2 + cx t' expanded using Horner's rule.
+    return ((ax_ * t + bx_) * t + cx_) * t;
+  }
+
+  double SampleCurveY(double t) const {
+    return ((ay_ * t + by_) * t + cy_) * t;
+  }
+
+  double SampleCurveDerivativeX(double t) const {
+    return (3.0 * ax_ * t + 2.0 * bx_) * t + cx_;
+  }
+
+  double SampleCurveDerivativeY(double t) const {
+    return (3.0 * ay_ * t + 2.0 * by_) * t + cy_;
+  }
+
+  static double GetDefaultEpsilon();
+
+  // Given an x value, find a parametric value it came from.
+  // x must be in [0, 1] range. Doesn't use gradients.
+  double SolveCurveX(double x, double epsilon) const;
+
+  // Evaluates y at the given x with default epsilon.
+  double Solve(double x) const;
+  // Evaluates y at the given x. The epsilon parameter provides a hint as to the
+  // required accuracy and is not guaranteed. Uses gradients if x is
+  // out of [0, 1] range.
+  double SolveWithEpsilon(double x, double epsilon) const {
+    if (x < 0.0)
+      return 0.0 + start_gradient_ * x;
+    if (x > 1.0)
+      return 1.0 + end_gradient_ * (x - 1.0);
+    return SampleCurveY(SolveCurveX(x, epsilon));
+  }
+
+  // Returns an approximation of dy/dx at the given x with default epsilon.
+  double Slope(double x) const;
+  // Returns an approximation of dy/dx at the given x.
+  // Clamps x to range [0, 1].
+  double SlopeWithEpsilon(double x, double epsilon) const;
+
+  // These getters are used rarely. We reverse compute them from coefficients.
+  // See CubicBezier::InitCoefficients. The speed has been traded for memory.
+  double GetX1() const;
+  double GetY1() const;
+  double GetX2() const;
+  double GetY2() const;
+
+  // Gets the bezier's minimum y value in the interval [0, 1].
+  double range_min() const { return range_min_; }
+  // Gets the bezier's maximum y value in the interval [0, 1].
+  double range_max() const { return range_max_; }
+
+ private:
+  void InitCoefficients(double p1x, double p1y, double p2x, double p2y);
+  void InitGradients(double p1x, double p1y, double p2x, double p2y);
+  void InitRange(double p1y, double p2y);
+
+  double ax_;
+  double bx_;
+  double cx_;
+
+  double ay_;
+  double by_;
+  double cy_;
+
+  double start_gradient_;
+  double end_gradient_;
+
+  double range_min_;
+  double range_max_;
+
+  DISALLOW_ASSIGN(CubicBezier);
+};
+
+}  // namespace gfx
+
+#endif  // UI_GFX_GEOMETRY_CUBIC_BEZIER_H_
diff --git a/ui/gfx/geometry/cubic_bezier_unittest.cc b/ui/gfx/geometry/cubic_bezier_unittest.cc
new file mode 100644
index 0000000..74ccb6d
--- /dev/null
+++ b/ui/gfx/geometry/cubic_bezier_unittest.cc
@@ -0,0 +1,230 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/gfx/geometry/cubic_bezier.h"
+
+#include <memory>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace gfx {
+namespace {
+
+TEST(CubicBezierTest, Basic) {
+  CubicBezier function(0.25, 0.0, 0.75, 1.0);
+
+  double epsilon = 0.00015;
+
+  EXPECT_NEAR(function.Solve(0), 0, epsilon);
+  EXPECT_NEAR(function.Solve(0.05), 0.01136, epsilon);
+  EXPECT_NEAR(function.Solve(0.1), 0.03978, epsilon);
+  EXPECT_NEAR(function.Solve(0.15), 0.079780, epsilon);
+  EXPECT_NEAR(function.Solve(0.2), 0.12803, epsilon);
+  EXPECT_NEAR(function.Solve(0.25), 0.18235, epsilon);
+  EXPECT_NEAR(function.Solve(0.3), 0.24115, epsilon);
+  EXPECT_NEAR(function.Solve(0.35), 0.30323, epsilon);
+  EXPECT_NEAR(function.Solve(0.4), 0.36761, epsilon);
+  EXPECT_NEAR(function.Solve(0.45), 0.43345, epsilon);
+  EXPECT_NEAR(function.Solve(0.5), 0.5, epsilon);
+  EXPECT_NEAR(function.Solve(0.6), 0.63238, epsilon);
+  EXPECT_NEAR(function.Solve(0.65), 0.69676, epsilon);
+  EXPECT_NEAR(function.Solve(0.7), 0.75884, epsilon);
+  EXPECT_NEAR(function.Solve(0.75), 0.81764, epsilon);
+  EXPECT_NEAR(function.Solve(0.8), 0.87196, epsilon);
+  EXPECT_NEAR(function.Solve(0.85), 0.92021, epsilon);
+  EXPECT_NEAR(function.Solve(0.9), 0.96021, epsilon);
+  EXPECT_NEAR(function.Solve(0.95), 0.98863, epsilon);
+  EXPECT_NEAR(function.Solve(1), 1, epsilon);
+
+  CubicBezier basic_use(0.5, 1.0, 0.5, 1.0);
+  EXPECT_EQ(0.875, basic_use.Solve(0.5));
+
+  CubicBezier overshoot(0.5, 2.0, 0.5, 2.0);
+  EXPECT_EQ(1.625, overshoot.Solve(0.5));
+
+  CubicBezier undershoot(0.5, -1.0, 0.5, -1.0);
+  EXPECT_EQ(-0.625, undershoot.Solve(0.5));
+}
+
+// Tests that solving the bezier works with knots with y not in (0, 1).
+TEST(CubicBezierTest, UnclampedYValues) {
+  CubicBezier function(0.5, -1.0, 0.5, 2.0);
+
+  double epsilon = 0.00015;
+
+  EXPECT_NEAR(function.Solve(0.0), 0.0, epsilon);
+  EXPECT_NEAR(function.Solve(0.05), -0.08954, epsilon);
+  EXPECT_NEAR(function.Solve(0.1), -0.15613, epsilon);
+  EXPECT_NEAR(function.Solve(0.15), -0.19641, epsilon);
+  EXPECT_NEAR(function.Solve(0.2), -0.20651, epsilon);
+  EXPECT_NEAR(function.Solve(0.25), -0.18232, epsilon);
+  EXPECT_NEAR(function.Solve(0.3), -0.11992, epsilon);
+  EXPECT_NEAR(function.Solve(0.35), -0.01672, epsilon);
+  EXPECT_NEAR(function.Solve(0.4), 0.12660, epsilon);
+  EXPECT_NEAR(function.Solve(0.45), 0.30349, epsilon);
+  EXPECT_NEAR(function.Solve(0.5), 0.50000, epsilon);
+  EXPECT_NEAR(function.Solve(0.55), 0.69651, epsilon);
+  EXPECT_NEAR(function.Solve(0.6), 0.87340, epsilon);
+  EXPECT_NEAR(function.Solve(0.65), 1.01672, epsilon);
+  EXPECT_NEAR(function.Solve(0.7), 1.11992, epsilon);
+  EXPECT_NEAR(function.Solve(0.75), 1.18232, epsilon);
+  EXPECT_NEAR(function.Solve(0.8), 1.20651, epsilon);
+  EXPECT_NEAR(function.Solve(0.85), 1.19641, epsilon);
+  EXPECT_NEAR(function.Solve(0.9), 1.15613, epsilon);
+  EXPECT_NEAR(function.Solve(0.95), 1.08954, epsilon);
+  EXPECT_NEAR(function.Solve(1.0), 1.0, epsilon);
+}
+
+TEST(CubicBezierTest, Range) {
+  double epsilon = 0.00015;
+
+  // Derivative is a constant.
+  std::unique_ptr<CubicBezier> function(
+      new CubicBezier(0.25, (1.0 / 3.0), 0.75, (2.0 / 3.0)));
+  EXPECT_EQ(0, function->range_min());
+  EXPECT_EQ(1, function->range_max());
+
+  // Derivative is linear.
+  function.reset(new CubicBezier(0.25, -0.5, 0.75, (-1.0 / 6.0)));
+  EXPECT_NEAR(function->range_min(), -0.225, epsilon);
+  EXPECT_EQ(1, function->range_max());
+
+  // Derivative has no real roots.
+  function.reset(new CubicBezier(0.25, 0.25, 0.75, 0.5));
+  EXPECT_EQ(0, function->range_min());
+  EXPECT_EQ(1, function->range_max());
+
+  // Derivative has exactly one real root.
+  function.reset(new CubicBezier(0.0, 1.0, 1.0, 0.0));
+  EXPECT_EQ(0, function->range_min());
+  EXPECT_EQ(1, function->range_max());
+
+  // Derivative has one root < 0 and one root > 1.
+  function.reset(new CubicBezier(0.25, 0.1, 0.75, 0.9));
+  EXPECT_EQ(0, function->range_min());
+  EXPECT_EQ(1, function->range_max());
+
+  // Derivative has two roots in [0,1].
+  function.reset(new CubicBezier(0.25, 2.5, 0.75, 0.5));
+  EXPECT_EQ(0, function->range_min());
+  EXPECT_NEAR(function->range_max(), 1.28818, epsilon);
+  function.reset(new CubicBezier(0.25, 0.5, 0.75, -1.5));
+  EXPECT_NEAR(function->range_min(), -0.28818, epsilon);
+  EXPECT_EQ(1, function->range_max());
+
+  // Derivative has one root < 0 and one root in [0,1].
+  function.reset(new CubicBezier(0.25, 0.1, 0.75, 1.5));
+  EXPECT_EQ(0, function->range_min());
+  EXPECT_NEAR(function->range_max(), 1.10755, epsilon);
+
+  // Derivative has one root in [0,1] and one root > 1.
+  function.reset(new CubicBezier(0.25, -0.5, 0.75, 0.9));
+  EXPECT_NEAR(function->range_min(), -0.10755, epsilon);
+  EXPECT_EQ(1, function->range_max());
+
+  // Derivative has two roots < 0.
+  function.reset(new CubicBezier(0.25, 0.3, 0.75, 0.633));
+  EXPECT_EQ(0, function->range_min());
+  EXPECT_EQ(1, function->range_max());
+
+  // Derivative has two roots > 1.
+  function.reset(new CubicBezier(0.25, 0.367, 0.75, 0.7));
+  EXPECT_EQ(0.f, function->range_min());
+  EXPECT_EQ(1.f, function->range_max());
+}
+
+TEST(CubicBezierTest, Slope) {
+  CubicBezier function(0.25, 0.0, 0.75, 1.0);
+
+  double epsilon = 0.00015;
+
+  EXPECT_NEAR(function.Slope(-0.1), 0, epsilon);
+  EXPECT_NEAR(function.Slope(0), 0, epsilon);
+  EXPECT_NEAR(function.Slope(0.05), 0.42170, epsilon);
+  EXPECT_NEAR(function.Slope(0.1), 0.69778, epsilon);
+  EXPECT_NEAR(function.Slope(0.15), 0.89121, epsilon);
+  EXPECT_NEAR(function.Slope(0.2), 1.03184, epsilon);
+  EXPECT_NEAR(function.Slope(0.25), 1.13576, epsilon);
+  EXPECT_NEAR(function.Slope(0.3), 1.21239, epsilon);
+  EXPECT_NEAR(function.Slope(0.35), 1.26751, epsilon);
+  EXPECT_NEAR(function.Slope(0.4), 1.30474, epsilon);
+  EXPECT_NEAR(function.Slope(0.45), 1.32628, epsilon);
+  EXPECT_NEAR(function.Slope(0.5), 1.33333, epsilon);
+  EXPECT_NEAR(function.Slope(0.55), 1.32628, epsilon);
+  EXPECT_NEAR(function.Slope(0.6), 1.30474, epsilon);
+  EXPECT_NEAR(function.Slope(0.65), 1.26751, epsilon);
+  EXPECT_NEAR(function.Slope(0.7), 1.21239, epsilon);
+  EXPECT_NEAR(function.Slope(0.75), 1.13576, epsilon);
+  EXPECT_NEAR(function.Slope(0.8), 1.03184, epsilon);
+  EXPECT_NEAR(function.Slope(0.85), 0.89121, epsilon);
+  EXPECT_NEAR(function.Slope(0.9), 0.69778, epsilon);
+  EXPECT_NEAR(function.Slope(0.95), 0.42170, epsilon);
+  EXPECT_NEAR(function.Slope(1), 0, epsilon);
+  EXPECT_NEAR(function.Slope(1.1), 0, epsilon);
+}
+
+TEST(CubicBezierTest, InputOutOfRange) {
+  CubicBezier simple(0.5, 1.0, 0.5, 1.0);
+  EXPECT_EQ(-2.0, simple.Solve(-1.0));
+  EXPECT_EQ(1.0, simple.Solve(2.0));
+
+  CubicBezier at_edge_of_range(0.5, 1.0, 0.5, 1.0);
+  EXPECT_EQ(0.0, at_edge_of_range.Solve(0.0));
+  EXPECT_EQ(1.0, at_edge_of_range.Solve(1.0));
+
+  CubicBezier large_epsilon(0.5, 1.0, 0.5, 1.0);
+  EXPECT_EQ(-2.0, large_epsilon.SolveWithEpsilon(-1.0, 1.0));
+  EXPECT_EQ(1.0, large_epsilon.SolveWithEpsilon(2.0, 1.0));
+
+  CubicBezier coincident_endpoints(0.0, 0.0, 1.0, 1.0);
+  EXPECT_EQ(-1.0, coincident_endpoints.Solve(-1.0));
+  EXPECT_EQ(2.0, coincident_endpoints.Solve(2.0));
+
+  CubicBezier vertical_gradient(0.0, 1.0, 1.0, 0.0);
+  EXPECT_EQ(0.0, vertical_gradient.Solve(-1.0));
+  EXPECT_EQ(1.0, vertical_gradient.Solve(2.0));
+
+  CubicBezier distinct_endpoints(0.1, 0.2, 0.8, 0.8);
+  EXPECT_EQ(-2.0, distinct_endpoints.Solve(-1.0));
+  EXPECT_EQ(2.0, distinct_endpoints.Solve(2.0));
+
+  CubicBezier coincident_endpoint(0.0, 0.0, 0.8, 0.8);
+  EXPECT_EQ(-1.0, coincident_endpoint.Solve(-1.0));
+  EXPECT_EQ(2.0, coincident_endpoint.Solve(2.0));
+
+  CubicBezier three_coincident_points(0.0, 0.0, 0.0, 0.0);
+  EXPECT_EQ(0, three_coincident_points.Solve(-1.0));
+  EXPECT_EQ(2.0, three_coincident_points.Solve(2.0));
+}
+
+TEST(CubicBezierTest, GetPoints) {
+  double epsilon = 0.00015;
+
+  CubicBezier cubic1(0.1, 0.2, 0.8, 0.9);
+  EXPECT_NEAR(0.1, cubic1.GetX1(), epsilon);
+  EXPECT_NEAR(0.2, cubic1.GetY1(), epsilon);
+  EXPECT_NEAR(0.8, cubic1.GetX2(), epsilon);
+  EXPECT_NEAR(0.9, cubic1.GetY2(), epsilon);
+
+  CubicBezier cubic_zero(0, 0, 0, 0);
+  EXPECT_NEAR(0, cubic_zero.GetX1(), epsilon);
+  EXPECT_NEAR(0, cubic_zero.GetY1(), epsilon);
+  EXPECT_NEAR(0, cubic_zero.GetX2(), epsilon);
+  EXPECT_NEAR(0, cubic_zero.GetY2(), epsilon);
+
+  CubicBezier cubic_one(1, 1, 1, 1);
+  EXPECT_NEAR(1, cubic_one.GetX1(), epsilon);
+  EXPECT_NEAR(1, cubic_one.GetY1(), epsilon);
+  EXPECT_NEAR(1, cubic_one.GetX2(), epsilon);
+  EXPECT_NEAR(1, cubic_one.GetY2(), epsilon);
+
+  CubicBezier cubic_oor(-0.5, -1.5, 1.5, -1.6);
+  EXPECT_NEAR(-0.5, cubic_oor.GetX1(), epsilon);
+  EXPECT_NEAR(-1.5, cubic_oor.GetY1(), epsilon);
+  EXPECT_NEAR(1.5, cubic_oor.GetX2(), epsilon);
+  EXPECT_NEAR(-1.6, cubic_oor.GetY2(), epsilon);
+}
+
+}  // namespace
+}  // namespace gfx
diff --git a/ui/gfx/geometry/dip_util.cc b/ui/gfx/geometry/dip_util.cc
new file mode 100644
index 0000000..db8e1be
--- /dev/null
+++ b/ui/gfx/geometry/dip_util.cc
@@ -0,0 +1,88 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/gfx/geometry/dip_util.h"
+
+#include "ui/gfx/geometry/insets.h"
+#include "ui/gfx/geometry/point.h"
+#include "ui/gfx/geometry/point_conversions.h"
+#include "ui/gfx/geometry/point_f.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/rect_conversions.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/gfx/geometry/size_conversions.h"
+
+namespace gfx {
+
+Insets ConvertInsetsToDIP(float scale_factor,
+                          const gfx::Insets& insets_in_pixel) {
+  if (scale_factor == 1.f)
+    return insets_in_pixel;
+  return insets_in_pixel.Scale(1.f / scale_factor);
+}
+
+Point ConvertPointToDIP(float scale_factor, const Point& point_in_pixel) {
+  if (scale_factor == 1.f)
+    return point_in_pixel;
+  return ScaleToFlooredPoint(point_in_pixel, 1.f / scale_factor);
+}
+
+PointF ConvertPointToDIP(float scale_factor, const PointF& point_in_pixel) {
+  if (scale_factor == 1.f)
+    return point_in_pixel;
+  return ScalePoint(point_in_pixel, 1.f / scale_factor);
+}
+
+Size ConvertSizeToDIP(float scale_factor, const Size& size_in_pixel) {
+  if (scale_factor == 1.f)
+    return size_in_pixel;
+  return ScaleToFlooredSize(size_in_pixel, 1.f / scale_factor);
+}
+
+Rect ConvertRectToDIP(float scale_factor, const Rect& rect_in_pixel) {
+  if (scale_factor == 1.f)
+    return rect_in_pixel;
+  return ToFlooredRectDeprecated(
+      ScaleRect(RectF(rect_in_pixel), 1.f / scale_factor));
+}
+
+Insets ConvertInsetsToPixel(float scale_factor,
+                            const gfx::Insets& insets_in_dip) {
+  if (scale_factor == 1.f)
+    return insets_in_dip;
+  return insets_in_dip.Scale(scale_factor);
+}
+
+Point ConvertPointToPixel(float scale_factor, const Point& point_in_dip) {
+  if (scale_factor == 1.f)
+    return point_in_dip;
+  return ScaleToFlooredPoint(point_in_dip, scale_factor);
+}
+
+PointF ConvertPointToPixel(float scale_factor, const PointF& point_in_dip) {
+  if (scale_factor == 1.f)
+    return point_in_dip;
+  return ScalePoint(point_in_dip, scale_factor);
+}
+
+Size ConvertSizeToPixel(float scale_factor, const Size& size_in_dip) {
+  if (scale_factor == 1.f)
+    return size_in_dip;
+  return ScaleToFlooredSize(size_in_dip, scale_factor);
+}
+
+Rect ConvertRectToPixel(float scale_factor, const Rect& rect_in_dip) {
+  // Use ToEnclosingRect() to ensure we paint all the possible pixels
+  // touched. ToEnclosingRect() floors the origin, and ceils the max
+  // coordinate. To do otherwise (such as flooring the size) potentially
+  // results in rounding down and not drawing all the pixels that are
+  // touched.
+  if (scale_factor == 1.f)
+    return rect_in_dip;
+  return ToEnclosingRect(
+      RectF(ScalePoint(gfx::PointF(rect_in_dip.origin()), scale_factor),
+            ScaleSize(gfx::SizeF(rect_in_dip.size()), scale_factor)));
+}
+
+}  // namespace gfx
diff --git a/ui/gfx/geometry/dip_util.h b/ui/gfx/geometry/dip_util.h
new file mode 100644
index 0000000..e88d49b
--- /dev/null
+++ b/ui/gfx/geometry/dip_util.h
@@ -0,0 +1,41 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_GFX_GEOMETRY_DIP_UTIL_H_
+#define UI_GFX_GEOMETRY_DIP_UTIL_H_
+
+#include "ui/gfx/gfx_export.h"
+
+namespace gfx {
+
+class Insets;
+class Point;
+class PointF;
+class Rect;
+class Size;
+
+GFX_EXPORT gfx::Insets ConvertInsetsToDIP(float scale_factor,
+                                          const gfx::Insets& insets_in_pixel);
+GFX_EXPORT gfx::Point ConvertPointToDIP(float scale_factor,
+                                        const gfx::Point& point_in_pixel);
+GFX_EXPORT gfx::PointF ConvertPointToDIP(float scale_factor,
+                                         const gfx::PointF& point_in_pixel);
+GFX_EXPORT gfx::Size ConvertSizeToDIP(float scale_factor,
+                                      const gfx::Size& size_in_pixel);
+GFX_EXPORT gfx::Rect ConvertRectToDIP(float scale_factor,
+                                      const gfx::Rect& rect_in_pixel);
+
+GFX_EXPORT gfx::Insets ConvertInsetsToPixel(float scale_factor,
+                                            const gfx::Insets& insets_in_dip);
+GFX_EXPORT gfx::Point ConvertPointToPixel(float scale_factor,
+                                          const gfx::Point& point_in_dip);
+GFX_EXPORT gfx::PointF ConvertPointToPixel(float scale_factor,
+                                           const gfx::PointF& point_in_dip);
+GFX_EXPORT gfx::Size ConvertSizeToPixel(float scale_factor,
+                                        const gfx::Size& size_in_dip);
+GFX_EXPORT gfx::Rect ConvertRectToPixel(float scale_factor,
+                                        const gfx::Rect& rect_in_dip);
+}  // gfx
+
+#endif  // UI_GFX_GEOMETRY_DIP_UTIL_H_
diff --git a/ui/gfx/geometry/insets_unittest.cc b/ui/gfx/geometry/insets_unittest.cc
new file mode 100644
index 0000000..6f607d9
--- /dev/null
+++ b/ui/gfx/geometry/insets_unittest.cc
@@ -0,0 +1,139 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/gfx/geometry/insets.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/geometry/insets_f.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/vector2d.h"
+
+TEST(InsetsTest, InsetsDefault) {
+  gfx::Insets insets;
+  EXPECT_EQ(0, insets.top());
+  EXPECT_EQ(0, insets.left());
+  EXPECT_EQ(0, insets.bottom());
+  EXPECT_EQ(0, insets.right());
+  EXPECT_EQ(0, insets.width());
+  EXPECT_EQ(0, insets.height());
+  EXPECT_TRUE(insets.IsEmpty());
+}
+
+TEST(InsetsTest, Insets) {
+  gfx::Insets insets(1, 2, 3, 4);
+  EXPECT_EQ(1, insets.top());
+  EXPECT_EQ(2, insets.left());
+  EXPECT_EQ(3, insets.bottom());
+  EXPECT_EQ(4, insets.right());
+  EXPECT_EQ(6, insets.width());  // Left + right.
+  EXPECT_EQ(4, insets.height());  // Top + bottom.
+  EXPECT_FALSE(insets.IsEmpty());
+}
+
+TEST(InsetsTest, Set) {
+  gfx::Insets insets;
+  insets.Set(1, 2, 3, 4);
+  EXPECT_EQ(1, insets.top());
+  EXPECT_EQ(2, insets.left());
+  EXPECT_EQ(3, insets.bottom());
+  EXPECT_EQ(4, insets.right());
+}
+
+TEST(InsetsTest, Operators) {
+  gfx::Insets insets;
+  insets.Set(1, 2, 3, 4);
+  insets += gfx::Insets(5, 6, 7, 8);
+  EXPECT_EQ(6, insets.top());
+  EXPECT_EQ(8, insets.left());
+  EXPECT_EQ(10, insets.bottom());
+  EXPECT_EQ(12, insets.right());
+
+  insets -= gfx::Insets(-1, 0, 1, 2);
+  EXPECT_EQ(7, insets.top());
+  EXPECT_EQ(8, insets.left());
+  EXPECT_EQ(9, insets.bottom());
+  EXPECT_EQ(10, insets.right());
+
+  insets = gfx::Insets(10, 10, 10, 10) + gfx::Insets(5, 5, 0, -20);
+  EXPECT_EQ(15, insets.top());
+  EXPECT_EQ(15, insets.left());
+  EXPECT_EQ(10, insets.bottom());
+  EXPECT_EQ(-10, insets.right());
+
+  insets = gfx::Insets(10, 10, 10, 10) - gfx::Insets(5, 5, 0, -20);
+  EXPECT_EQ(5, insets.top());
+  EXPECT_EQ(5, insets.left());
+  EXPECT_EQ(10, insets.bottom());
+  EXPECT_EQ(30, insets.right());
+}
+
+TEST(InsetsFTest, Operators) {
+  gfx::InsetsF insets;
+  insets.Set(1.f, 2.5f, 3.3f, 4.1f);
+  insets += gfx::InsetsF(5.8f, 6.7f, 7.6f, 8.5f);
+  EXPECT_FLOAT_EQ(6.8f, insets.top());
+  EXPECT_FLOAT_EQ(9.2f, insets.left());
+  EXPECT_FLOAT_EQ(10.9f, insets.bottom());
+  EXPECT_FLOAT_EQ(12.6f, insets.right());
+
+  insets -= gfx::InsetsF(-1.f, 0, 1.1f, 2.2f);
+  EXPECT_FLOAT_EQ(7.8f, insets.top());
+  EXPECT_FLOAT_EQ(9.2f, insets.left());
+  EXPECT_FLOAT_EQ(9.8f, insets.bottom());
+  EXPECT_FLOAT_EQ(10.4f, insets.right());
+
+  insets = gfx::InsetsF(10, 10.1f, 10.01f, 10.001f) +
+           gfx::InsetsF(5.5f, 5.f, 0, -20.2f);
+  EXPECT_FLOAT_EQ(15.5f, insets.top());
+  EXPECT_FLOAT_EQ(15.1f, insets.left());
+  EXPECT_FLOAT_EQ(10.01f, insets.bottom());
+  EXPECT_FLOAT_EQ(-10.199f, insets.right());
+
+  insets = gfx::InsetsF(10, 10.1f, 10.01f, 10.001f) -
+           gfx::InsetsF(5.5f, 5.f, 0, -20.2f);
+  EXPECT_FLOAT_EQ(4.5f, insets.top());
+  EXPECT_FLOAT_EQ(5.1f, insets.left());
+  EXPECT_FLOAT_EQ(10.01f, insets.bottom());
+  EXPECT_FLOAT_EQ(30.201f, insets.right());
+}
+
+TEST(InsetsTest, Equality) {
+  gfx::Insets insets1;
+  insets1.Set(1, 2, 3, 4);
+  gfx::Insets insets2;
+  // Test operator== and operator!=.
+  EXPECT_FALSE(insets1 == insets2);
+  EXPECT_TRUE(insets1 != insets2);
+
+  insets2.Set(1, 2, 3, 4);
+  EXPECT_TRUE(insets1 == insets2);
+  EXPECT_FALSE(insets1 != insets2);
+}
+
+TEST(InsetsTest, ToString) {
+  gfx::Insets insets(1, 2, 3, 4);
+  EXPECT_EQ("1,2,3,4", insets.ToString());
+}
+
+TEST(InsetsTest, Offset) {
+  const gfx::Insets insets(1, 2, 3, 4);
+  const gfx::Rect rect(5, 6, 7, 8);
+  const gfx::Vector2d vector(9, 10);
+
+  // Whether you inset then offset the rect, offset then inset the rect, or
+  // offset the insets then apply to the rect, the outcome should be the same.
+  gfx::Rect inset_first = rect;
+  inset_first.Inset(insets);
+  inset_first.Offset(vector);
+
+  gfx::Rect offset_first = rect;
+  offset_first.Offset(vector);
+  offset_first.Inset(insets);
+
+  gfx::Rect inset_by_offset = rect;
+  inset_by_offset.Inset(insets.Offset(vector));
+
+  EXPECT_EQ(inset_first, offset_first);
+  EXPECT_EQ(inset_by_offset, inset_first);
+}
diff --git a/ui/gfx/geometry/matrix3_f.cc b/ui/gfx/geometry/matrix3_f.cc
new file mode 100644
index 0000000..1420ee5
--- /dev/null
+++ b/ui/gfx/geometry/matrix3_f.cc
@@ -0,0 +1,291 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/gfx/geometry/matrix3_f.h"
+
+#include <algorithm>
+#include <cmath>
+#include <limits>
+
+#include "base/numerics/math_constants.h"
+#include "base/strings/stringprintf.h"
+
+namespace {
+
+// This is only to make accessing indices self-explanatory.
+enum MatrixCoordinates {
+  M00,
+  M01,
+  M02,
+  M10,
+  M11,
+  M12,
+  M20,
+  M21,
+  M22,
+  M_END
+};
+
+template<typename T>
+double Determinant3x3(T data[M_END]) {
+  // This routine is separated from the Matrix3F::Determinant because in
+  // computing inverse we do want higher precision afforded by the explicit
+  // use of 'double'.
+  return
+      static_cast<double>(data[M00]) * (
+          static_cast<double>(data[M11]) * data[M22] -
+          static_cast<double>(data[M12]) * data[M21]) +
+      static_cast<double>(data[M01]) * (
+          static_cast<double>(data[M12]) * data[M20] -
+          static_cast<double>(data[M10]) * data[M22]) +
+      static_cast<double>(data[M02]) * (
+          static_cast<double>(data[M10]) * data[M21] -
+          static_cast<double>(data[M11]) * data[M20]);
+}
+
+}  // namespace
+
+namespace gfx {
+
+Matrix3F::Matrix3F() {
+}
+
+Matrix3F::~Matrix3F() {
+}
+
+// static
+Matrix3F Matrix3F::Zeros() {
+  Matrix3F matrix;
+  matrix.set(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f);
+  return matrix;
+}
+
+// static
+Matrix3F Matrix3F::Ones() {
+  Matrix3F matrix;
+  matrix.set(1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f);
+  return matrix;
+}
+
+// static
+Matrix3F Matrix3F::Identity() {
+  Matrix3F matrix;
+  matrix.set(1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f);
+  return matrix;
+}
+
+// static
+Matrix3F Matrix3F::FromOuterProduct(const Vector3dF& a, const Vector3dF& bt) {
+  Matrix3F matrix;
+  matrix.set(a.x() * bt.x(), a.x() * bt.y(), a.x() * bt.z(),
+             a.y() * bt.x(), a.y() * bt.y(), a.y() * bt.z(),
+             a.z() * bt.x(), a.z() * bt.y(), a.z() * bt.z());
+  return matrix;
+}
+
+bool Matrix3F::IsEqual(const Matrix3F& rhs) const {
+  return 0 == memcmp(data_, rhs.data_, sizeof(data_));
+}
+
+bool Matrix3F::IsNear(const Matrix3F& rhs, float precision) const {
+  DCHECK(precision >= 0);
+  for (int i = 0; i < M_END; ++i) {
+    if (std::abs(data_[i] - rhs.data_[i]) > precision)
+      return false;
+  }
+  return true;
+}
+
+Matrix3F Matrix3F::Add(const Matrix3F& rhs) const {
+  Matrix3F result;
+  for (int i = 0; i < M_END; ++i)
+    result.data_[i] = data_[i] + rhs.data_[i];
+  return result;
+}
+
+Matrix3F Matrix3F::Subtract(const Matrix3F& rhs) const {
+  Matrix3F result;
+  for (int i = 0; i < M_END; ++i)
+    result.data_[i] = data_[i] - rhs.data_[i];
+  return result;
+}
+
+Matrix3F Matrix3F::Inverse() const {
+  Matrix3F inverse = Matrix3F::Zeros();
+  double determinant = Determinant3x3(data_);
+  if (std::numeric_limits<float>::epsilon() > std::abs(determinant))
+    return inverse;  // Singular matrix. Return Zeros().
+
+  inverse.set(
+      static_cast<float>((data_[M11] * data_[M22] - data_[M12] * data_[M21]) /
+          determinant),
+      static_cast<float>((data_[M02] * data_[M21] - data_[M01] * data_[M22]) /
+          determinant),
+      static_cast<float>((data_[M01] * data_[M12] - data_[M02] * data_[M11]) /
+          determinant),
+      static_cast<float>((data_[M12] * data_[M20] - data_[M10] * data_[M22]) /
+          determinant),
+      static_cast<float>((data_[M00] * data_[M22] - data_[M02] * data_[M20]) /
+          determinant),
+      static_cast<float>((data_[M02] * data_[M10] - data_[M00] * data_[M12]) /
+          determinant),
+      static_cast<float>((data_[M10] * data_[M21] - data_[M11] * data_[M20]) /
+          determinant),
+      static_cast<float>((data_[M01] * data_[M20] - data_[M00] * data_[M21]) /
+          determinant),
+      static_cast<float>((data_[M00] * data_[M11] - data_[M01] * data_[M10]) /
+          determinant));
+  return inverse;
+}
+
+Matrix3F Matrix3F::Transpose() const {
+  Matrix3F transpose;
+  transpose.set(data_[M00], data_[M10], data_[M20], data_[M01], data_[M11],
+                data_[M21], data_[M02], data_[M12], data_[M22]);
+  return transpose;
+}
+
+float Matrix3F::Determinant() const {
+  return static_cast<float>(Determinant3x3(data_));
+}
+
+Vector3dF Matrix3F::SolveEigenproblem(Matrix3F* eigenvectors) const {
+  // The matrix must be symmetric.
+  const float epsilon = std::numeric_limits<float>::epsilon();
+  if (std::abs(data_[M01] - data_[M10]) > epsilon ||
+      std::abs(data_[M02] - data_[M20]) > epsilon ||
+      std::abs(data_[M12] - data_[M21]) > epsilon) {
+    NOTREACHED();
+    return Vector3dF();
+  }
+
+  float eigenvalues[3];
+  float p =
+      data_[M01] * data_[M01] +
+      data_[M02] * data_[M02] +
+      data_[M12] * data_[M12];
+
+  bool diagonal = std::abs(p) < epsilon;
+  if (diagonal) {
+    eigenvalues[0] = data_[M00];
+    eigenvalues[1] = data_[M11];
+    eigenvalues[2] = data_[M22];
+  } else {
+    float q = Trace() / 3.0f;
+    p = (data_[M00] - q) * (data_[M00] - q) +
+        (data_[M11] - q) * (data_[M11] - q) +
+        (data_[M22] - q) * (data_[M22] - q) +
+        2 * p;
+    p = std::sqrt(p / 6);
+
+    // The computation below puts B as (A - qI) / p, where A is *this.
+    Matrix3F matrix_b(*this);
+    matrix_b.data_[M00] -= q;
+    matrix_b.data_[M11] -= q;
+    matrix_b.data_[M22] -= q;
+    for (int i = 0; i < M_END; ++i)
+      matrix_b.data_[i] /= p;
+
+    double half_det_b = Determinant3x3(matrix_b.data_) / 2.0;
+    // half_det_b should be in <-1, 1>, but beware of rounding error.
+    double phi = 0.0f;
+    if (half_det_b <= -1.0)
+      phi = base::kPiDouble / 3;
+    else if (half_det_b < 1.0)
+      phi = acos(half_det_b) / 3;
+
+    eigenvalues[0] = q + 2 * p * static_cast<float>(cos(phi));
+    eigenvalues[2] =
+        q + 2 * p * static_cast<float>(cos(phi + 2.0 * base::kPiDouble / 3.0));
+    eigenvalues[1] = 3 * q - eigenvalues[0] - eigenvalues[2];
+  }
+
+  // Put eigenvalues in the descending order.
+  int indices[3] = {0, 1, 2};
+  if (eigenvalues[2] > eigenvalues[1]) {
+    std::swap(eigenvalues[2], eigenvalues[1]);
+    std::swap(indices[2], indices[1]);
+  }
+
+  if (eigenvalues[1] > eigenvalues[0]) {
+    std::swap(eigenvalues[1], eigenvalues[0]);
+    std::swap(indices[1], indices[0]);
+  }
+
+  if (eigenvalues[2] > eigenvalues[1]) {
+    std::swap(eigenvalues[2], eigenvalues[1]);
+    std::swap(indices[2], indices[1]);
+  }
+
+  if (eigenvectors != NULL && diagonal) {
+    // Eigenvectors are e-vectors, just need to be sorted accordingly.
+    *eigenvectors = Zeros();
+    for (int i = 0; i < 3; ++i)
+      eigenvectors->set(indices[i], i, 1.0f);
+  } else if (eigenvectors != NULL) {
+    // Consult the following for a detailed discussion:
+    // Joachim Kopp
+    // Numerical diagonalization of hermitian 3x3 matrices
+    // arXiv.org preprint: physics/0610206
+    // Int. J. Mod. Phys. C19 (2008) 523-548
+
+    // TODO(motek): expand to handle correctly negative and multiple
+    // eigenvalues.
+    for (int i = 0; i < 3; ++i) {
+      float l = eigenvalues[i];
+      // B = A - l * I
+      Matrix3F matrix_b(*this);
+      matrix_b.data_[M00] -= l;
+      matrix_b.data_[M11] -= l;
+      matrix_b.data_[M22] -= l;
+      Vector3dF e1 = CrossProduct(matrix_b.get_column(0),
+                                  matrix_b.get_column(1));
+      Vector3dF e2 = CrossProduct(matrix_b.get_column(1),
+                                  matrix_b.get_column(2));
+      Vector3dF e3 = CrossProduct(matrix_b.get_column(2),
+                                  matrix_b.get_column(0));
+
+      // e1, e2 and e3 should point in the same direction.
+      if (DotProduct(e1, e2) < 0)
+        e2 = -e2;
+
+      if (DotProduct(e1, e3) < 0)
+        e3 = -e3;
+
+      Vector3dF eigvec = e1 + e2 + e3;
+      // Normalize.
+      eigvec.Scale(1.0f / eigvec.Length());
+      eigenvectors->set_column(i, eigvec);
+    }
+  }
+
+  return Vector3dF(eigenvalues[0], eigenvalues[1], eigenvalues[2]);
+}
+
+Matrix3F MatrixProduct(const Matrix3F& lhs, const Matrix3F& rhs) {
+  Matrix3F result = Matrix3F::Zeros();
+  for (int i = 0; i < 3; i++) {
+    for (int j = 0; j < 3; j++) {
+      result.set(i, j, DotProduct(lhs.get_row(i), rhs.get_column(j)));
+    }
+  }
+  return result;
+}
+
+Vector3dF MatrixProduct(const Matrix3F& lhs, const Vector3dF& rhs) {
+  return Vector3dF(DotProduct(lhs.get_row(0), rhs),
+                   DotProduct(lhs.get_row(1), rhs),
+                   DotProduct(lhs.get_row(2), rhs));
+}
+
+std::string Matrix3F::ToString() const {
+  return base::StringPrintf(
+      "[[%+0.4f, %+0.4f, %+0.4f],"
+      " [%+0.4f, %+0.4f, %+0.4f],"
+      " [%+0.4f, %+0.4f, %+0.4f]]",
+      data_[M00], data_[M01], data_[M02], data_[M10], data_[M11], data_[M12],
+      data_[M20], data_[M21], data_[M22]);
+}
+
+}  // namespace gfx
diff --git a/ui/gfx/geometry/matrix3_f.h b/ui/gfx/geometry/matrix3_f.h
new file mode 100644
index 0000000..7fab2e3
--- /dev/null
+++ b/ui/gfx/geometry/matrix3_f.h
@@ -0,0 +1,139 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_GFX_GEOMETRY_MATRIX3_F_H_
+#define UI_GFX_GEOMETRY_MATRIX3_F_H_
+
+#include "base/logging.h"
+#include "ui/gfx/geometry/vector3d_f.h"
+
+namespace gfx {
+
+class GFX_EXPORT Matrix3F {
+ public:
+  ~Matrix3F();
+
+  static Matrix3F Zeros();
+  static Matrix3F Ones();
+  static Matrix3F Identity();
+  static Matrix3F FromOuterProduct(const Vector3dF& a, const Vector3dF& bt);
+
+  bool IsEqual(const Matrix3F& rhs) const;
+
+  // Element-wise comparison with given precision.
+  bool IsNear(const Matrix3F& rhs, float precision) const;
+
+  float get(int i, int j) const {
+    return data_[MatrixToArrayCoords(i, j)];
+  }
+
+  void set(int i, int j, float v) {
+    data_[MatrixToArrayCoords(i, j)] = v;
+  }
+
+  void set(float m00, float m01, float m02,
+           float m10, float m11, float m12,
+           float m20, float m21, float m22) {
+    data_[0] = m00;
+    data_[1] = m01;
+    data_[2] = m02;
+    data_[3] = m10;
+    data_[4] = m11;
+    data_[5] = m12;
+    data_[6] = m20;
+    data_[7] = m21;
+    data_[8] = m22;
+  }
+
+  Vector3dF get_row(int i) const {
+    return Vector3dF(data_[MatrixToArrayCoords(i, 0)],
+                     data_[MatrixToArrayCoords(i, 1)],
+                     data_[MatrixToArrayCoords(i, 2)]);
+  }
+
+  Vector3dF get_column(int i) const {
+    return Vector3dF(
+      data_[MatrixToArrayCoords(0, i)],
+      data_[MatrixToArrayCoords(1, i)],
+      data_[MatrixToArrayCoords(2, i)]);
+  }
+
+  void set_column(int i, const Vector3dF& c) {
+    data_[MatrixToArrayCoords(0, i)] = c.x();
+    data_[MatrixToArrayCoords(1, i)] = c.y();
+    data_[MatrixToArrayCoords(2, i)] = c.z();
+  }
+
+  // Produces a new matrix by adding the elements of |rhs| to this matrix
+  Matrix3F Add(const Matrix3F& rhs) const;
+  // Produces a new matrix by subtracting elements of |rhs| from this matrix.
+  Matrix3F Subtract(const Matrix3F& rhs) const;
+
+  // Returns an inverse of this if the matrix is non-singular, zero (== Zero())
+  // otherwise.
+  Matrix3F Inverse() const;
+
+  // Returns a transpose of this matrix.
+  Matrix3F Transpose() const;
+
+  // Value of the determinant of the matrix.
+  float Determinant() const;
+
+  // Trace (sum of diagonal elements) of the matrix.
+  float Trace() const {
+    return data_[MatrixToArrayCoords(0, 0)] +
+        data_[MatrixToArrayCoords(1, 1)] +
+        data_[MatrixToArrayCoords(2, 2)];
+  }
+
+  // Compute eigenvalues and (optionally) normalized eigenvectors of
+  // a positive defnite matrix *this. Eigenvectors are computed only if
+  // non-null |eigenvectors| matrix is passed. If it is NULL, the routine
+  // will not attempt to compute eigenvectors but will still return eigenvalues
+  // if they can be computed.
+  // If eigenvalues cannot be computed (the matrix does not meet constraints)
+  // the 0-vector is returned. Note that to retrieve eigenvalues, the matrix
+  // only needs to be symmetric while eigenvectors require it to be
+  // positive-definite. Passing a non-positive definite matrix will result in
+  // NaNs in vectors which cannot be computed.
+  // Eigenvectors are placed as column in |eigenvectors| in order corresponding
+  // to eigenvalues.
+  Vector3dF SolveEigenproblem(Matrix3F* eigenvectors) const;
+
+  std::string ToString() const;
+
+ private:
+  Matrix3F();  // Uninitialized default.
+
+  static int MatrixToArrayCoords(int i, int j) {
+    DCHECK(i >= 0 && i < 3);
+    DCHECK(j >= 0 && j < 3);
+    return i * 3 + j;
+  }
+
+  float data_[9];
+};
+
+inline bool operator==(const Matrix3F& lhs, const Matrix3F& rhs) {
+  return lhs.IsEqual(rhs);
+}
+
+// Matrix addition. Produces a new matrix by adding the corresponding elements
+// together.
+inline Matrix3F operator+(const Matrix3F& lhs, const Matrix3F& rhs) {
+  return lhs.Add(rhs);
+}
+
+// Matrix subtraction. Produces a new matrix by subtracting elements of rhs
+// from corresponding elements of lhs.
+inline Matrix3F operator-(const Matrix3F& lhs, const Matrix3F& rhs) {
+  return lhs.Subtract(rhs);
+}
+
+GFX_EXPORT Matrix3F MatrixProduct(const Matrix3F& lhs, const Matrix3F& rhs);
+GFX_EXPORT Vector3dF MatrixProduct(const Matrix3F& lhs, const Vector3dF& rhs);
+
+}  // namespace gfx
+
+#endif  // UI_GFX_GEOMETRY_MATRIX3_F_H_
diff --git a/ui/gfx/geometry/matrix3_unittest.cc b/ui/gfx/geometry/matrix3_unittest.cc
new file mode 100644
index 0000000..27e9182
--- /dev/null
+++ b/ui/gfx/geometry/matrix3_unittest.cc
@@ -0,0 +1,182 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <cmath>
+#include <limits>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/geometry/matrix3_f.h"
+
+namespace gfx {
+namespace {
+
+TEST(Matrix3fTest, Constructors) {
+  Matrix3F zeros = Matrix3F::Zeros();
+  Matrix3F ones = Matrix3F::Ones();
+  Matrix3F identity = Matrix3F::Identity();
+
+  Matrix3F product_ones = Matrix3F::FromOuterProduct(
+      Vector3dF(1.0f, 1.0f, 1.0f), Vector3dF(1.0f, 1.0f, 1.0f));
+  Matrix3F product_zeros = Matrix3F::FromOuterProduct(
+      Vector3dF(1.0f, 1.0f, 1.0f), Vector3dF(0.0f, 0.0f, 0.0f));
+  EXPECT_EQ(ones, product_ones);
+  EXPECT_EQ(zeros, product_zeros);
+
+  for (int i = 0; i < 3; ++i) {
+    for (int j = 0; j < 3; ++j)
+      EXPECT_EQ(i == j ? 1.0f : 0.0f, identity.get(i, j));
+  }
+}
+
+TEST(Matrix3fTest, DataAccess) {
+  Matrix3F matrix = Matrix3F::Ones();
+  Matrix3F identity = Matrix3F::Identity();
+
+  EXPECT_EQ(Vector3dF(0.0f, 1.0f, 0.0f), identity.get_column(1));
+  EXPECT_EQ(Vector3dF(0.0f, 1.0f, 0.0f), identity.get_row(1));
+  matrix.set(0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f);
+  EXPECT_EQ(Vector3dF(2.0f, 5.0f, 8.0f), matrix.get_column(2));
+  EXPECT_EQ(Vector3dF(6.0f, 7.0f, 8.0f), matrix.get_row(2));
+  matrix.set_column(0, Vector3dF(0.1f, 0.2f, 0.3f));
+  matrix.set_column(0, Vector3dF(0.1f, 0.2f, 0.3f));
+  EXPECT_EQ(Vector3dF(0.1f, 0.2f, 0.3f), matrix.get_column(0));
+  EXPECT_EQ(Vector3dF(0.1f, 1.0f, 2.0f), matrix.get_row(0));
+
+  EXPECT_EQ(0.1f, matrix.get(0, 0));
+  EXPECT_EQ(5.0f, matrix.get(1, 2));
+}
+
+TEST(Matrix3fTest, Determinant) {
+  EXPECT_EQ(1.0f, Matrix3F::Identity().Determinant());
+  EXPECT_EQ(0.0f, Matrix3F::Zeros().Determinant());
+  EXPECT_EQ(0.0f, Matrix3F::Ones().Determinant());
+
+  // Now for something non-trivial...
+  Matrix3F matrix = Matrix3F::Zeros();
+  matrix.set(0, 5, 6, 8, 7, 0, 1, 9, 0);
+  EXPECT_EQ(390.0f, matrix.Determinant());
+  matrix.set(2, 0, 3 * matrix.get(0, 0));
+  matrix.set(2, 1, 3 * matrix.get(0, 1));
+  matrix.set(2, 2, 3 * matrix.get(0, 2));
+  EXPECT_EQ(0, matrix.Determinant());
+
+  matrix.set(0.57f,  0.205f,  0.942f,
+             0.314f,  0.845f,  0.826f,
+             0.131f,  0.025f,  0.962f);
+  EXPECT_NEAR(0.3149f, matrix.Determinant(), 0.0001f);
+}
+
+TEST(Matrix3fTest, Inverse) {
+  Matrix3F identity = Matrix3F::Identity();
+  Matrix3F inv_identity = identity.Inverse();
+  EXPECT_EQ(identity, inv_identity);
+
+  Matrix3F singular = Matrix3F::Zeros();
+  singular.set(1.0f, 3.0f, 4.0f,
+               2.0f, 11.0f, 5.0f,
+               0.5f, 1.5f, 2.0f);
+  EXPECT_EQ(0, singular.Determinant());
+  EXPECT_EQ(Matrix3F::Zeros(), singular.Inverse());
+
+  Matrix3F regular = Matrix3F::Zeros();
+  regular.set(0.57f,  0.205f,  0.942f,
+              0.314f,  0.845f,  0.826f,
+              0.131f,  0.025f,  0.962f);
+  Matrix3F inv_regular = regular.Inverse();
+  regular.set(2.51540616f, -0.55138018f, -1.98968043f,
+              -0.61552266f,  1.34920184f, -0.55573636f,
+              -0.32653861f,  0.04002158f,  1.32488726f);
+  EXPECT_TRUE(regular.IsNear(inv_regular, 0.00001f));
+}
+
+TEST(Matrix3fTest, Transpose) {
+  Matrix3F matrix = Matrix3F::Zeros();
+
+  matrix.set(0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f);
+
+  Matrix3F transpose = matrix.Transpose();
+  EXPECT_EQ(Vector3dF(0.0f, 1.0f, 2.0f), transpose.get_column(0));
+  EXPECT_EQ(Vector3dF(3.0f, 4.0f, 5.0f), transpose.get_column(1));
+  EXPECT_EQ(Vector3dF(6.0f, 7.0f, 8.0f), transpose.get_column(2));
+
+  EXPECT_TRUE(matrix.IsEqual(transpose.Transpose()));
+}
+
+TEST(Matrix3fTest, EigenvectorsIdentity) {
+  // This block tests the trivial case of eigenvalues of the identity matrix.
+  Matrix3F identity = Matrix3F::Identity();
+  Vector3dF eigenvals = identity.SolveEigenproblem(NULL);
+  EXPECT_EQ(Vector3dF(1.0f, 1.0f, 1.0f), eigenvals);
+}
+
+TEST(Matrix3fTest, EigenvectorsDiagonal)  {
+  // This block tests the another trivial case of eigenvalues of a diagonal
+  // matrix. Here we expect values to be sorted.
+  Matrix3F matrix = Matrix3F::Zeros();
+  matrix.set(0, 0, 1.0f);
+  matrix.set(1, 1, -2.5f);
+  matrix.set(2, 2, 3.14f);
+  Matrix3F eigenvectors = Matrix3F::Zeros();
+  Vector3dF eigenvals = matrix.SolveEigenproblem(&eigenvectors);
+  EXPECT_EQ(Vector3dF(3.14f, 1.0f, -2.5f), eigenvals);
+
+  EXPECT_EQ(Vector3dF(0.0f, 0.0f, 1.0f), eigenvectors.get_column(0));
+  EXPECT_EQ(Vector3dF(1.0f, 0.0f, 0.0f), eigenvectors.get_column(1));
+  EXPECT_EQ(Vector3dF(0.0f, 1.0f, 0.0f), eigenvectors.get_column(2));
+}
+
+TEST(Matrix3fTest, EigenvectorsNiceNotPositive)  {
+  // This block tests computation of eigenvectors of a matrix where nice
+  // round values are expected.
+  Matrix3F matrix = Matrix3F::Zeros();
+  // This is not a positive-definite matrix but eigenvalues and the first
+  // eigenvector should nonetheless be computed correctly.
+  matrix.set(3, 2, 4, 2, 0, 2, 4, 2, 3);
+  Matrix3F eigenvectors = Matrix3F::Zeros();
+  Vector3dF eigenvals = matrix.SolveEigenproblem(&eigenvectors);
+  EXPECT_EQ(Vector3dF(8.0f, -1.0f, -1.0f), eigenvals);
+
+  Vector3dF expected_principal(0.66666667f, 0.33333333f, 0.66666667f);
+  EXPECT_NEAR(0.0f,
+              (expected_principal - eigenvectors.get_column(0)).Length(),
+              0.000001f);
+}
+
+TEST(Matrix3fTest, EigenvectorsPositiveDefinite) {
+  // This block tests computation of eigenvectors of a matrix where output
+  // is not as nice as above, but it actually meets the definition.
+  Matrix3F matrix = Matrix3F::Zeros();
+  Matrix3F eigenvectors = Matrix3F::Zeros();
+  Matrix3F expected_eigenvectors = Matrix3F::Zeros();
+  matrix.set(1, -1,  2, -1,  4,  5, 2,  5,  0);
+  Vector3dF eigenvals =  matrix.SolveEigenproblem(&eigenvectors);
+  Vector3dF expected_eigv(7.3996266f, 1.91197255f, -4.31159915f);
+  expected_eigv -= eigenvals;
+  EXPECT_NEAR(0, expected_eigv.LengthSquared(), 0.00001f);
+  expected_eigenvectors.set(0.04926317f, -0.92135662f, -0.38558414f,
+                            0.82134249f, 0.25703273f, -0.50924521f,
+                            0.56830419f, -0.2916096f, 0.76941158f);
+  EXPECT_TRUE(expected_eigenvectors.IsNear(eigenvectors, 0.00001f));
+}
+
+TEST(Matrix3fTest, Operators) {
+  Matrix3F matrix1 = Matrix3F::Zeros();
+  matrix1.set(1, 2, 3, 4, 5, 6, 7, 8, 9);
+  EXPECT_EQ(matrix1 + Matrix3F::Zeros(), matrix1);
+
+  Matrix3F matrix2 = Matrix3F::Zeros();
+  matrix2.set(-1, -2, -3, -4, -5, -6, -7, -8, -9);
+  EXPECT_EQ(matrix1 + matrix2, Matrix3F::Zeros());
+
+  EXPECT_EQ(Matrix3F::Zeros() - matrix1, matrix2);
+
+  Matrix3F result = Matrix3F::Zeros();
+  result.set(2, 4, 6, 8, 10, 12, 14, 16, 18);
+  EXPECT_EQ(matrix1 - matrix2, result);
+  result.set(-2, -4, -6, -8, -10, -12, -14, -16, -18);
+  EXPECT_EQ(matrix2 - matrix1, result);
+}
+
+}  // namespace
+}  // namespace gfx
diff --git a/ui/gfx/geometry/mojo/BUILD.gn b/ui/gfx/geometry/mojo/BUILD.gn
deleted file mode 100644
index 2d0e1ef..0000000
--- a/ui/gfx/geometry/mojo/BUILD.gn
+++ /dev/null
@@ -1,51 +0,0 @@
-# Copyright 2016 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//mojo/public/tools/bindings/mojom.gni")
-
-# This target does NOT depend on skia. One can depend on this target to avoid
-# picking up a dependency on skia.
-mojom("mojo") {
-  sources = [
-    "geometry.mojom",
-  ]
-
-  check_includes_blink = false
-}
-
-mojom("test_interfaces") {
-  sources = [
-    "geometry_traits_test_service.mojom",
-  ]
-
-  public_deps = [
-    ":mojo",
-  ]
-}
-
-source_set("unit_test") {
-  testonly = true
-
-  sources = [
-    "geometry_struct_traits_unittest.cc",
-  ]
-
-  deps = [
-    ":test_interfaces",
-    "//base",
-    "//mojo/public/cpp/bindings",
-    "//testing/gtest",
-    "//ui/gfx/geometry",
-  ]
-}
-
-source_set("struct_traits") {
-  sources = [
-    "geometry_struct_traits.h",
-  ]
-  public_deps = [
-    ":mojo_shared_cpp_sources",
-    "//ui/gfx/geometry",
-  ]
-}
diff --git a/ui/gfx/geometry/point3_f.cc b/ui/gfx/geometry/point3_f.cc
new file mode 100644
index 0000000..465376e
--- /dev/null
+++ b/ui/gfx/geometry/point3_f.cc
@@ -0,0 +1,40 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/gfx/geometry/point3_f.h"
+
+#include "base/strings/stringprintf.h"
+
+namespace gfx {
+
+std::string Point3F::ToString() const {
+  return base::StringPrintf("%f,%f,%f", x_, y_, z_);
+}
+
+Point3F operator+(const Point3F& lhs, const Vector3dF& rhs) {
+  float x = lhs.x() + rhs.x();
+  float y = lhs.y() + rhs.y();
+  float z = lhs.z() + rhs.z();
+  return Point3F(x, y, z);
+}
+
+// Subtract a vector from a point, producing a new point offset by the vector's
+// inverse.
+Point3F operator-(const Point3F& lhs, const Vector3dF& rhs) {
+  float x = lhs.x() - rhs.x();
+  float y = lhs.y() - rhs.y();
+  float z = lhs.z() - rhs.z();
+  return Point3F(x, y, z);
+}
+
+// Subtract one point from another, producing a vector that represents the
+// distances between the two points along each axis.
+Vector3dF operator-(const Point3F& lhs, const Point3F& rhs) {
+  float x = lhs.x() - rhs.x();
+  float y = lhs.y() - rhs.y();
+  float z = lhs.z() - rhs.z();
+  return Vector3dF(x, y, z);
+}
+
+}  // namespace gfx
diff --git a/ui/gfx/geometry/point3_f.h b/ui/gfx/geometry/point3_f.h
new file mode 100644
index 0000000..15a560c
--- /dev/null
+++ b/ui/gfx/geometry/point3_f.h
@@ -0,0 +1,128 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_GFX_GEOMETRY_POINT3_F_H_
+#define UI_GFX_GEOMETRY_POINT3_F_H_
+
+#include <iosfwd>
+#include <string>
+
+#include "ui/gfx/geometry/point_f.h"
+#include "ui/gfx/geometry/vector3d_f.h"
+#include "ui/gfx/gfx_export.h"
+
+namespace gfx {
+
+// A point has an x, y and z coordinate.
+class GFX_EXPORT Point3F {
+ public:
+  constexpr Point3F() : x_(0), y_(0), z_(0) {}
+  constexpr Point3F(float x, float y, float z) : x_(x), y_(y), z_(z) {}
+
+  constexpr explicit Point3F(const PointF& point)
+      : x_(point.x()), y_(point.y()), z_(0) {}
+
+  void Scale(float scale) {
+    Scale(scale, scale, scale);
+  }
+
+  void Scale(float x_scale, float y_scale, float z_scale) {
+    SetPoint(x() * x_scale, y() * y_scale, z() * z_scale);
+  }
+
+  constexpr float x() const { return x_; }
+  constexpr float y() const { return y_; }
+  constexpr float z() const { return z_; }
+
+  void set_x(float x) { x_ = x; }
+  void set_y(float y) { y_ = y; }
+  void set_z(float z) { z_ = z; }
+
+  void SetPoint(float x, float y, float z) {
+    x_ = x;
+    y_ = y;
+    z_ = z;
+  }
+
+  // Offset the point by the given vector.
+  void operator+=(const Vector3dF& v) {
+    x_ += v.x();
+    y_ += v.y();
+    z_ += v.z();
+  }
+
+  // Offset the point by the given vector's inverse.
+  void operator-=(const Vector3dF& v) {
+    x_ -= v.x();
+    y_ -= v.y();
+    z_ -= v.z();
+  }
+
+  // Returns the squared euclidean distance between two points.
+  float SquaredDistanceTo(const Point3F& other) const {
+    float dx = x_ - other.x_;
+    float dy = y_ - other.y_;
+    float dz = z_ - other.z_;
+    return dx * dx + dy * dy + dz * dz;
+  }
+
+  PointF AsPointF() const { return PointF(x_, y_); }
+
+  // Returns a string representation of 3d point.
+  std::string ToString() const;
+
+ private:
+  float x_;
+  float y_;
+  float z_;
+
+  // copy/assign are allowed.
+};
+
+inline bool operator==(const Point3F& lhs, const Point3F& rhs) {
+  return lhs.x() == rhs.x() && lhs.y() == rhs.y() && lhs.z() == rhs.z();
+}
+
+inline bool operator!=(const Point3F& lhs, const Point3F& rhs) {
+  return !(lhs == rhs);
+}
+
+// Add a vector to a point, producing a new point offset by the vector.
+GFX_EXPORT Point3F operator+(const Point3F& lhs, const Vector3dF& rhs);
+
+// Subtract a vector from a point, producing a new point offset by the vector's
+// inverse.
+GFX_EXPORT Point3F operator-(const Point3F& lhs, const Vector3dF& rhs);
+
+// Subtract one point from another, producing a vector that represents the
+// distances between the two points along each axis.
+GFX_EXPORT Vector3dF operator-(const Point3F& lhs, const Point3F& rhs);
+
+inline Point3F PointAtOffsetFromOrigin(const Vector3dF& offset) {
+  return Point3F(offset.x(), offset.y(), offset.z());
+}
+
+inline Point3F ScalePoint(const Point3F& p,
+                          float x_scale,
+                          float y_scale,
+                          float z_scale) {
+  return Point3F(p.x() * x_scale, p.y() * y_scale, p.z() * z_scale);
+}
+
+inline Point3F ScalePoint(const Point3F& p, const Vector3dF& v) {
+  return Point3F(p.x() * v.x(), p.y() * v.y(), p.z() * v.z());
+}
+
+inline Point3F ScalePoint(const Point3F& p, float scale) {
+  return ScalePoint(p, scale, scale, scale);
+}
+
+// This is declared here for use in gtest-based unit tests but is defined in
+// the //ui/gfx:test_support target. Depend on that to use this in your unit
+// test. This should not be used in production code - call ToString() instead.
+void PrintTo(const Point3F& point, ::std::ostream* os);
+
+}  // namespace gfx
+
+#endif  // UI_GFX_GEOMETRY_POINT3_F_H_
diff --git a/ui/gfx/geometry/point3_unittest.cc b/ui/gfx/geometry/point3_unittest.cc
new file mode 100644
index 0000000..999b1f4
--- /dev/null
+++ b/ui/gfx/geometry/point3_unittest.cc
@@ -0,0 +1,71 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+
+#include "base/macros.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/geometry/point3_f.h"
+
+namespace gfx {
+
+TEST(Point3Test, VectorArithmetic) {
+  gfx::Point3F a(1.6f, 5.1f, 3.2f);
+  gfx::Vector3dF v1(3.1f, -3.2f, 9.3f);
+  gfx::Vector3dF v2(-8.1f, 1.2f, 3.3f);
+
+  static const struct {
+    gfx::Point3F expected;
+    gfx::Point3F actual;
+  } tests[] = {
+    { gfx::Point3F(4.7f, 1.9f, 12.5f), a + v1 },
+    { gfx::Point3F(-1.5f, 8.3f, -6.1f), a - v1 },
+    { a, a - v1 + v1 },
+    { a, a + v1 - v1 },
+    { a, a + gfx::Vector3dF() },
+    { gfx::Point3F(12.8f, 0.7f, 9.2f), a + v1 - v2 },
+    { gfx::Point3F(-9.6f, 9.5f, -2.8f), a - v1 + v2 }
+  };
+
+  for (size_t i = 0; i < arraysize(tests); ++i)
+    EXPECT_EQ(tests[i].expected.ToString(),
+              tests[i].actual.ToString());
+
+  a += v1;
+  EXPECT_EQ(Point3F(4.7f, 1.9f, 12.5f).ToString(), a.ToString());
+
+  a -= v2;
+  EXPECT_EQ(Point3F(12.8f, 0.7f, 9.2f).ToString(), a.ToString());
+}
+
+TEST(Point3Test, VectorFromPoints) {
+  gfx::Point3F a(1.6f, 5.2f, 3.2f);
+  gfx::Vector3dF v1(3.1f, -3.2f, 9.3f);
+
+  gfx::Point3F b(a + v1);
+  EXPECT_EQ((b - a).ToString(), v1.ToString());
+}
+
+TEST(Point3Test, Scale) {
+  EXPECT_EQ(Point3F().ToString(), ScalePoint(Point3F(), 2.f).ToString());
+  EXPECT_EQ(Point3F().ToString(),
+            ScalePoint(Point3F(), 2.f, 2.f, 2.f).ToString());
+
+  EXPECT_EQ(Point3F(2.f, -2.f, 4.f).ToString(),
+            ScalePoint(Point3F(1.f, -1.f, 2.f), 2.f).ToString());
+  EXPECT_EQ(Point3F(2.f, -3.f, 8.f).ToString(),
+            ScalePoint(Point3F(1.f, -1.f, 2.f), 2.f, 3.f, 4.f).ToString());
+
+  Point3F zero;
+  zero.Scale(2.f);
+  zero.Scale(6.f, 3.f, 1.5f);
+  EXPECT_EQ(Point3F().ToString(), zero.ToString());
+
+  Point3F point(1.f, -1.f, 2.f);
+  point.Scale(2.f);
+  point.Scale(6.f, 3.f, 1.5f);
+  EXPECT_EQ(Point3F(12.f, -6.f, 6.f).ToString(), point.ToString());
+}
+
+}  // namespace gfx
diff --git a/ui/gfx/geometry/point_unittest.cc b/ui/gfx/geometry/point_unittest.cc
new file mode 100644
index 0000000..b453dd7
--- /dev/null
+++ b/ui/gfx/geometry/point_unittest.cc
@@ -0,0 +1,236 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+
+#include "base/macros.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/geometry/point.h"
+#include "ui/gfx/geometry/point_conversions.h"
+#include "ui/gfx/geometry/point_f.h"
+
+namespace gfx {
+
+TEST(PointTest, ToPointF) {
+  // Check that explicit conversion from integer to float compiles.
+  Point a(10, 20);
+  PointF b = PointF(a);
+
+  EXPECT_EQ(static_cast<float>(a.x()), b.x());
+  EXPECT_EQ(static_cast<float>(a.y()), b.y());
+}
+
+TEST(PointTest, IsOrigin) {
+  EXPECT_FALSE(Point(1, 0).IsOrigin());
+  EXPECT_FALSE(Point(0, 1).IsOrigin());
+  EXPECT_FALSE(Point(1, 2).IsOrigin());
+  EXPECT_FALSE(Point(-1, 0).IsOrigin());
+  EXPECT_FALSE(Point(0, -1).IsOrigin());
+  EXPECT_FALSE(Point(-1, -2).IsOrigin());
+  EXPECT_TRUE(Point(0, 0).IsOrigin());
+
+  EXPECT_FALSE(PointF(0.1f, 0).IsOrigin());
+  EXPECT_FALSE(PointF(0, 0.1f).IsOrigin());
+  EXPECT_FALSE(PointF(0.1f, 2).IsOrigin());
+  EXPECT_FALSE(PointF(-0.1f, 0).IsOrigin());
+  EXPECT_FALSE(PointF(0, -0.1f).IsOrigin());
+  EXPECT_FALSE(PointF(-0.1f, -2).IsOrigin());
+  EXPECT_TRUE(PointF(0, 0).IsOrigin());
+}
+
+TEST(PointTest, VectorArithmetic) {
+  Point a(1, 5);
+  Vector2d v1(3, -3);
+  Vector2d v2(-8, 1);
+
+  static const struct {
+    Point expected;
+    Point actual;
+  } tests[] = {
+    { Point(4, 2), a + v1 },
+    { Point(-2, 8), a - v1 },
+    { a, a - v1 + v1 },
+    { a, a + v1 - v1 },
+    { a, a + Vector2d() },
+    { Point(12, 1), a + v1 - v2 },
+    { Point(-10, 9), a - v1 + v2 }
+  };
+
+  for (size_t i = 0; i < arraysize(tests); ++i)
+    EXPECT_EQ(tests[i].expected.ToString(), tests[i].actual.ToString());
+}
+
+TEST(PointTest, OffsetFromPoint) {
+  Point a(1, 5);
+  Point b(-20, 8);
+  EXPECT_EQ(Vector2d(-20 - 1, 8 - 5).ToString(), (b - a).ToString());
+}
+
+TEST(PointTest, ToRoundedPoint) {
+  EXPECT_EQ(Point(0, 0), ToRoundedPoint(PointF(0, 0)));
+  EXPECT_EQ(Point(0, 0), ToRoundedPoint(PointF(0.0001f, 0.0001f)));
+  EXPECT_EQ(Point(0, 0), ToRoundedPoint(PointF(0.4999f, 0.4999f)));
+  EXPECT_EQ(Point(1, 1), ToRoundedPoint(PointF(0.5f, 0.5f)));
+  EXPECT_EQ(Point(1, 1), ToRoundedPoint(PointF(0.9999f, 0.9999f)));
+
+  EXPECT_EQ(Point(10, 10), ToRoundedPoint(PointF(10, 10)));
+  EXPECT_EQ(Point(10, 10), ToRoundedPoint(PointF(10.0001f, 10.0001f)));
+  EXPECT_EQ(Point(10, 10), ToRoundedPoint(PointF(10.4999f, 10.4999f)));
+  EXPECT_EQ(Point(11, 11), ToRoundedPoint(PointF(10.5f, 10.5f)));
+  EXPECT_EQ(Point(11, 11), ToRoundedPoint(PointF(10.9999f, 10.9999f)));
+
+  EXPECT_EQ(Point(-10, -10), ToRoundedPoint(PointF(-10, -10)));
+  EXPECT_EQ(Point(-10, -10), ToRoundedPoint(PointF(-10.0001f, -10.0001f)));
+  EXPECT_EQ(Point(-10, -10), ToRoundedPoint(PointF(-10.4999f, -10.4999f)));
+  EXPECT_EQ(Point(-11, -11), ToRoundedPoint(PointF(-10.5f, -10.5f)));
+  EXPECT_EQ(Point(-11, -11), ToRoundedPoint(PointF(-10.9999f, -10.9999f)));
+}
+
+TEST(PointTest, Scale) {
+  EXPECT_EQ(PointF().ToString(), ScalePoint(PointF(), 2).ToString());
+  EXPECT_EQ(PointF().ToString(), ScalePoint(PointF(), 2, 2).ToString());
+
+  EXPECT_EQ(PointF(2, -2).ToString(), ScalePoint(PointF(1, -1), 2).ToString());
+  EXPECT_EQ(PointF(2, -2).ToString(),
+            ScalePoint(PointF(1, -1), 2, 2).ToString());
+
+  PointF zero;
+  PointF one(1, -1);
+
+  zero.Scale(2);
+  zero.Scale(3, 1.5);
+
+  one.Scale(2);
+  one.Scale(3, 1.5);
+
+  EXPECT_EQ(PointF().ToString(), zero.ToString());
+  EXPECT_EQ(PointF(6, -3).ToString(), one.ToString());
+}
+
+TEST(PointTest, ClampPoint) {
+  Point a;
+
+  a = Point(3, 5);
+  EXPECT_EQ(Point(3, 5).ToString(), a.ToString());
+  a.SetToMax(Point(2, 4));
+  EXPECT_EQ(Point(3, 5).ToString(), a.ToString());
+  a.SetToMax(Point(3, 5));
+  EXPECT_EQ(Point(3, 5).ToString(), a.ToString());
+  a.SetToMax(Point(4, 2));
+  EXPECT_EQ(Point(4, 5).ToString(), a.ToString());
+  a.SetToMax(Point(8, 10));
+  EXPECT_EQ(Point(8, 10).ToString(), a.ToString());
+
+  a.SetToMin(Point(9, 11));
+  EXPECT_EQ(Point(8, 10).ToString(), a.ToString());
+  a.SetToMin(Point(8, 10));
+  EXPECT_EQ(Point(8, 10).ToString(), a.ToString());
+  a.SetToMin(Point(11, 9));
+  EXPECT_EQ(Point(8, 9).ToString(), a.ToString());
+  a.SetToMin(Point(7, 11));
+  EXPECT_EQ(Point(7, 9).ToString(), a.ToString());
+  a.SetToMin(Point(3, 5));
+  EXPECT_EQ(Point(3, 5).ToString(), a.ToString());
+}
+
+TEST(PointTest, ClampPointF) {
+  PointF a;
+
+  a = PointF(3.5f, 5.5f);
+  EXPECT_EQ(PointF(3.5f, 5.5f).ToString(), a.ToString());
+  a.SetToMax(PointF(2.5f, 4.5f));
+  EXPECT_EQ(PointF(3.5f, 5.5f).ToString(), a.ToString());
+  a.SetToMax(PointF(3.5f, 5.5f));
+  EXPECT_EQ(PointF(3.5f, 5.5f).ToString(), a.ToString());
+  a.SetToMax(PointF(4.5f, 2.5f));
+  EXPECT_EQ(PointF(4.5f, 5.5f).ToString(), a.ToString());
+  a.SetToMax(PointF(8.5f, 10.5f));
+  EXPECT_EQ(PointF(8.5f, 10.5f).ToString(), a.ToString());
+
+  a.SetToMin(PointF(9.5f, 11.5f));
+  EXPECT_EQ(PointF(8.5f, 10.5f).ToString(), a.ToString());
+  a.SetToMin(PointF(8.5f, 10.5f));
+  EXPECT_EQ(PointF(8.5f, 10.5f).ToString(), a.ToString());
+  a.SetToMin(PointF(11.5f, 9.5f));
+  EXPECT_EQ(PointF(8.5f, 9.5f).ToString(), a.ToString());
+  a.SetToMin(PointF(7.5f, 11.5f));
+  EXPECT_EQ(PointF(7.5f, 9.5f).ToString(), a.ToString());
+  a.SetToMin(PointF(3.5f, 5.5f));
+  EXPECT_EQ(PointF(3.5f, 5.5f).ToString(), a.ToString());
+}
+
+TEST(PointTest, Offset) {
+  Point test(3, 4);
+  test.Offset(5, -8);
+  EXPECT_EQ(test, Point(8, -4));
+}
+
+TEST(PointTest, VectorMath) {
+  Point test = Point(3, 4);
+  test += Vector2d(5, -8);
+  EXPECT_EQ(test, Point(8, -4));
+
+  Point test2 = Point(3, 4);
+  test2 -= Vector2d(5, -8);
+  EXPECT_EQ(test2, Point(-2, 12));
+}
+
+TEST(PointTest, IntegerOverflow) {
+  int int_max = std::numeric_limits<int>::max();
+  int int_min = std::numeric_limits<int>::min();
+
+  Point max_point(int_max, int_max);
+  Point min_point(int_min, int_min);
+  Point test;
+
+  test = Point();
+  test.Offset(int_max, int_max);
+  EXPECT_EQ(test, max_point);
+
+  test = Point();
+  test.Offset(int_min, int_min);
+  EXPECT_EQ(test, min_point);
+
+  test = Point(10, 20);
+  test.Offset(int_max, int_max);
+  EXPECT_EQ(test, max_point);
+
+  test = Point(-10, -20);
+  test.Offset(int_min, int_min);
+  EXPECT_EQ(test, min_point);
+
+  test = Point();
+  test += Vector2d(int_max, int_max);
+  EXPECT_EQ(test, max_point);
+
+  test = Point();
+  test += Vector2d(int_min, int_min);
+  EXPECT_EQ(test, min_point);
+
+  test = Point(10, 20);
+  test += Vector2d(int_max, int_max);
+  EXPECT_EQ(test, max_point);
+
+  test = Point(-10, -20);
+  test += Vector2d(int_min, int_min);
+  EXPECT_EQ(test, min_point);
+
+  test = Point();
+  test -= Vector2d(int_max, int_max);
+  EXPECT_EQ(test, Point(-int_max, -int_max));
+
+  test = Point();
+  test -= Vector2d(int_min, int_min);
+  EXPECT_EQ(test, max_point);
+
+  test = Point(10, 20);
+  test -= Vector2d(int_min, int_min);
+  EXPECT_EQ(test, max_point);
+
+  test = Point(-10, -20);
+  test -= Vector2d(int_max, int_max);
+  EXPECT_EQ(test, min_point);
+}
+
+}  // namespace gfx
diff --git a/ui/gfx/geometry/quad_f.cc b/ui/gfx/geometry/quad_f.cc
new file mode 100644
index 0000000..8ed8b91
--- /dev/null
+++ b/ui/gfx/geometry/quad_f.cc
@@ -0,0 +1,134 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/gfx/geometry/quad_f.h"
+
+#include <limits>
+
+#include "base/strings/stringprintf.h"
+
+namespace gfx {
+
+void QuadF::operator=(const RectF& rect) {
+  p1_ = PointF(rect.x(), rect.y());
+  p2_ = PointF(rect.right(), rect.y());
+  p3_ = PointF(rect.right(), rect.bottom());
+  p4_ = PointF(rect.x(), rect.bottom());
+}
+
+std::string QuadF::ToString() const {
+  return base::StringPrintf("%s;%s;%s;%s",
+                            p1_.ToString().c_str(),
+                            p2_.ToString().c_str(),
+                            p3_.ToString().c_str(),
+                            p4_.ToString().c_str());
+}
+
+static inline bool WithinEpsilon(float a, float b) {
+  return std::abs(a - b) < std::numeric_limits<float>::epsilon();
+}
+
+bool QuadF::IsRectilinear() const {
+  return
+      (WithinEpsilon(p1_.x(), p2_.x()) && WithinEpsilon(p2_.y(), p3_.y()) &&
+       WithinEpsilon(p3_.x(), p4_.x()) && WithinEpsilon(p4_.y(), p1_.y())) ||
+      (WithinEpsilon(p1_.y(), p2_.y()) && WithinEpsilon(p2_.x(), p3_.x()) &&
+       WithinEpsilon(p3_.y(), p4_.y()) && WithinEpsilon(p4_.x(), p1_.x()));
+}
+
+bool QuadF::IsCounterClockwise() const {
+  // This math computes the signed area of the quad. Positive area
+  // indicates the quad is clockwise; negative area indicates the quad is
+  // counter-clockwise. Note carefully: this is backwards from conventional
+  // math because our geometric space uses screen coordiantes with y-axis
+  // pointing downards.
+  // Reference: http://mathworld.wolfram.com/PolygonArea.html.
+  // The equation can be written:
+  // Signed area = determinant1 + determinant2 + determinant3 + determinant4
+  // In practise, Refactoring the computation of adding determinants so that
+  // reducing the number of operations. The equation is:
+  // Signed area = element1 + element2 - element3 - element4
+
+  float p24 = p2_.y() - p4_.y();
+  float p31 = p3_.y() - p1_.y();
+
+  // Up-cast to double so this cannot overflow.
+  double element1 = static_cast<double>(p1_.x()) * p24;
+  double element2 = static_cast<double>(p2_.x()) * p31;
+  double element3 = static_cast<double>(p3_.x()) * p24;
+  double element4 = static_cast<double>(p4_.x()) * p31;
+
+  return element1 + element2 < element3 + element4;
+}
+
+static inline bool PointIsInTriangle(const PointF& point,
+                                     const PointF& r1,
+                                     const PointF& r2,
+                                     const PointF& r3) {
+  // Compute the barycentric coordinates (u, v, w) of |point| relative to the
+  // triangle (r1, r2, r3) by the solving the system of equations:
+  //   1) point = u * r1 + v * r2 + w * r3
+  //   2) u + v + w = 1
+  // This algorithm comes from Christer Ericson's Real-Time Collision Detection.
+
+  Vector2dF r31 = r1 - r3;
+  Vector2dF r32 = r2 - r3;
+  Vector2dF r3p = point - r3;
+
+  // Promote to doubles so all the math below is done with doubles, because
+  // otherwise it gets incorrect results on arm64.
+  double r31x = r31.x();
+  double r31y = r31.y();
+  double r32x = r32.x();
+  double r32y = r32.y();
+
+  double denom = r32y * r31x - r32x * r31y;
+  double u = (r32y * r3p.x() - r32x * r3p.y()) / denom;
+  double v = (r31x * r3p.y() - r31y * r3p.x()) / denom;
+  double w = 1.0 - u - v;
+
+  // Use the barycentric coordinates to test if |point| is inside the
+  // triangle (r1, r2, r2).
+  return (u >= 0) && (v >= 0) && (w >= 0);
+}
+
+bool QuadF::Contains(const PointF& point) const {
+  return PointIsInTriangle(point, p1_, p2_, p3_)
+      || PointIsInTriangle(point, p1_, p3_, p4_);
+}
+
+void QuadF::Scale(float x_scale, float y_scale) {
+  p1_.Scale(x_scale, y_scale);
+  p2_.Scale(x_scale, y_scale);
+  p3_.Scale(x_scale, y_scale);
+  p4_.Scale(x_scale, y_scale);
+}
+
+void QuadF::operator+=(const Vector2dF& rhs) {
+  p1_ += rhs;
+  p2_ += rhs;
+  p3_ += rhs;
+  p4_ += rhs;
+}
+
+void QuadF::operator-=(const Vector2dF& rhs) {
+  p1_ -= rhs;
+  p2_ -= rhs;
+  p3_ -= rhs;
+  p4_ -= rhs;
+}
+
+QuadF operator+(const QuadF& lhs, const Vector2dF& rhs) {
+  QuadF result = lhs;
+  result += rhs;
+  return result;
+}
+
+QuadF operator-(const QuadF& lhs, const Vector2dF& rhs) {
+  QuadF result = lhs;
+  result -= rhs;
+  return result;
+}
+
+}  // namespace gfx
diff --git a/ui/gfx/geometry/quad_f.h b/ui/gfx/geometry/quad_f.h
new file mode 100644
index 0000000..8e45b3e
--- /dev/null
+++ b/ui/gfx/geometry/quad_f.h
@@ -0,0 +1,130 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_GFX_GEOMETRY_QUAD_F_H_
+#define UI_GFX_GEOMETRY_QUAD_F_H_
+
+#include <stddef.h>
+
+#include <algorithm>
+#include <cmath>
+#include <iosfwd>
+#include <string>
+
+#include "base/logging.h"
+#include "ui/gfx/geometry/point_f.h"
+#include "ui/gfx/geometry/rect_f.h"
+#include "ui/gfx/gfx_export.h"
+
+namespace gfx {
+
+// A Quad is defined by four corners, allowing it to have edges that are not
+// axis-aligned, unlike a Rect.
+class GFX_EXPORT QuadF {
+ public:
+  constexpr QuadF() = default;
+  constexpr QuadF(const PointF& p1,
+                  const PointF& p2,
+                  const PointF& p3,
+                  const PointF& p4)
+      : p1_(p1), p2_(p2), p3_(p3), p4_(p4) {}
+
+  constexpr explicit QuadF(const RectF& rect)
+      : p1_(rect.x(), rect.y()),
+        p2_(rect.right(), rect.y()),
+        p3_(rect.right(), rect.bottom()),
+        p4_(rect.x(), rect.bottom()) {}
+
+  void operator=(const RectF& rect);
+
+  void set_p1(const PointF& p) { p1_ = p; }
+  void set_p2(const PointF& p) { p2_ = p; }
+  void set_p3(const PointF& p) { p3_ = p; }
+  void set_p4(const PointF& p) { p4_ = p; }
+
+  constexpr const PointF& p1() const { return p1_; }
+  constexpr const PointF& p2() const { return p2_; }
+  constexpr const PointF& p3() const { return p3_; }
+  constexpr const PointF& p4() const { return p4_; }
+
+  // Returns true if the quad is an axis-aligned rectangle.
+  bool IsRectilinear() const;
+
+  // Returns true if the points of the quad are in counter-clockwise order. This
+  // assumes that the quad is convex, and that no three points are collinear.
+  bool IsCounterClockwise() const;
+
+  // Returns true if the |point| is contained within the quad, or lies on on
+  // edge of the quad. This assumes that the quad is convex.
+  bool Contains(const gfx::PointF& point) const;
+
+  // Returns a rectangle that bounds the four points of the quad. The points of
+  // the quad may lie on the right/bottom edge of the resulting rectangle,
+  // rather than being strictly inside it.
+  RectF BoundingBox() const {
+    float rl = std::min(std::min(p1_.x(), p2_.x()), std::min(p3_.x(), p4_.x()));
+    float rr = std::max(std::max(p1_.x(), p2_.x()), std::max(p3_.x(), p4_.x()));
+    float rt = std::min(std::min(p1_.y(), p2_.y()), std::min(p3_.y(), p4_.y()));
+    float rb = std::max(std::max(p1_.y(), p2_.y()), std::max(p3_.y(), p4_.y()));
+    return RectF(rl, rt, rr - rl, rb - rt);
+  }
+
+  // Realigns the corners in the quad by rotating them n corners to the right.
+  void Realign(size_t times) {
+    DCHECK_LE(times, 4u);
+    for (size_t i = 0; i < times; ++i) {
+      PointF temp = p1_;
+      p1_ = p2_;
+      p2_ = p3_;
+      p3_ = p4_;
+      p4_ = temp;
+    }
+  }
+
+  // Add a vector to the quad, offseting each point in the quad by the vector.
+  void operator+=(const Vector2dF& rhs);
+  // Subtract a vector from the quad, offseting each point in the quad by the
+  // inverse of the vector.
+  void operator-=(const Vector2dF& rhs);
+
+  // Scale each point in the quad by the |scale| factor.
+  void Scale(float scale) { Scale(scale, scale); }
+
+  // Scale each point in the quad by the scale factors along each axis.
+  void Scale(float x_scale, float y_scale);
+
+  // Returns a string representation of quad.
+  std::string ToString() const;
+
+ private:
+  PointF p1_;
+  PointF p2_;
+  PointF p3_;
+  PointF p4_;
+};
+
+inline bool operator==(const QuadF& lhs, const QuadF& rhs) {
+  return
+      lhs.p1() == rhs.p1() && lhs.p2() == rhs.p2() &&
+      lhs.p3() == rhs.p3() && lhs.p4() == rhs.p4();
+}
+
+inline bool operator!=(const QuadF& lhs, const QuadF& rhs) {
+  return !(lhs == rhs);
+}
+
+// Add a vector to a quad, offseting each point in the quad by the vector.
+GFX_EXPORT QuadF operator+(const QuadF& lhs, const Vector2dF& rhs);
+// Subtract a vector from a quad, offseting each point in the quad by the
+// inverse of the vector.
+GFX_EXPORT QuadF operator-(const QuadF& lhs, const Vector2dF& rhs);
+
+// This is declared here for use in gtest-based unit tests but is defined in
+// the //ui/gfx:test_support target. Depend on that to use this in your unit
+// test. This should not be used in production code - call ToString() instead.
+void PrintTo(const QuadF& quad, ::std::ostream* os);
+
+}  // namespace gfx
+
+#endif  // UI_GFX_GEOMETRY_QUAD_F_H_
diff --git a/ui/gfx/geometry/quad_unittest.cc b/ui/gfx/geometry/quad_unittest.cc
new file mode 100644
index 0000000..2731583
--- /dev/null
+++ b/ui/gfx/geometry/quad_unittest.cc
@@ -0,0 +1,361 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+
+#include "base/macros.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/geometry/quad_f.h"
+#include "ui/gfx/geometry/rect_f.h"
+
+namespace gfx {
+
+TEST(QuadTest, Construction) {
+  // Verify constructors.
+  PointF a(1, 1);
+  PointF b(2, 1);
+  PointF c(2, 2);
+  PointF d(1, 2);
+  PointF e;
+  QuadF q1;
+  QuadF q2(e, e, e, e);
+  QuadF q3(a, b, c, d);
+  QuadF q4(BoundingRect(a, c));
+  EXPECT_EQ(q1, q2);
+  EXPECT_EQ(q3, q4);
+
+  // Verify getters.
+  EXPECT_EQ(q3.p1(), a);
+  EXPECT_EQ(q3.p2(), b);
+  EXPECT_EQ(q3.p3(), c);
+  EXPECT_EQ(q3.p4(), d);
+
+  // Verify setters.
+  q3.set_p1(b);
+  q3.set_p2(c);
+  q3.set_p3(d);
+  q3.set_p4(a);
+  EXPECT_EQ(q3.p1(), b);
+  EXPECT_EQ(q3.p2(), c);
+  EXPECT_EQ(q3.p3(), d);
+  EXPECT_EQ(q3.p4(), a);
+
+  // Verify operator=(Rect)
+  EXPECT_NE(q1, q4);
+  q1 = BoundingRect(a, c);
+  EXPECT_EQ(q1, q4);
+
+  // Verify operator=(Quad)
+  EXPECT_NE(q1, q3);
+  q1 = q3;
+  EXPECT_EQ(q1, q3);
+}
+
+TEST(QuadTest, AddingVectors) {
+  PointF a(1, 1);
+  PointF b(2, 1);
+  PointF c(2, 2);
+  PointF d(1, 2);
+  Vector2dF v(3.5f, -2.5f);
+
+  QuadF q1(a, b, c, d);
+  QuadF added = q1 + v;
+  q1 += v;
+  QuadF expected1(PointF(4.5f, -1.5f),
+                  PointF(5.5f, -1.5f),
+                  PointF(5.5f, -0.5f),
+                  PointF(4.5f, -0.5f));
+  EXPECT_EQ(expected1, added);
+  EXPECT_EQ(expected1, q1);
+
+  QuadF q2(a, b, c, d);
+  QuadF subtracted = q2 - v;
+  q2 -= v;
+  QuadF expected2(PointF(-2.5f, 3.5f),
+                  PointF(-1.5f, 3.5f),
+                  PointF(-1.5f, 4.5f),
+                  PointF(-2.5f, 4.5f));
+  EXPECT_EQ(expected2, subtracted);
+  EXPECT_EQ(expected2, q2);
+
+  QuadF q3(a, b, c, d);
+  q3 += v;
+  q3 -= v;
+  EXPECT_EQ(QuadF(a, b, c, d), q3);
+  EXPECT_EQ(q3, (q3 + v - v));
+}
+
+TEST(QuadTest, IsRectilinear) {
+  PointF a(1, 1);
+  PointF b(2, 1);
+  PointF c(2, 2);
+  PointF d(1, 2);
+  Vector2dF v(3.5f, -2.5f);
+
+  EXPECT_TRUE(QuadF().IsRectilinear());
+  EXPECT_TRUE(QuadF(a, b, c, d).IsRectilinear());
+  EXPECT_TRUE((QuadF(a, b, c, d) + v).IsRectilinear());
+
+  float epsilon = std::numeric_limits<float>::epsilon();
+  PointF a2(1 + epsilon / 2, 1 + epsilon / 2);
+  PointF b2(2 + epsilon / 2, 1 + epsilon / 2);
+  PointF c2(2 + epsilon / 2, 2 + epsilon / 2);
+  PointF d2(1 + epsilon / 2, 2 + epsilon / 2);
+  EXPECT_TRUE(QuadF(a2, b, c, d).IsRectilinear());
+  EXPECT_TRUE((QuadF(a2, b, c, d) + v).IsRectilinear());
+  EXPECT_TRUE(QuadF(a, b2, c, d).IsRectilinear());
+  EXPECT_TRUE((QuadF(a, b2, c, d) + v).IsRectilinear());
+  EXPECT_TRUE(QuadF(a, b, c2, d).IsRectilinear());
+  EXPECT_TRUE((QuadF(a, b, c2, d) + v).IsRectilinear());
+  EXPECT_TRUE(QuadF(a, b, c, d2).IsRectilinear());
+  EXPECT_TRUE((QuadF(a, b, c, d2) + v).IsRectilinear());
+
+  struct {
+    PointF a_off, b_off, c_off, d_off;
+  } tests[] = {
+    {
+      PointF(1, 1.00001f),
+      PointF(2, 1.00001f),
+      PointF(2, 2.00001f),
+      PointF(1, 2.00001f)
+    },
+    {
+      PointF(1.00001f, 1),
+      PointF(2.00001f, 1),
+      PointF(2.00001f, 2),
+      PointF(1.00001f, 2)
+    },
+    {
+      PointF(1.00001f, 1.00001f),
+      PointF(2.00001f, 1.00001f),
+      PointF(2.00001f, 2.00001f),
+      PointF(1.00001f, 2.00001f)
+    },
+    {
+      PointF(1, 0.99999f),
+      PointF(2, 0.99999f),
+      PointF(2, 1.99999f),
+      PointF(1, 1.99999f)
+    },
+    {
+      PointF(0.99999f, 1),
+      PointF(1.99999f, 1),
+      PointF(1.99999f, 2),
+      PointF(0.99999f, 2)
+    },
+    {
+      PointF(0.99999f, 0.99999f),
+      PointF(1.99999f, 0.99999f),
+      PointF(1.99999f, 1.99999f),
+      PointF(0.99999f, 1.99999f)
+    }
+  };
+
+  for (size_t i = 0; i < arraysize(tests); ++i) {
+    PointF a_off = tests[i].a_off;
+    PointF b_off = tests[i].b_off;
+    PointF c_off = tests[i].c_off;
+    PointF d_off = tests[i].d_off;
+
+    EXPECT_FALSE(QuadF(a_off, b, c, d).IsRectilinear());
+    EXPECT_FALSE((QuadF(a_off, b, c, d) + v).IsRectilinear());
+    EXPECT_FALSE(QuadF(a, b_off, c, d).IsRectilinear());
+    EXPECT_FALSE((QuadF(a, b_off, c, d) + v).IsRectilinear());
+    EXPECT_FALSE(QuadF(a, b, c_off, d).IsRectilinear());
+    EXPECT_FALSE((QuadF(a, b, c_off, d) + v).IsRectilinear());
+    EXPECT_FALSE(QuadF(a, b, c, d_off).IsRectilinear());
+    EXPECT_FALSE((QuadF(a, b, c, d_off) + v).IsRectilinear());
+    EXPECT_FALSE(QuadF(a_off, b, c_off, d).IsRectilinear());
+    EXPECT_FALSE((QuadF(a_off, b, c_off, d) + v).IsRectilinear());
+    EXPECT_FALSE(QuadF(a, b_off, c, d_off).IsRectilinear());
+    EXPECT_FALSE((QuadF(a, b_off, c, d_off) + v).IsRectilinear());
+    EXPECT_FALSE(QuadF(a, b_off, c_off, d_off).IsRectilinear());
+    EXPECT_FALSE((QuadF(a, b_off, c_off, d_off) + v).IsRectilinear());
+    EXPECT_FALSE(QuadF(a_off, b, c_off, d_off).IsRectilinear());
+    EXPECT_FALSE((QuadF(a_off, b, c_off, d_off) + v).IsRectilinear());
+    EXPECT_FALSE(QuadF(a_off, b_off, c, d_off).IsRectilinear());
+    EXPECT_FALSE((QuadF(a_off, b_off, c, d_off) + v).IsRectilinear());
+    EXPECT_FALSE(QuadF(a_off, b_off, c_off, d).IsRectilinear());
+    EXPECT_FALSE((QuadF(a_off, b_off, c_off, d) + v).IsRectilinear());
+    EXPECT_TRUE(QuadF(a_off, b_off, c_off, d_off).IsRectilinear());
+    EXPECT_TRUE((QuadF(a_off, b_off, c_off, d_off) + v).IsRectilinear());
+  }
+}
+
+TEST(QuadTest, IsCounterClockwise) {
+  PointF a1(1, 1);
+  PointF b1(2, 1);
+  PointF c1(2, 2);
+  PointF d1(1, 2);
+  EXPECT_FALSE(QuadF(a1, b1, c1, d1).IsCounterClockwise());
+  EXPECT_FALSE(QuadF(b1, c1, d1, a1).IsCounterClockwise());
+  EXPECT_TRUE(QuadF(a1, d1, c1, b1).IsCounterClockwise());
+  EXPECT_TRUE(QuadF(c1, b1, a1, d1).IsCounterClockwise());
+
+  // Slightly more complicated quads should work just as easily.
+  PointF a2(1.3f, 1.4f);
+  PointF b2(-0.7f, 4.9f);
+  PointF c2(1.8f, 6.2f);
+  PointF d2(2.1f, 1.6f);
+  EXPECT_TRUE(QuadF(a2, b2, c2, d2).IsCounterClockwise());
+  EXPECT_TRUE(QuadF(b2, c2, d2, a2).IsCounterClockwise());
+  EXPECT_FALSE(QuadF(a2, d2, c2, b2).IsCounterClockwise());
+  EXPECT_FALSE(QuadF(c2, b2, a2, d2).IsCounterClockwise());
+
+  // Quads with 3 collinear points should work correctly, too.
+  PointF a3(0, 0);
+  PointF b3(1, 0);
+  PointF c3(2, 0);
+  PointF d3(1, 1);
+  EXPECT_FALSE(QuadF(a3, b3, c3, d3).IsCounterClockwise());
+  EXPECT_FALSE(QuadF(b3, c3, d3, a3).IsCounterClockwise());
+  EXPECT_TRUE(QuadF(a3, d3, c3, b3).IsCounterClockwise());
+  // The next expectation in particular would fail for an implementation
+  // that incorrectly uses only a cross product of the first 3 vertices.
+  EXPECT_TRUE(QuadF(c3, b3, a3, d3).IsCounterClockwise());
+
+  // Non-convex quads should work correctly, too.
+  PointF a4(0, 0);
+  PointF b4(1, 1);
+  PointF c4(2, 0);
+  PointF d4(1, 3);
+  EXPECT_FALSE(QuadF(a4, b4, c4, d4).IsCounterClockwise());
+  EXPECT_FALSE(QuadF(b4, c4, d4, a4).IsCounterClockwise());
+  EXPECT_TRUE(QuadF(a4, d4, c4, b4).IsCounterClockwise());
+  EXPECT_TRUE(QuadF(c4, b4, a4, d4).IsCounterClockwise());
+
+  // A quad with huge coordinates should not fail this check due to
+  // single-precision overflow.
+  PointF a5(1e30f, 1e30f);
+  PointF b5(1e35f, 1e30f);
+  PointF c5(1e35f, 1e35f);
+  PointF d5(1e30f, 1e35f);
+  EXPECT_FALSE(QuadF(a5, b5, c5, d5).IsCounterClockwise());
+  EXPECT_FALSE(QuadF(b5, c5, d5, a5).IsCounterClockwise());
+  EXPECT_TRUE(QuadF(a5, d5, c5, b5).IsCounterClockwise());
+  EXPECT_TRUE(QuadF(c5, b5, a5, d5).IsCounterClockwise());
+}
+
+TEST(QuadTest, BoundingBox) {
+  RectF r(3.2f, 5.4f, 7.007f, 12.01f);
+  EXPECT_EQ(r, QuadF(r).BoundingBox());
+
+  PointF a(1.3f, 1.4f);
+  PointF b(-0.7f, 4.9f);
+  PointF c(1.8f, 6.2f);
+  PointF d(2.1f, 1.6f);
+  float left = -0.7f;
+  float top = 1.4f;
+  float right = 2.1f;
+  float bottom = 6.2f;
+  EXPECT_EQ(RectF(left, top, right - left, bottom - top),
+            QuadF(a, b, c, d).BoundingBox());
+}
+
+TEST(QuadTest, ContainsPoint) {
+  PointF a(1.3f, 1.4f);
+  PointF b(-0.8f, 4.4f);
+  PointF c(1.8f, 6.1f);
+  PointF d(2.1f, 1.6f);
+
+  Vector2dF epsilon_x(2 * std::numeric_limits<float>::epsilon(), 0);
+  Vector2dF epsilon_y(0, 2 * std::numeric_limits<float>::epsilon());
+
+  Vector2dF ac_center = c - a;
+  ac_center.Scale(0.5f);
+  Vector2dF bd_center = d - b;
+  bd_center.Scale(0.5f);
+
+  EXPECT_TRUE(QuadF(a, b, c, d).Contains(a + ac_center));
+  EXPECT_TRUE(QuadF(a, b, c, d).Contains(b + bd_center));
+  EXPECT_TRUE(QuadF(a, b, c, d).Contains(c - ac_center));
+  EXPECT_TRUE(QuadF(a, b, c, d).Contains(d - bd_center));
+  EXPECT_FALSE(QuadF(a, b, c, d).Contains(a - ac_center));
+  EXPECT_FALSE(QuadF(a, b, c, d).Contains(b - bd_center));
+  EXPECT_FALSE(QuadF(a, b, c, d).Contains(c + ac_center));
+  EXPECT_FALSE(QuadF(a, b, c, d).Contains(d + bd_center));
+
+  EXPECT_TRUE(QuadF(a, b, c, d).Contains(a));
+  EXPECT_FALSE(QuadF(a, b, c, d).Contains(a - epsilon_x));
+  EXPECT_FALSE(QuadF(a, b, c, d).Contains(a - epsilon_y));
+  EXPECT_FALSE(QuadF(a, b, c, d).Contains(a + epsilon_x));
+  EXPECT_TRUE(QuadF(a, b, c, d).Contains(a + epsilon_y));
+
+  EXPECT_TRUE(QuadF(a, b, c, d).Contains(b));
+  EXPECT_FALSE(QuadF(a, b, c, d).Contains(b - epsilon_x));
+  EXPECT_FALSE(QuadF(a, b, c, d).Contains(b - epsilon_y));
+  EXPECT_TRUE(QuadF(a, b, c, d).Contains(b + epsilon_x));
+  EXPECT_FALSE(QuadF(a, b, c, d).Contains(b + epsilon_y));
+
+  EXPECT_TRUE(QuadF(a, b, c, d).Contains(c));
+  EXPECT_FALSE(QuadF(a, b, c, d).Contains(c - epsilon_x));
+  EXPECT_TRUE(QuadF(a, b, c, d).Contains(c - epsilon_y));
+  EXPECT_FALSE(QuadF(a, b, c, d).Contains(c + epsilon_x));
+  EXPECT_FALSE(QuadF(a, b, c, d).Contains(c + epsilon_y));
+
+  EXPECT_TRUE(QuadF(a, b, c, d).Contains(d));
+  EXPECT_TRUE(QuadF(a, b, c, d).Contains(d - epsilon_x));
+  EXPECT_FALSE(QuadF(a, b, c, d).Contains(d - epsilon_y));
+  EXPECT_FALSE(QuadF(a, b, c, d).Contains(d + epsilon_x));
+  EXPECT_FALSE(QuadF(a, b, c, d).Contains(d + epsilon_y));
+
+  // Test a simple square.
+  PointF s1(-1, -1);
+  PointF s2(1, -1);
+  PointF s3(1, 1);
+  PointF s4(-1, 1);
+  // Top edge.
+  EXPECT_FALSE(QuadF(s1, s2, s3, s4).Contains(PointF(-1.1f, -1.0f)));
+  EXPECT_TRUE(QuadF(s1, s2, s3, s4).Contains(PointF(-1.0f, -1.0f)));
+  EXPECT_TRUE(QuadF(s1, s2, s3, s4).Contains(PointF(0.0f, -1.0f)));
+  EXPECT_TRUE(QuadF(s1, s2, s3, s4).Contains(PointF(1.0f, -1.0f)));
+  EXPECT_FALSE(QuadF(s1, s2, s3, s4).Contains(PointF(1.1f, -1.0f)));
+  // Bottom edge.
+  EXPECT_FALSE(QuadF(s1, s2, s3, s4).Contains(PointF(-1.1f, 1.0f)));
+  EXPECT_TRUE(QuadF(s1, s2, s3, s4).Contains(PointF(-1.0f, 1.0f)));
+  EXPECT_TRUE(QuadF(s1, s2, s3, s4).Contains(PointF(0.0f, 1.0f)));
+  EXPECT_TRUE(QuadF(s1, s2, s3, s4).Contains(PointF(1.0f, 1.0f)));
+  EXPECT_FALSE(QuadF(s1, s2, s3, s4).Contains(PointF(1.1f, 1.0f)));
+  // Left edge.
+  EXPECT_FALSE(QuadF(s1, s2, s3, s4).Contains(PointF(-1.0f, -1.1f)));
+  EXPECT_TRUE(QuadF(s1, s2, s3, s4).Contains(PointF(-1.0f, -1.0f)));
+  EXPECT_TRUE(QuadF(s1, s2, s3, s4).Contains(PointF(-1.0f, 0.0f)));
+  EXPECT_TRUE(QuadF(s1, s2, s3, s4).Contains(PointF(-1.0f, 1.0f)));
+  EXPECT_FALSE(QuadF(s1, s2, s3, s4).Contains(PointF(-1.0f, 1.1f)));
+  // Right edge.
+  EXPECT_FALSE(QuadF(s1, s2, s3, s4).Contains(PointF(1.0f, -1.1f)));
+  EXPECT_TRUE(QuadF(s1, s2, s3, s4).Contains(PointF(1.0f, -1.0f)));
+  EXPECT_TRUE(QuadF(s1, s2, s3, s4).Contains(PointF(1.0f, 0.0f)));
+  EXPECT_TRUE(QuadF(s1, s2, s3, s4).Contains(PointF(1.0f, 1.0f)));
+  EXPECT_FALSE(QuadF(s1, s2, s3, s4).Contains(PointF(1.0f, 1.1f)));
+  // Centered inside.
+  EXPECT_TRUE(QuadF(s1, s2, s3, s4).Contains(PointF(0, 0)));
+  // Centered outside.
+  EXPECT_FALSE(QuadF(s1, s2, s3, s4).Contains(PointF(-1.1f, 0)));
+  EXPECT_FALSE(QuadF(s1, s2, s3, s4).Contains(PointF(1.1f, 0)));
+  EXPECT_FALSE(QuadF(s1, s2, s3, s4).Contains(PointF(0, -1.1f)));
+  EXPECT_FALSE(QuadF(s1, s2, s3, s4).Contains(PointF(0, 1.1f)));
+}
+
+TEST(QuadTest, Scale) {
+  PointF a(1.3f, 1.4f);
+  PointF b(-0.8f, 4.4f);
+  PointF c(1.8f, 6.1f);
+  PointF d(2.1f, 1.6f);
+  QuadF q1(a, b, c, d);
+  q1.Scale(1.5f);
+
+  PointF a_scaled = ScalePoint(a, 1.5f);
+  PointF b_scaled = ScalePoint(b, 1.5f);
+  PointF c_scaled = ScalePoint(c, 1.5f);
+  PointF d_scaled = ScalePoint(d, 1.5f);
+  EXPECT_EQ(q1, QuadF(a_scaled, b_scaled, c_scaled, d_scaled));
+
+  QuadF q2;
+  q2.Scale(1.5f);
+  EXPECT_EQ(q2, q2);
+}
+
+}  // namespace gfx
diff --git a/ui/gfx/geometry/quaternion.cc b/ui/gfx/geometry/quaternion.cc
new file mode 100644
index 0000000..f2be00b
--- /dev/null
+++ b/ui/gfx/geometry/quaternion.cc
@@ -0,0 +1,106 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/gfx/geometry/quaternion.h"
+
+#include <algorithm>
+#include <cmath>
+
+#include "base/numerics/math_constants.h"
+#include "base/strings/stringprintf.h"
+#include "ui/gfx/geometry/vector3d_f.h"
+
+namespace gfx {
+
+namespace {
+
+const double kEpsilon = 1e-5;
+
+}  // namespace
+
+Quaternion::Quaternion(const Vector3dF& axis, double theta) {
+  // Rotation angle is the product of |angle| and the magnitude of |axis|.
+  double length = axis.Length();
+  if (std::abs(length) < kEpsilon)
+    return;
+
+  Vector3dF normalized = axis;
+  normalized.Scale(1.0 / length);
+
+  theta *= 0.5;
+  double s = sin(theta);
+  x_ = normalized.x() * s;
+  y_ = normalized.y() * s;
+  z_ = normalized.z() * s;
+  w_ = cos(theta);
+}
+
+Quaternion::Quaternion(const Vector3dF& from, const Vector3dF& to) {
+  double dot = gfx::DotProduct(from, to);
+  double norm = sqrt(from.LengthSquared() * to.LengthSquared());
+  double real = norm + dot;
+  gfx::Vector3dF axis;
+  if (real < kEpsilon * norm) {
+    real = 0.0f;
+    axis = std::abs(from.x()) > std::abs(from.z())
+               ? gfx::Vector3dF{-from.y(), from.x(), 0.0}
+               : gfx::Vector3dF{0.0, -from.z(), from.y()};
+  } else {
+    axis = gfx::CrossProduct(from, to);
+  }
+  x_ = axis.x();
+  y_ = axis.y();
+  z_ = axis.z();
+  w_ = real;
+  *this = this->Normalized();
+}
+
+// Taken from http://www.w3.org/TR/css3-transforms/.
+Quaternion Quaternion::Slerp(const Quaternion& q, double t) const {
+  double dot = x_ * q.x_ + y_ * q.y_ + z_ * q.z_ + w_ * q.w_;
+
+  // Clamp dot to -1.0 <= dot <= 1.0.
+  dot = std::min(std::max(dot, -1.0), 1.0);
+
+  // Quaternions are facing the same direction.
+  if (std::abs(dot - 1.0) < kEpsilon || std::abs(dot + 1.0) < kEpsilon)
+    return *this;
+
+  double denom = std::sqrt(1.0 - dot * dot);
+  double theta = std::acos(dot);
+  double w = std::sin(t * theta) * (1.0 / denom);
+
+  double s1 = std::cos(t * theta) - dot * w;
+  double s2 = w;
+
+  return (s1 * *this) + (s2 * q);
+}
+
+Quaternion Quaternion::Lerp(const Quaternion& q, double t) const {
+  return (((1.0 - t) * *this) + (t * q)).Normalized();
+}
+
+double Quaternion::Length() const {
+  return x_ * x_ + y_ * y_ + z_ * z_ + w_ * w_;
+}
+
+Quaternion Quaternion::Normalized() const {
+  double length = Length();
+  if (length < kEpsilon)
+    return *this;
+  return *this / sqrt(length);
+}
+
+std::string Quaternion::ToString() const {
+  // q = (con(abs(v_theta)/2), v_theta/abs(v_theta) * sin(abs(v_theta)/2))
+  float abs_theta = acos(w_) * 2;
+  float scale = 1. / sin(abs_theta * .5);
+  gfx::Vector3dF v(x_, y_, z_);
+  v.Scale(scale);
+  return base::StringPrintf("[%f %f %f %f], v:", x_, y_, z_, w_) +
+         v.ToString() +
+         base::StringPrintf(", θ:%fπ", abs_theta / base::kPiFloat);
+}
+
+}  // namespace gfx
diff --git a/ui/gfx/geometry/quaternion.h b/ui/gfx/geometry/quaternion.h
new file mode 100644
index 0000000..7f65b79
--- /dev/null
+++ b/ui/gfx/geometry/quaternion.h
@@ -0,0 +1,93 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_GFX_GEOMETRY_QUATERNION_
+#define UI_GFX_GEOMETRY_QUATERNION_
+
+#include <string>
+
+#include "ui/gfx/gfx_export.h"
+
+namespace gfx {
+
+class Vector3dF;
+
+class GFX_EXPORT Quaternion {
+ public:
+  constexpr Quaternion() = default;
+  constexpr Quaternion(double x, double y, double z, double w)
+      : x_(x), y_(y), z_(z), w_(w) {}
+  Quaternion(const Vector3dF& axis, double angle);
+
+  // Constructs a quaternion representing a rotation between |from| and |to|.
+  Quaternion(const Vector3dF& from, const Vector3dF& to);
+
+  constexpr double x() const { return x_; }
+  void set_x(double x) { x_ = x; }
+
+  constexpr double y() const { return y_; }
+  void set_y(double y) { y_ = y; }
+
+  constexpr double z() const { return z_; }
+  void set_z(double z) { z_ = z; }
+
+  constexpr double w() const { return w_; }
+  void set_w(double w) { w_ = w; }
+
+  Quaternion operator+(const Quaternion& q) const {
+    return {q.x_ + x_, q.y_ + y_, q.z_ + z_, q.w_ + w_};
+  }
+
+  Quaternion operator*(const Quaternion& q) const {
+    return {w_ * q.x_ + x_ * q.w_ + y_ * q.z_ - z_ * q.y_,
+            w_ * q.y_ - x_ * q.z_ + y_ * q.w_ + z_ * q.x_,
+            w_ * q.z_ + x_ * q.y_ - y_ * q.x_ + z_ * q.w_,
+            w_ * q.w_ - x_ * q.x_ - y_ * q.y_ - z_ * q.z_};
+  }
+
+  Quaternion inverse() const { return {-x_, -y_, -z_, w_}; }
+
+  // Blends with the given quaternion, |q|, via spherical linear interpolation.
+  // Values of |t| in the range [0, 1] will interpolate between |this| and |q|,
+  // and values outside that range will extrapolate beyond in either direction.
+  Quaternion Slerp(const Quaternion& q, double t) const;
+
+  // Blends with the given quaternion, |q|, via linear interpolation. This is
+  // rarely what you want. Use only if you know what you're doing.
+  // Values of |t| in the range [0, 1] will interpolate between |this| and |q|,
+  // and values outside that range will extrapolate beyond in either direction.
+  Quaternion Lerp(const Quaternion& q, double t) const;
+
+  double Length() const;
+
+  Quaternion Normalized() const;
+
+  std::string ToString() const;
+
+ private:
+  double x_ = 0.0;
+  double y_ = 0.0;
+  double z_ = 0.0;
+  double w_ = 1.0;
+};
+
+// |s| is an arbitrary, real constant.
+inline Quaternion operator*(const Quaternion& q, double s) {
+  return Quaternion(q.x() * s, q.y() * s, q.z() * s, q.w() * s);
+}
+
+// |s| is an arbitrary, real constant.
+inline Quaternion operator*(double s, const Quaternion& q) {
+  return Quaternion(q.x() * s, q.y() * s, q.z() * s, q.w() * s);
+}
+
+// |s| is an arbitrary, real constant.
+inline Quaternion operator/(const Quaternion& q, double s) {
+  double inv = 1.0 / s;
+  return q * inv;
+}
+
+}  // namespace gfx
+
+#endif  // UI_GFX_GEOMETRY_QUATERNION_
diff --git a/ui/gfx/geometry/quaternion_unittest.cc b/ui/gfx/geometry/quaternion_unittest.cc
new file mode 100644
index 0000000..5c8fa9b
--- /dev/null
+++ b/ui/gfx/geometry/quaternion_unittest.cc
@@ -0,0 +1,169 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#define _USE_MATH_DEFINES  // For VC++ to get M_PI. This has to be first.
+
+#include <cmath>
+
+#include "base/macros.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/geometry/quaternion.h"
+#include "ui/gfx/geometry/vector3d_f.h"
+
+namespace gfx {
+
+namespace {
+
+const double kEpsilon = 1e-7;
+
+void CompareQuaternions(const Quaternion& a, const Quaternion& b) {
+  EXPECT_FLOAT_EQ(a.x(), b.x());
+  EXPECT_FLOAT_EQ(a.y(), b.y());
+  EXPECT_FLOAT_EQ(a.z(), b.z());
+  EXPECT_FLOAT_EQ(a.w(), b.w());
+}
+
+}  // namespace
+
+TEST(QuatTest, DefaultConstruction) {
+  CompareQuaternions(Quaternion(0, 0, 0, 1), Quaternion());
+}
+
+TEST(QuatTest, AxisAngleCommon) {
+  double radians = 0.5;
+  Quaternion q(Vector3dF(1, 0, 0), radians);
+  CompareQuaternions(
+      Quaternion(std::sin(radians / 2), 0, 0, std::cos(radians / 2)), q);
+}
+
+TEST(QuatTest, VectorToVectorRotation) {
+  Quaternion q(Vector3dF(1.0f, 0.0f, 0.0f), Vector3dF(0.0f, 1.0f, 0.0f));
+  Quaternion r(Vector3dF(0.0f, 0.0f, 1.0f), M_PI_2);
+
+  EXPECT_FLOAT_EQ(r.x(), q.x());
+  EXPECT_FLOAT_EQ(r.y(), q.y());
+  EXPECT_FLOAT_EQ(r.z(), q.z());
+  EXPECT_FLOAT_EQ(r.w(), q.w());
+}
+
+TEST(QuatTest, AxisAngleWithZeroLengthAxis) {
+  Quaternion q(Vector3dF(0, 0, 0), 0.5);
+  // If the axis of zero length, we should assume the default values.
+  CompareQuaternions(q, Quaternion());
+}
+
+TEST(QuatTest, Addition) {
+  double values[] = {0, 1, 100};
+  for (size_t i = 0; i < arraysize(values); ++i) {
+    float t = values[i];
+    Quaternion a(t, 2 * t, 3 * t, 4 * t);
+    Quaternion b(5 * t, 4 * t, 3 * t, 2 * t);
+    Quaternion sum = a + b;
+    CompareQuaternions(Quaternion(t, t, t, t) * 6, sum);
+  }
+}
+
+TEST(QuatTest, Multiplication) {
+  struct {
+    Quaternion a;
+    Quaternion b;
+    Quaternion expected;
+  } cases[] = {
+      {Quaternion(1, 0, 0, 0), Quaternion(1, 0, 0, 0), Quaternion(0, 0, 0, -1)},
+      {Quaternion(0, 1, 0, 0), Quaternion(0, 1, 0, 0), Quaternion(0, 0, 0, -1)},
+      {Quaternion(0, 0, 1, 0), Quaternion(0, 0, 1, 0), Quaternion(0, 0, 0, -1)},
+      {Quaternion(0, 0, 0, 1), Quaternion(0, 0, 0, 1), Quaternion(0, 0, 0, 1)},
+      {Quaternion(1, 2, 3, 4), Quaternion(5, 6, 7, 8),
+       Quaternion(24, 48, 48, -6)},
+      {Quaternion(5, 6, 7, 8), Quaternion(1, 2, 3, 4),
+       Quaternion(32, 32, 56, -6)},
+  };
+
+  for (size_t i = 0; i < arraysize(cases); ++i) {
+    Quaternion product = cases[i].a * cases[i].b;
+    CompareQuaternions(cases[i].expected, product);
+  }
+}
+
+TEST(QuatTest, Scaling) {
+  double values[] = {0, 10, 100};
+  for (size_t i = 0; i < arraysize(values); ++i) {
+    double s = values[i];
+    Quaternion q(1, 2, 3, 4);
+    Quaternion expected(s, 2 * s, 3 * s, 4 * s);
+    CompareQuaternions(expected, q * s);
+    CompareQuaternions(expected, s * q);
+    if (s > 0)
+      CompareQuaternions(expected, q / (1 / s));
+  }
+}
+
+TEST(QuatTest, Normalization) {
+  Quaternion q(1, -1, 1, -1);
+  EXPECT_NEAR(q.Length(), 4, kEpsilon);
+
+  q = q.Normalized();
+
+  EXPECT_NEAR(q.Length(), 1, kEpsilon);
+  EXPECT_NEAR(q.x(), 0.5, kEpsilon);
+  EXPECT_NEAR(q.y(), -0.5, kEpsilon);
+  EXPECT_NEAR(q.z(), 0.5, kEpsilon);
+  EXPECT_NEAR(q.w(), -0.5, kEpsilon);
+}
+
+TEST(QuatTest, Lerp) {
+  for (size_t i = 1; i < 100; ++i) {
+    Quaternion a(0, 0, 0, 0);
+    Quaternion b(1, 2, 3, 4);
+    float t = static_cast<float>(i) / 100.0f;
+    Quaternion interpolated = a.Lerp(b, t);
+    double s = 1.0 / sqrt(30.0);
+    CompareQuaternions(Quaternion(1, 2, 3, 4) * s, interpolated);
+  }
+
+  Quaternion a(4, 3, 2, 1);
+  Quaternion b(1, 2, 3, 4);
+  CompareQuaternions(a.Normalized(), a.Lerp(b, 0));
+  CompareQuaternions(b.Normalized(), a.Lerp(b, 1));
+  CompareQuaternions(Quaternion(1, 1, 1, 1).Normalized(), a.Lerp(b, 0.5));
+}
+
+TEST(QuatTest, Slerp) {
+  Vector3dF axis(1, 1, 1);
+  double start_radians = -0.5;
+  double stop_radians = 0.5;
+  Quaternion start(axis, start_radians);
+  Quaternion stop(axis, stop_radians);
+
+  for (size_t i = 0; i < 100; ++i) {
+    float t = static_cast<float>(i) / 100.0f;
+    double radians = (1.0 - t) * start_radians + t * stop_radians;
+    Quaternion expected(axis, radians);
+    Quaternion interpolated = start.Slerp(stop, t);
+    EXPECT_NEAR(expected.x(), interpolated.x(), kEpsilon);
+    EXPECT_NEAR(expected.y(), interpolated.y(), kEpsilon);
+    EXPECT_NEAR(expected.z(), interpolated.z(), kEpsilon);
+    EXPECT_NEAR(expected.w(), interpolated.w(), kEpsilon);
+  }
+}
+
+TEST(QuatTest, SlerpOppositeAngles) {
+  Vector3dF axis(1, 1, 1);
+  double start_radians = -M_PI_2;
+  double stop_radians = M_PI_2;
+  Quaternion start(axis, start_radians);
+  Quaternion stop(axis, stop_radians);
+
+  // When quaternions are pointed in the fully opposite direction, this is
+  // ambiguous, so we rotate as per https://www.w3.org/TR/css-transforms-1/
+  Quaternion expected(axis, 0);
+
+  Quaternion interpolated = start.Slerp(stop, 0.5f);
+  EXPECT_NEAR(expected.x(), interpolated.x(), kEpsilon);
+  EXPECT_NEAR(expected.y(), interpolated.y(), kEpsilon);
+  EXPECT_NEAR(expected.z(), interpolated.z(), kEpsilon);
+  EXPECT_NEAR(expected.w(), interpolated.w(), kEpsilon);
+}
+
+}  // namespace gfx
diff --git a/ui/gfx/geometry/rect_conversions.cc b/ui/gfx/geometry/rect_conversions.cc
new file mode 100644
index 0000000..3a5b2dc
--- /dev/null
+++ b/ui/gfx/geometry/rect_conversions.cc
@@ -0,0 +1,82 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/gfx/geometry/rect_conversions.h"
+
+#include <algorithm>
+#include <cmath>
+
+#include "base/logging.h"
+#include "ui/gfx/geometry/safe_integer_conversions.h"
+
+namespace gfx {
+
+Rect ToEnclosingRect(const RectF& r) {
+  int left = ToFlooredInt(r.x());
+  int right = r.width() ? ToCeiledInt(r.right()) : left;
+  int top = ToFlooredInt(r.y());
+  int bottom = r.height() ? ToCeiledInt(r.bottom()) : top;
+
+  Rect result;
+  result.SetByBounds(left, top, right, bottom);
+  return result;
+}
+
+Rect ToEnclosedRect(const RectF& rect) {
+  Rect result;
+  result.SetByBounds(ToCeiledInt(rect.x()), ToCeiledInt(rect.y()),
+                     ToFlooredInt(rect.right()), ToFlooredInt(rect.bottom()));
+  return result;
+}
+
+Rect ToNearestRect(const RectF& rect) {
+  float float_min_x = rect.x();
+  float float_min_y = rect.y();
+  float float_max_x = rect.right();
+  float float_max_y = rect.bottom();
+
+  int min_x = ToRoundedInt(float_min_x);
+  int min_y = ToRoundedInt(float_min_y);
+  int max_x = ToRoundedInt(float_max_x);
+  int max_y = ToRoundedInt(float_max_y);
+
+  // If these DCHECKs fail, you're using the wrong method, consider using
+  // ToEnclosingRect or ToEnclosedRect instead.
+  DCHECK(std::abs(min_x - float_min_x) < 0.01f);
+  DCHECK(std::abs(min_y - float_min_y) < 0.01f);
+  DCHECK(std::abs(max_x - float_max_x) < 0.01f);
+  DCHECK(std::abs(max_y - float_max_y) < 0.01f);
+
+  Rect result;
+  result.SetByBounds(min_x, min_y, max_x, max_y);
+
+  return result;
+}
+
+bool IsNearestRectWithinDistance(const gfx::RectF& rect, float distance) {
+  float float_min_x = rect.x();
+  float float_min_y = rect.y();
+  float float_max_x = rect.right();
+  float float_max_y = rect.bottom();
+
+  int min_x = ToRoundedInt(float_min_x);
+  int min_y = ToRoundedInt(float_min_y);
+  int max_x = ToRoundedInt(float_max_x);
+  int max_y = ToRoundedInt(float_max_y);
+
+  return
+      (std::abs(min_x - float_min_x) < distance) &&
+      (std::abs(min_y - float_min_y) < distance) &&
+      (std::abs(max_x - float_max_x) < distance) &&
+      (std::abs(max_y - float_max_y) < distance);
+}
+
+Rect ToFlooredRectDeprecated(const RectF& rect) {
+  return Rect(ToFlooredInt(rect.x()),
+              ToFlooredInt(rect.y()),
+              ToFlooredInt(rect.width()),
+              ToFlooredInt(rect.height()));
+}
+
+}  // namespace gfx
diff --git a/ui/gfx/geometry/rect_conversions.h b/ui/gfx/geometry/rect_conversions.h
new file mode 100644
index 0000000..617074a
--- /dev/null
+++ b/ui/gfx/geometry/rect_conversions.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_GFX_GEOMETRY_RECT_CONVERSIONS_H_
+#define UI_GFX_GEOMETRY_RECT_CONVERSIONS_H_
+
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/rect_f.h"
+
+namespace gfx {
+
+// Returns the smallest Rect that encloses the given RectF.
+GFX_EXPORT Rect ToEnclosingRect(const RectF& rect);
+
+// Returns the largest Rect that is enclosed by the given RectF.
+GFX_EXPORT Rect ToEnclosedRect(const RectF& rect);
+
+// Returns the Rect after snapping the corners of the RectF to an integer grid.
+// This should only be used when the RectF you provide is expected to be an
+// integer rect with floating point error. If it is an arbitrary RectF, then
+// you should use a different method.
+GFX_EXPORT Rect ToNearestRect(const RectF& rect);
+
+// Returns true if the Rect produced after snapping the corners of the RectF
+// to an integer grid is withing |distance|.
+GFX_EXPORT bool IsNearestRectWithinDistance(
+    const gfx::RectF& rect, float distance);
+
+// Returns a Rect obtained by flooring the values of the given RectF.
+// Please prefer the previous two functions in new code.
+GFX_EXPORT Rect ToFlooredRectDeprecated(const RectF& rect);
+
+}  // namespace gfx
+
+#endif  // UI_GFX_GEOMETRY_RECT_CONVERSIONS_H_
diff --git a/ui/gfx/geometry/rect_unittest.cc b/ui/gfx/geometry/rect_unittest.cc
new file mode 100644
index 0000000..aaa533b
--- /dev/null
+++ b/ui/gfx/geometry/rect_unittest.cc
@@ -0,0 +1,1141 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <limits>
+
+#include <stddef.h>
+
+#include "base/macros.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/rect_conversions.h"
+#include "ui/gfx/test/gfx_util.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+
+namespace gfx {
+
+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},
+  #if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
+    {0, 0, -10, -10, 0, 0, false},
+  #endif
+  };
+  for (size_t i = 0; i < arraysize(contains_cases); ++i) {
+    const ContainsCase& value = contains_cases[i];
+    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, 0, 0, -10, -10, 20, 20, false },
+    { -10, 0, 0, 20, 0, -10, 20, 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) {
+    Rect r1(tests[i].x1, tests[i].y1, tests[i].w1, tests[i].h1);
+    Rect r2(tests[i].x2, tests[i].y2, tests[i].w2, tests[i].h2);
+    EXPECT_EQ(tests[i].intersects, r1.Intersects(r2));
+    EXPECT_EQ(tests[i].intersects, r2.Intersects(r1));
+  }
+}
+
+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) {
+    Rect r1(tests[i].x1, tests[i].y1, tests[i].w1, tests[i].h1);
+    Rect r2(tests[i].x2, tests[i].y2, tests[i].w2, tests[i].h2);
+    Rect r3(tests[i].x3, tests[i].y3, tests[i].w3, tests[i].h3);
+    Rect ir = IntersectRects(r1, 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 Test {
+    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) {
+    Rect r1(tests[i].x1, tests[i].y1, tests[i].w1, tests[i].h1);
+    Rect r2(tests[i].x2, tests[i].y2, tests[i].w2, tests[i].h2);
+    Rect r3(tests[i].x3, tests[i].y3, tests[i].w3, tests[i].h3);
+    Rect u = UnionRects(r1, 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(Rect(0, 0, 0, 0) == Rect(0, 0, 0, 0));
+  ASSERT_TRUE(Rect(1, 2, 3, 4) == Rect(1, 2, 3, 4));
+  ASSERT_FALSE(Rect(0, 0, 0, 0) == Rect(0, 0, 0, 1));
+  ASSERT_FALSE(Rect(0, 0, 0, 0) == Rect(0, 0, 1, 0));
+  ASSERT_FALSE(Rect(0, 0, 0, 0) == Rect(0, 1, 0, 0));
+  ASSERT_FALSE(Rect(0, 0, 0, 0) == Rect(1, 0, 0, 0));
+}
+
+TEST(RectTest, AdjustToFit) {
+  static const struct Test {
+    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) {
+    Rect r1(tests[i].x1, tests[i].y1, tests[i].w1, tests[i].h1);
+    Rect r2(tests[i].x2, tests[i].y2, tests[i].w2, tests[i].h2);
+    Rect r3(tests[i].x3, tests[i].y3, tests[i].w3, tests[i].h3);
+    Rect u = r1;
+    u.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(RectTest, Subtract) {
+  Rect result;
+
+  // Matching
+  result = Rect(10, 10, 20, 20);
+  result.Subtract(Rect(10, 10, 20, 20));
+  EXPECT_EQ(Rect(0, 0, 0, 0), result);
+
+  // Contains
+  result = Rect(10, 10, 20, 20);
+  result.Subtract(Rect(5, 5, 30, 30));
+  EXPECT_EQ(Rect(0, 0, 0, 0), result);
+
+  // No intersection
+  result = Rect(10, 10, 20, 20);
+  result.Subtract(Rect(30, 30, 30, 30));
+  EXPECT_EQ(Rect(10, 10, 20, 20), result);
+
+  // Not a complete intersection in either direction
+  result = Rect(10, 10, 20, 20);
+  result.Subtract(Rect(15, 15, 20, 20));
+  EXPECT_EQ(Rect(10, 10, 20, 20), result);
+
+  // Complete intersection in the x-direction, top edge is fully covered.
+  result = Rect(10, 10, 20, 20);
+  result.Subtract(Rect(10, 15, 20, 20));
+  EXPECT_EQ(Rect(10, 10, 20, 5), result);
+
+  // Complete intersection in the x-direction, top edge is fully covered.
+  result = Rect(10, 10, 20, 20);
+  result.Subtract(Rect(5, 15, 30, 20));
+  EXPECT_EQ(Rect(10, 10, 20, 5), result);
+
+  // Complete intersection in the x-direction, bottom edge is fully covered.
+  result = Rect(10, 10, 20, 20);
+  result.Subtract(Rect(5, 5, 30, 20));
+  EXPECT_EQ(Rect(10, 25, 20, 5), result);
+
+  // Complete intersection in the x-direction, none of the edges is fully
+  // covered.
+  result = Rect(10, 10, 20, 20);
+  result.Subtract(Rect(5, 15, 30, 1));
+  EXPECT_EQ(Rect(10, 10, 20, 20), result);
+
+  // Complete intersection in the y-direction, left edge is fully covered.
+  result = Rect(10, 10, 20, 20);
+  result.Subtract(Rect(10, 10, 10, 30));
+  EXPECT_EQ(Rect(20, 10, 10, 20), result);
+
+  // Complete intersection in the y-direction, left edge is fully covered.
+  result = Rect(10, 10, 20, 20);
+  result.Subtract(Rect(5, 5, 20, 30));
+  EXPECT_EQ(Rect(25, 10, 5, 20), result);
+
+  // Complete intersection in the y-direction, right edge is fully covered.
+  result = Rect(10, 10, 20, 20);
+  result.Subtract(Rect(20, 5, 20, 30));
+  EXPECT_EQ(Rect(10, 10, 10, 20), result);
+
+  // Complete intersection in the y-direction, none of the edges is fully
+  // covered.
+  result = Rect(10, 10, 20, 20);
+  result.Subtract(Rect(15, 5, 1, 30));
+  EXPECT_EQ(Rect(10, 10, 20, 20), result);
+}
+
+TEST(RectTest, IsEmpty) {
+  EXPECT_TRUE(Rect(0, 0, 0, 0).IsEmpty());
+  EXPECT_TRUE(Rect(0, 0, 0, 0).size().IsEmpty());
+  EXPECT_TRUE(Rect(0, 0, 10, 0).IsEmpty());
+  EXPECT_TRUE(Rect(0, 0, 10, 0).size().IsEmpty());
+  EXPECT_TRUE(Rect(0, 0, 0, 10).IsEmpty());
+  EXPECT_TRUE(Rect(0, 0, 0, 10).size().IsEmpty());
+  EXPECT_FALSE(Rect(0, 0, 10, 10).IsEmpty());
+  EXPECT_FALSE(Rect(0, 0, 10, 10).size().IsEmpty());
+}
+
+TEST(RectTest, SplitVertically) {
+  Rect left_half, right_half;
+
+  // Splitting when origin is (0, 0).
+  Rect(0, 0, 20, 20).SplitVertically(&left_half, &right_half);
+  EXPECT_TRUE(left_half == Rect(0, 0, 10, 20));
+  EXPECT_TRUE(right_half == Rect(10, 0, 10, 20));
+
+  // Splitting when origin is arbitrary.
+  Rect(10, 10, 20, 10).SplitVertically(&left_half, &right_half);
+  EXPECT_TRUE(left_half == Rect(10, 10, 10, 10));
+  EXPECT_TRUE(right_half == Rect(20, 10, 10, 10));
+
+  // Splitting a rectangle of zero width.
+  Rect(10, 10, 0, 10).SplitVertically(&left_half, &right_half);
+  EXPECT_TRUE(left_half == Rect(10, 10, 0, 10));
+  EXPECT_TRUE(right_half == Rect(10, 10, 0, 10));
+
+  // Splitting a rectangle of odd width.
+  Rect(10, 10, 5, 10).SplitVertically(&left_half, &right_half);
+  EXPECT_TRUE(left_half == Rect(10, 10, 2, 10));
+  EXPECT_TRUE(right_half == Rect(12, 10, 3, 10));
+}
+
+TEST(RectTest, CenterPoint) {
+  Point center;
+
+  // When origin is (0, 0).
+  center = Rect(0, 0, 20, 20).CenterPoint();
+  EXPECT_TRUE(center == Point(10, 10));
+
+  // When origin is even.
+  center = Rect(10, 10, 20, 20).CenterPoint();
+  EXPECT_TRUE(center == Point(20, 20));
+
+  // When origin is odd.
+  center = Rect(11, 11, 20, 20).CenterPoint();
+  EXPECT_TRUE(center == Point(21, 21));
+
+  // When 0 width or height.
+  center = Rect(10, 10, 0, 20).CenterPoint();
+  EXPECT_TRUE(center == Point(10, 20));
+  center = Rect(10, 10, 20, 0).CenterPoint();
+  EXPECT_TRUE(center == Point(20, 10));
+
+  // When an odd size.
+  center = Rect(10, 10, 21, 21).CenterPoint();
+  EXPECT_TRUE(center == Point(20, 20));
+
+  // When an odd size and position.
+  center = Rect(11, 11, 21, 21).CenterPoint();
+  EXPECT_TRUE(center == Point(21, 21));
+}
+
+TEST(RectTest, CenterPointF) {
+  PointF center;
+
+  // When origin is (0, 0).
+  center = RectF(0, 0, 20, 20).CenterPoint();
+  EXPECT_TRUE(center == PointF(10, 10));
+
+  // When origin is even.
+  center = RectF(10, 10, 20, 20).CenterPoint();
+  EXPECT_TRUE(center == PointF(20, 20));
+
+  // When origin is odd.
+  center = RectF(11, 11, 20, 20).CenterPoint();
+  EXPECT_TRUE(center == PointF(21, 21));
+
+  // When 0 width or height.
+  center = RectF(10, 10, 0, 20).CenterPoint();
+  EXPECT_TRUE(center == PointF(10, 20));
+  center = RectF(10, 10, 20, 0).CenterPoint();
+  EXPECT_TRUE(center == PointF(20, 10));
+
+  // When an odd size.
+  center = RectF(10, 10, 21, 21).CenterPoint();
+  EXPECT_TRUE(center == PointF(20.5f, 20.5f));
+
+  // When an odd size and position.
+  center = RectF(11, 11, 21, 21).CenterPoint();
+  EXPECT_TRUE(center == PointF(21.5f, 21.5f));
+}
+
+TEST(RectTest, SharesEdgeWith) {
+  Rect r(2, 3, 4, 5);
+
+  // Must be non-overlapping
+  EXPECT_FALSE(r.SharesEdgeWith(r));
+
+  Rect just_above(2, 1, 4, 2);
+  Rect just_below(2, 8, 4, 2);
+  Rect just_left(0, 3, 2, 5);
+  Rect just_right(6, 3, 2, 5);
+
+  EXPECT_TRUE(r.SharesEdgeWith(just_above));
+  EXPECT_TRUE(r.SharesEdgeWith(just_below));
+  EXPECT_TRUE(r.SharesEdgeWith(just_left));
+  EXPECT_TRUE(r.SharesEdgeWith(just_right));
+
+  // Wrong placement
+  Rect same_height_no_edge(0, 0, 1, 5);
+  Rect same_width_no_edge(0, 0, 4, 1);
+
+  EXPECT_FALSE(r.SharesEdgeWith(same_height_no_edge));
+  EXPECT_FALSE(r.SharesEdgeWith(same_width_no_edge));
+
+  Rect just_above_no_edge(2, 1, 5, 2);  // too wide
+  Rect just_below_no_edge(2, 8, 3, 2);  // too narrow
+  Rect just_left_no_edge(0, 3, 2, 6);   // too tall
+  Rect just_right_no_edge(6, 3, 2, 4);  // too short
+
+  EXPECT_FALSE(r.SharesEdgeWith(just_above_no_edge));
+  EXPECT_FALSE(r.SharesEdgeWith(just_below_no_edge));
+  EXPECT_FALSE(r.SharesEdgeWith(just_left_no_edge));
+  EXPECT_FALSE(r.SharesEdgeWith(just_right_no_edge));
+}
+
+// Similar to EXPECT_FLOAT_EQ, but lets NaN equal NaN
+#define EXPECT_FLOAT_AND_NAN_EQ(a, b) \
+  { if (a == a || b == b) { EXPECT_FLOAT_EQ(a, b); } }
+
+TEST(RectTest, ScaleRect) {
+  static const struct Test {
+    int x1;  // source
+    int y1;
+    int w1;
+    int h1;
+    float scale;
+    float x2;  // target
+    float y2;
+    float w2;
+    float h2;
+  } tests[] = {
+    { 3, 3, 3, 3,
+      1.5f,
+      4.5f, 4.5f, 4.5f, 4.5f },
+    { 3, 3, 3, 3,
+      0.0f,
+      0.0f, 0.0f, 0.0f, 0.0f },
+    { 3, 3, 3, 3,
+      std::numeric_limits<float>::quiet_NaN(),
+      std::numeric_limits<float>::quiet_NaN(),
+      std::numeric_limits<float>::quiet_NaN(),
+      std::numeric_limits<float>::quiet_NaN(),
+      std::numeric_limits<float>::quiet_NaN() },
+    { 3, 3, 3, 3,
+      std::numeric_limits<float>::max(),
+      std::numeric_limits<float>::max(),
+      std::numeric_limits<float>::max(),
+      std::numeric_limits<float>::max(),
+      std::numeric_limits<float>::max() }
+  };
+
+  for (size_t i = 0; i < arraysize(tests); ++i) {
+    RectF r1(tests[i].x1, tests[i].y1, tests[i].w1, tests[i].h1);
+    RectF r2(tests[i].x2, tests[i].y2, tests[i].w2, tests[i].h2);
+
+    RectF scaled = ScaleRect(r1, tests[i].scale);
+    EXPECT_FLOAT_AND_NAN_EQ(r2.x(), scaled.x());
+    EXPECT_FLOAT_AND_NAN_EQ(r2.y(), scaled.y());
+    EXPECT_FLOAT_AND_NAN_EQ(r2.width(), scaled.width());
+    EXPECT_FLOAT_AND_NAN_EQ(r2.height(), scaled.height());
+  }
+}
+
+TEST(RectTest, ToEnclosedRect) {
+  static const int max_int = std::numeric_limits<int>::max();
+  static const int min_int = std::numeric_limits<int>::min();
+  static const float max_float = std::numeric_limits<float>::max();
+  static const float max_int_f = static_cast<float>(max_int);
+  static const float min_int_f = static_cast<float>(min_int);
+
+  static const struct Test {
+    struct {
+      float x;
+      float y;
+      float width;
+      float height;
+    } in;
+    struct {
+      int x;
+      int y;
+      int width;
+      int height;
+    } expected;
+  } tests[] = {
+      {{0.0f, 0.0f, 0.0f, 0.0f}, {0, 0, 0, 0}},
+      {{-1.5f, -1.5f, 3.0f, 3.0f}, {-1, -1, 2, 2}},
+      {{-1.5f, -1.5f, 3.5f, 3.5f}, {-1, -1, 3, 3}},
+      {{max_float, max_float, 2.0f, 2.0f}, {max_int, max_int, 0, 0}},
+      {{0.0f, 0.0f, max_float, max_float}, {0, 0, max_int, max_int}},
+      {{20000.5f, 20000.5f, 0.5f, 0.5f}, {20001, 20001, 0, 0}},
+      {{max_int_f, max_int_f, max_int_f, max_int_f}, {max_int, max_int, 0, 0}}};
+
+  for (size_t i = 0; i < arraysize(tests); ++i) {
+    RectF source(tests[i].in.x, tests[i].in.y, tests[i].in.width,
+                 tests[i].in.height);
+    Rect enclosed = ToEnclosedRect(source);
+
+    EXPECT_EQ(tests[i].expected.x, enclosed.x());
+    EXPECT_EQ(tests[i].expected.y, enclosed.y());
+    EXPECT_EQ(tests[i].expected.width, enclosed.width());
+    EXPECT_EQ(tests[i].expected.height, enclosed.height());
+  }
+
+  {
+    RectF source(min_int_f, min_int_f, max_int_f * 3.f, max_int_f * 3.f);
+    Rect enclosed = ToEnclosedRect(source);
+
+    // That rect can't be represented, but it should be big.
+    EXPECT_EQ(max_int, enclosed.width());
+    EXPECT_EQ(max_int, enclosed.height());
+    // It should include some axis near the global origin.
+    EXPECT_GT(1, enclosed.x());
+    EXPECT_GT(1, enclosed.y());
+    // And it should not cause computation issues for itself.
+    EXPECT_LT(0, enclosed.right());
+    EXPECT_LT(0, enclosed.bottom());
+  }
+}
+
+TEST(RectTest, ToEnclosingRect) {
+  static const int max_int = std::numeric_limits<int>::max();
+  static const int min_int = std::numeric_limits<int>::min();
+  static const float max_float = std::numeric_limits<float>::max();
+  static const float epsilon_float = std::numeric_limits<float>::epsilon();
+  static const float max_int_f = static_cast<float>(max_int);
+  static const float min_int_f = static_cast<float>(min_int);
+  static const struct Test {
+    struct {
+      float x;
+      float y;
+      float width;
+      float height;
+    } in;
+    struct {
+      int x;
+      int y;
+      int width;
+      int height;
+    } expected;
+  } tests[] = {
+      {{0.0f, 0.0f, 0.0f, 0.0f}, {0, 0, 0, 0}},
+      {{5.5f, 5.5f, 0.0f, 0.0f}, {5, 5, 0, 0}},
+      {{3.5f, 2.5f, epsilon_float, -0.0f}, {3, 2, 0, 0}},
+      {{3.5f, 2.5f, 0.f, 0.001f}, {3, 2, 0, 1}},
+      {{-1.5f, -1.5f, 3.0f, 3.0f}, {-2, -2, 4, 4}},
+      {{-1.5f, -1.5f, 3.5f, 3.5f}, {-2, -2, 4, 4}},
+      {{max_float, max_float, 2.0f, 2.0f}, {max_int, max_int, 0, 0}},
+      {{0.0f, 0.0f, max_float, max_float}, {0, 0, max_int, max_int}},
+      {{20000.5f, 20000.5f, 0.5f, 0.5f}, {20000, 20000, 1, 1}},
+      {{max_int_f, max_int_f, max_int_f, max_int_f}, {max_int, max_int, 0, 0}},
+      {{-0.5f, -0.5f, 22777712.f, 1.f}, {-1, -1, 22777713, 2}}};
+
+  for (size_t i = 0; i < arraysize(tests); ++i) {
+    RectF source(tests[i].in.x, tests[i].in.y, tests[i].in.width,
+                 tests[i].in.height);
+
+    Rect enclosing = ToEnclosingRect(source);
+    EXPECT_EQ(tests[i].expected.x, enclosing.x());
+    EXPECT_EQ(tests[i].expected.y, enclosing.y());
+    EXPECT_EQ(tests[i].expected.width, enclosing.width());
+    EXPECT_EQ(tests[i].expected.height, enclosing.height());
+  }
+
+  {
+    RectF source(min_int_f, min_int_f, max_int_f * 3.f, max_int_f * 3.f);
+    Rect enclosing = ToEnclosingRect(source);
+
+    // That rect can't be represented, but it should be big.
+    EXPECT_EQ(max_int, enclosing.width());
+    EXPECT_EQ(max_int, enclosing.height());
+    // It should include some axis near the global origin.
+    EXPECT_GT(1, enclosing.x());
+    EXPECT_GT(1, enclosing.y());
+    // And it should cause computation issues for itself.
+    EXPECT_LT(0, enclosing.right());
+    EXPECT_LT(0, enclosing.bottom());
+  }
+}
+
+TEST(RectTest, ToNearestRect) {
+  Rect rect;
+  EXPECT_EQ(rect, ToNearestRect(RectF(rect)));
+
+  rect = Rect(-1, -1, 3, 3);
+  EXPECT_EQ(rect, ToNearestRect(RectF(rect)));
+
+  RectF rectf(-1.00001f, -0.999999f, 3.0000001f, 2.999999f);
+  EXPECT_EQ(rect, ToNearestRect(rectf));
+}
+
+TEST(RectTest, ToFlooredRect) {
+  static const struct Test {
+    float x1; // source
+    float y1;
+    float w1;
+    float h1;
+    int x2; // target
+    int y2;
+    int w2;
+    int h2;
+  } tests [] = {
+    { 0.0f, 0.0f, 0.0f, 0.0f,
+      0, 0, 0, 0 },
+    { -1.5f, -1.5f, 3.0f, 3.0f,
+      -2, -2, 3, 3 },
+    { -1.5f, -1.5f, 3.5f, 3.5f,
+      -2, -2, 3, 3 },
+    { 20000.5f, 20000.5f, 0.5f, 0.5f,
+      20000, 20000, 0, 0 },
+  };
+
+  for (size_t i = 0; i < arraysize(tests); ++i) {
+    RectF r1(tests[i].x1, tests[i].y1, tests[i].w1, tests[i].h1);
+    Rect r2(tests[i].x2, tests[i].y2, tests[i].w2, tests[i].h2);
+
+    Rect floored = ToFlooredRectDeprecated(r1);
+    EXPECT_FLOAT_EQ(r2.x(), floored.x());
+    EXPECT_FLOAT_EQ(r2.y(), floored.y());
+    EXPECT_FLOAT_EQ(r2.width(), floored.width());
+    EXPECT_FLOAT_EQ(r2.height(), floored.height());
+  }
+}
+
+TEST(RectTest, ScaleToEnclosedRect) {
+  static const struct Test {
+    Rect input_rect;
+    float input_scale;
+    Rect expected_rect;
+  } tests[] = {
+    {
+      Rect(),
+      5.f,
+      Rect(),
+    }, {
+      Rect(1, 1, 1, 1),
+      5.f,
+      Rect(5, 5, 5, 5),
+    }, {
+      Rect(-1, -1, 0, 0),
+      5.f,
+      Rect(-5, -5, 0, 0),
+    }, {
+      Rect(1, -1, 0, 1),
+      5.f,
+      Rect(5, -5, 0, 5),
+    }, {
+      Rect(-1, 1, 1, 0),
+      5.f,
+      Rect(-5, 5, 5, 0),
+    }, {
+      Rect(1, 2, 3, 4),
+      1.5f,
+      Rect(2, 3, 4, 6),
+    }, {
+      Rect(-1, -2, 0, 0),
+      1.5f,
+      Rect(-1, -3, 0, 0),
+    }
+  };
+
+  for (size_t i = 0; i < arraysize(tests); ++i) {
+    Rect result = ScaleToEnclosedRect(tests[i].input_rect,
+                                      tests[i].input_scale);
+    EXPECT_EQ(tests[i].expected_rect, result);
+  }
+}
+
+TEST(RectTest, ScaleToEnclosingRect) {
+  static const struct Test {
+    Rect input_rect;
+    float input_scale;
+    Rect expected_rect;
+  } tests[] = {
+    {
+      Rect(),
+      5.f,
+      Rect(),
+    }, {
+      Rect(1, 1, 1, 1),
+      5.f,
+      Rect(5, 5, 5, 5),
+    }, {
+      Rect(-1, -1, 0, 0),
+      5.f,
+      Rect(-5, -5, 0, 0),
+    }, {
+      Rect(1, -1, 0, 1),
+      5.f,
+      Rect(5, -5, 0, 5),
+    }, {
+      Rect(-1, 1, 1, 0),
+      5.f,
+      Rect(-5, 5, 5, 0),
+    }, {
+      Rect(1, 2, 3, 4),
+      1.5f,
+      Rect(1, 3, 5, 6),
+    }, {
+      Rect(-1, -2, 0, 0),
+      1.5f,
+      Rect(-2, -3, 0, 0),
+    }
+  };
+
+  for (size_t i = 0; i < arraysize(tests); ++i) {
+    Rect result =
+        ScaleToEnclosingRect(tests[i].input_rect, tests[i].input_scale);
+    EXPECT_EQ(tests[i].expected_rect, result);
+    Rect result_safe =
+        ScaleToEnclosingRectSafe(tests[i].input_rect, tests[i].input_scale);
+    EXPECT_EQ(tests[i].expected_rect, result_safe);
+  }
+}
+
+#if defined(OS_WIN)
+TEST(RectTest, ConstructAndAssign) {
+  const RECT rect_1 = { 0, 0, 10, 10 };
+  const RECT rect_2 = { 0, 0, -10, -10 };
+  Rect test1(rect_1);
+  Rect test2(rect_2);
+}
+#endif
+
+TEST(RectTest, ToRectF) {
+  // Check that explicit conversion from integer to float compiles.
+  Rect a(10, 20, 30, 40);
+  RectF b(10, 20, 30, 40);
+
+  RectF c = RectF(a);
+  EXPECT_EQ(b, c);
+}
+
+TEST(RectTest, BoundingRect) {
+  struct {
+    Point a;
+    Point b;
+    Rect expected;
+  } int_tests[] = {
+    // If point B dominates A, then A should be the origin.
+    { Point(4, 6), Point(4, 6), Rect(4, 6, 0, 0) },
+    { Point(4, 6), Point(8, 6), Rect(4, 6, 4, 0) },
+    { Point(4, 6), Point(4, 9), Rect(4, 6, 0, 3) },
+    { Point(4, 6), Point(8, 9), Rect(4, 6, 4, 3) },
+    // If point A dominates B, then B should be the origin.
+    { Point(4, 6), Point(4, 6), Rect(4, 6, 0, 0) },
+    { Point(8, 6), Point(4, 6), Rect(4, 6, 4, 0) },
+    { Point(4, 9), Point(4, 6), Rect(4, 6, 0, 3) },
+    { Point(8, 9), Point(4, 6), Rect(4, 6, 4, 3) },
+    // If neither point dominates, then the origin is a combination of the two.
+    { Point(4, 6), Point(6, 4), Rect(4, 4, 2, 2) },
+    { Point(-4, -6), Point(-6, -4), Rect(-6, -6, 2, 2) },
+    { Point(-4, 6), Point(6, -4), Rect(-4, -4, 10, 10) },
+  };
+
+  for (size_t i = 0; i < arraysize(int_tests); ++i) {
+    Rect actual = BoundingRect(int_tests[i].a, int_tests[i].b);
+    EXPECT_EQ(int_tests[i].expected, actual);
+  }
+
+  struct {
+    PointF a;
+    PointF b;
+    RectF expected;
+  } float_tests[] = {
+    // If point B dominates A, then A should be the origin.
+    { PointF(4.2f, 6.8f), PointF(4.2f, 6.8f),
+      RectF(4.2f, 6.8f, 0, 0) },
+    { PointF(4.2f, 6.8f), PointF(8.5f, 6.8f),
+      RectF(4.2f, 6.8f, 4.3f, 0) },
+    { PointF(4.2f, 6.8f), PointF(4.2f, 9.3f),
+      RectF(4.2f, 6.8f, 0, 2.5f) },
+    { PointF(4.2f, 6.8f), PointF(8.5f, 9.3f),
+      RectF(4.2f, 6.8f, 4.3f, 2.5f) },
+    // If point A dominates B, then B should be the origin.
+    { PointF(4.2f, 6.8f), PointF(4.2f, 6.8f),
+      RectF(4.2f, 6.8f, 0, 0) },
+    { PointF(8.5f, 6.8f), PointF(4.2f, 6.8f),
+      RectF(4.2f, 6.8f, 4.3f, 0) },
+    { PointF(4.2f, 9.3f), PointF(4.2f, 6.8f),
+      RectF(4.2f, 6.8f, 0, 2.5f) },
+    { PointF(8.5f, 9.3f), PointF(4.2f, 6.8f),
+      RectF(4.2f, 6.8f, 4.3f, 2.5f) },
+    // If neither point dominates, then the origin is a combination of the two.
+    { PointF(4.2f, 6.8f), PointF(6.8f, 4.2f),
+      RectF(4.2f, 4.2f, 2.6f, 2.6f) },
+    { PointF(-4.2f, -6.8f), PointF(-6.8f, -4.2f),
+      RectF(-6.8f, -6.8f, 2.6f, 2.6f) },
+    { PointF(-4.2f, 6.8f), PointF(6.8f, -4.2f),
+      RectF(-4.2f, -4.2f, 11.0f, 11.0f) }
+  };
+
+  for (size_t i = 0; i < arraysize(float_tests); ++i) {
+    RectF actual = BoundingRect(float_tests[i].a, float_tests[i].b);
+    EXPECT_RECTF_EQ(float_tests[i].expected, actual);
+  }
+}
+
+TEST(RectTest, IsExpressibleAsRect) {
+  EXPECT_TRUE(RectF().IsExpressibleAsRect());
+
+  float min = std::numeric_limits<int>::min();
+  float max = std::numeric_limits<int>::max();
+  float infinity = std::numeric_limits<float>::infinity();
+
+  EXPECT_TRUE(RectF(
+      min + 200, min + 200, max - 200, max - 200).IsExpressibleAsRect());
+  EXPECT_FALSE(RectF(
+      min - 200, min + 200, max + 200, max + 200).IsExpressibleAsRect());
+  EXPECT_FALSE(RectF(
+      min + 200 , min - 200, max + 200, max + 200).IsExpressibleAsRect());
+  EXPECT_FALSE(RectF(
+      min + 200, min + 200, max + 200, max - 200).IsExpressibleAsRect());
+  EXPECT_FALSE(RectF(
+      min + 200, min + 200, max - 200, max + 200).IsExpressibleAsRect());
+
+  EXPECT_TRUE(RectF(0, 0, max - 200, max - 200).IsExpressibleAsRect());
+  EXPECT_FALSE(RectF(200, 0, max + 200, max - 200).IsExpressibleAsRect());
+  EXPECT_FALSE(RectF(0, 200, max - 200, max + 200).IsExpressibleAsRect());
+  EXPECT_FALSE(RectF(0, 0, max + 200, max - 200).IsExpressibleAsRect());
+  EXPECT_FALSE(RectF(0, 0, max - 200, max + 200).IsExpressibleAsRect());
+
+  EXPECT_FALSE(RectF(infinity, 0, 1, 1).IsExpressibleAsRect());
+  EXPECT_FALSE(RectF(0, infinity, 1, 1).IsExpressibleAsRect());
+  EXPECT_FALSE(RectF(0, 0, infinity, 1).IsExpressibleAsRect());
+  EXPECT_FALSE(RectF(0, 0, 1, infinity).IsExpressibleAsRect());
+}
+
+TEST(RectTest, Offset) {
+  Rect i(1, 2, 3, 4);
+
+  EXPECT_EQ(Rect(2, 1, 3, 4), (i + Vector2d(1, -1)));
+  EXPECT_EQ(Rect(2, 1, 3, 4), (Vector2d(1, -1) + i));
+  i += Vector2d(1, -1);
+  EXPECT_EQ(Rect(2, 1, 3, 4), i);
+  EXPECT_EQ(Rect(1, 2, 3, 4), (i - Vector2d(1, -1)));
+  i -= Vector2d(1, -1);
+  EXPECT_EQ(Rect(1, 2, 3, 4), i);
+
+  RectF f(1.1f, 2.2f, 3.3f, 4.4f);
+  EXPECT_EQ(RectF(2.2f, 1.1f, 3.3f, 4.4f), (f + Vector2dF(1.1f, -1.1f)));
+  EXPECT_EQ(RectF(2.2f, 1.1f, 3.3f, 4.4f), (Vector2dF(1.1f, -1.1f) + f));
+  f += Vector2dF(1.1f, -1.1f);
+  EXPECT_EQ(RectF(2.2f, 1.1f, 3.3f, 4.4f), f);
+  EXPECT_EQ(RectF(1.1f, 2.2f, 3.3f, 4.4f), (f - Vector2dF(1.1f, -1.1f)));
+  f -= Vector2dF(1.1f, -1.1f);
+  EXPECT_EQ(RectF(1.1f, 2.2f, 3.3f, 4.4f), f);
+}
+
+TEST(RectTest, Corners) {
+  Rect i(1, 2, 3, 4);
+  RectF f(1.1f, 2.1f, 3.1f, 4.1f);
+
+  EXPECT_EQ(Point(1, 2), i.origin());
+  EXPECT_EQ(Point(4, 2), i.top_right());
+  EXPECT_EQ(Point(1, 6), i.bottom_left());
+  EXPECT_EQ(Point(4, 6), i.bottom_right());
+
+  EXPECT_EQ(PointF(1.1f, 2.1f), f.origin());
+  EXPECT_EQ(PointF(4.2f, 2.1f), f.top_right());
+  EXPECT_EQ(PointF(1.1f, 6.2f), f.bottom_left());
+  EXPECT_EQ(PointF(4.2f, 6.2f), f.bottom_right());
+}
+
+TEST(RectTest, ManhattanDistanceToPoint) {
+  Rect i(1, 2, 3, 4);
+  EXPECT_EQ(0, i.ManhattanDistanceToPoint(Point(1, 2)));
+  EXPECT_EQ(0, i.ManhattanDistanceToPoint(Point(4, 6)));
+  EXPECT_EQ(0, i.ManhattanDistanceToPoint(Point(2, 4)));
+  EXPECT_EQ(3, i.ManhattanDistanceToPoint(Point(0, 0)));
+  EXPECT_EQ(2, i.ManhattanDistanceToPoint(Point(2, 0)));
+  EXPECT_EQ(3, i.ManhattanDistanceToPoint(Point(5, 0)));
+  EXPECT_EQ(1, i.ManhattanDistanceToPoint(Point(5, 4)));
+  EXPECT_EQ(3, i.ManhattanDistanceToPoint(Point(5, 8)));
+  EXPECT_EQ(2, i.ManhattanDistanceToPoint(Point(3, 8)));
+  EXPECT_EQ(2, i.ManhattanDistanceToPoint(Point(0, 7)));
+  EXPECT_EQ(1, i.ManhattanDistanceToPoint(Point(0, 3)));
+
+  RectF f(1.1f, 2.1f, 3.1f, 4.1f);
+  EXPECT_FLOAT_EQ(0.f, f.ManhattanDistanceToPoint(PointF(1.1f, 2.1f)));
+  EXPECT_FLOAT_EQ(0.f, f.ManhattanDistanceToPoint(PointF(4.2f, 6.f)));
+  EXPECT_FLOAT_EQ(0.f, f.ManhattanDistanceToPoint(PointF(2.f, 4.f)));
+  EXPECT_FLOAT_EQ(3.2f, f.ManhattanDistanceToPoint(PointF(0.f, 0.f)));
+  EXPECT_FLOAT_EQ(2.1f, f.ManhattanDistanceToPoint(PointF(2.f, 0.f)));
+  EXPECT_FLOAT_EQ(2.9f, f.ManhattanDistanceToPoint(PointF(5.f, 0.f)));
+  EXPECT_FLOAT_EQ(.8f, f.ManhattanDistanceToPoint(PointF(5.f, 4.f)));
+  EXPECT_FLOAT_EQ(2.6f, f.ManhattanDistanceToPoint(PointF(5.f, 8.f)));
+  EXPECT_FLOAT_EQ(1.8f, f.ManhattanDistanceToPoint(PointF(3.f, 8.f)));
+  EXPECT_FLOAT_EQ(1.9f, f.ManhattanDistanceToPoint(PointF(0.f, 7.f)));
+  EXPECT_FLOAT_EQ(1.1f, f.ManhattanDistanceToPoint(PointF(0.f, 3.f)));
+}
+
+TEST(RectTest, ManhattanInternalDistance) {
+  Rect i(0, 0, 400, 400);
+  EXPECT_EQ(0, i.ManhattanInternalDistance(gfx::Rect(-1, 0, 2, 1)));
+  EXPECT_EQ(1, i.ManhattanInternalDistance(gfx::Rect(400, 0, 1, 400)));
+  EXPECT_EQ(2, i.ManhattanInternalDistance(gfx::Rect(-100, -100, 100, 100)));
+  EXPECT_EQ(2, i.ManhattanInternalDistance(gfx::Rect(-101, 100, 100, 100)));
+  EXPECT_EQ(4, i.ManhattanInternalDistance(gfx::Rect(-101, -101, 100, 100)));
+  EXPECT_EQ(435, i.ManhattanInternalDistance(gfx::Rect(630, 603, 100, 100)));
+
+  RectF f(0.0f, 0.0f, 400.0f, 400.0f);
+  static const float kEpsilon = std::numeric_limits<float>::epsilon();
+
+  EXPECT_FLOAT_EQ(
+      0.0f, f.ManhattanInternalDistance(gfx::RectF(-1.0f, 0.0f, 2.0f, 1.0f)));
+  EXPECT_FLOAT_EQ(
+      kEpsilon,
+      f.ManhattanInternalDistance(gfx::RectF(400.0f, 0.0f, 1.0f, 400.0f)));
+  EXPECT_FLOAT_EQ(2.0f * kEpsilon,
+                  f.ManhattanInternalDistance(
+                      gfx::RectF(-100.0f, -100.0f, 100.0f, 100.0f)));
+  EXPECT_FLOAT_EQ(
+      1.0f + kEpsilon,
+      f.ManhattanInternalDistance(gfx::RectF(-101.0f, 100.0f, 100.0f, 100.0f)));
+  EXPECT_FLOAT_EQ(2.0f + 2.0f * kEpsilon,
+                  f.ManhattanInternalDistance(
+                      gfx::RectF(-101.0f, -101.0f, 100.0f, 100.0f)));
+  EXPECT_FLOAT_EQ(
+      433.0f + 2.0f * kEpsilon,
+      f.ManhattanInternalDistance(gfx::RectF(630.0f, 603.0f, 100.0f, 100.0f)));
+
+  EXPECT_FLOAT_EQ(
+      0.0f, f.ManhattanInternalDistance(gfx::RectF(-1.0f, 0.0f, 1.1f, 1.0f)));
+  EXPECT_FLOAT_EQ(
+      0.1f + kEpsilon,
+      f.ManhattanInternalDistance(gfx::RectF(-1.5f, 0.0f, 1.4f, 1.0f)));
+  EXPECT_FLOAT_EQ(
+      kEpsilon,
+      f.ManhattanInternalDistance(gfx::RectF(-1.5f, 0.0f, 1.5f, 1.0f)));
+}
+
+TEST(RectTest, IntegerOverflow) {
+  int limit = std::numeric_limits<int>::max();
+  int min_limit = std::numeric_limits<int>::min();
+  int expected = 10;
+  int large_number = limit - expected;
+
+  Rect height_overflow(0, large_number, 100, 100);
+  EXPECT_EQ(large_number, height_overflow.y());
+  EXPECT_EQ(expected, height_overflow.height());
+
+  Rect width_overflow(large_number, 0, 100, 100);
+  EXPECT_EQ(large_number, width_overflow.x());
+  EXPECT_EQ(expected, width_overflow.width());
+
+  Rect size_height_overflow(Point(0, large_number), Size(100, 100));
+  EXPECT_EQ(large_number, size_height_overflow.y());
+  EXPECT_EQ(expected, size_height_overflow.height());
+
+  Rect size_width_overflow(Point(large_number, 0), Size(100, 100));
+  EXPECT_EQ(large_number, size_width_overflow.x());
+  EXPECT_EQ(expected, size_width_overflow.width());
+
+  Rect set_height_overflow(0, large_number, 100, 5);
+  EXPECT_EQ(5, set_height_overflow.height());
+  set_height_overflow.set_height(100);
+  EXPECT_EQ(expected, set_height_overflow.height());
+
+  Rect set_y_overflow(100, 100, 100, 100);
+  EXPECT_EQ(100, set_y_overflow.height());
+  set_y_overflow.set_y(large_number);
+  EXPECT_EQ(expected, set_y_overflow.height());
+
+  Rect set_width_overflow(large_number, 0, 5, 100);
+  EXPECT_EQ(5, set_width_overflow.width());
+  set_width_overflow.set_width(100);
+  EXPECT_EQ(expected, set_width_overflow.width());
+
+  Rect set_x_overflow(100, 100, 100, 100);
+  EXPECT_EQ(100, set_x_overflow.width());
+  set_x_overflow.set_x(large_number);
+  EXPECT_EQ(expected, set_x_overflow.width());
+
+  Point large_offset(large_number, large_number);
+  Size size(100, 100);
+  Size expected_size(10, 10);
+
+  Rect set_origin_overflow(100, 100, 100, 100);
+  EXPECT_EQ(size, set_origin_overflow.size());
+  set_origin_overflow.set_origin(large_offset);
+  EXPECT_EQ(large_offset, set_origin_overflow.origin());
+  EXPECT_EQ(expected_size, set_origin_overflow.size());
+
+  Rect set_size_overflow(large_number, large_number, 5, 5);
+  EXPECT_EQ(Size(5, 5), set_size_overflow.size());
+  set_size_overflow.set_size(size);
+  EXPECT_EQ(large_offset, set_size_overflow.origin());
+  EXPECT_EQ(expected_size, set_size_overflow.size());
+
+  Rect set_rect_overflow;
+  set_rect_overflow.SetRect(large_number, large_number, 100, 100);
+  EXPECT_EQ(large_offset, set_rect_overflow.origin());
+  EXPECT_EQ(expected_size, set_rect_overflow.size());
+
+  // Insetting an empty rect, but the total inset (left + right) could overflow.
+  Rect inset_overflow;
+  inset_overflow.Inset(large_number, large_number, 100, 100);
+  EXPECT_EQ(large_offset, inset_overflow.origin());
+  EXPECT_EQ(gfx::Size(), inset_overflow.size());
+
+  // Insetting where the total inset (width - left - right) could overflow.
+  // Also, this insetting by the min limit in all directions cannot
+  // represent width() without overflow, so that will also clamp.
+  Rect inset_overflow2;
+  inset_overflow2.Inset(min_limit, min_limit, min_limit, min_limit);
+  EXPECT_EQ(inset_overflow2, gfx::Rect(min_limit, min_limit, limit, limit));
+
+  // Insetting where the width shouldn't change, but if the insets operations
+  // clamped in the wrong order, e.g. ((width - left) - right) vs (width - (left
+  // + right)) then this will not work properly.  This is the proper order,
+  // as if left + right overflows, the width cannot be decreased by more than
+  // max int anyway.  Additionally, if left + right underflows, it cannot be
+  // increased by more then max int.
+  Rect inset_overflow3(0, 0, limit, limit);
+  inset_overflow3.Inset(-100, -100, 100, 100);
+  EXPECT_EQ(inset_overflow3, gfx::Rect(-100, -100, limit, limit));
+
+  Rect inset_overflow4(-1000, -1000, limit, limit);
+  inset_overflow4.Inset(100, 100, -100, -100);
+  EXPECT_EQ(inset_overflow4, gfx::Rect(-900, -900, limit, limit));
+
+  Rect offset_overflow(0, 0, 100, 100);
+  offset_overflow.Offset(large_number, large_number);
+  EXPECT_EQ(large_offset, offset_overflow.origin());
+  EXPECT_EQ(expected_size, offset_overflow.size());
+
+  Rect operator_overflow(0, 0, 100, 100);
+  operator_overflow += Vector2d(large_number, large_number);
+  EXPECT_EQ(large_offset, operator_overflow.origin());
+  EXPECT_EQ(expected_size, operator_overflow.size());
+
+  Rect origin_maxint(limit, limit, limit, limit);
+  EXPECT_EQ(origin_maxint, Rect(gfx::Point(limit, limit), gfx::Size()));
+
+  // Expect a rect at the origin and a rect whose right/bottom is maxint
+  // create a rect that extends from 0..maxint in both extents.
+  {
+    Rect origin_small(0, 0, 100, 100);
+    Rect big_clamped(50, 50, limit, limit);
+    EXPECT_EQ(big_clamped.right(), limit);
+
+    Rect unioned = UnionRects(origin_small, big_clamped);
+    Rect rect_limit(0, 0, limit, limit);
+    EXPECT_EQ(unioned, rect_limit);
+  }
+
+  // Expect a rect that would overflow width (but not right) to be clamped
+  // and to have maxint extents after unioning.
+  {
+    Rect small(-500, -400, 100, 100);
+    Rect big(-400, -500, limit, limit);
+    // Technically, this should be limit + 100 width, but will clamp to maxint.
+    EXPECT_EQ(UnionRects(small, big), Rect(-500, -500, limit, limit));
+  }
+
+  // Expect a rect that would overflow right *and* width to be clamped.
+  {
+    Rect clamped(500, 500, limit, limit);
+    Rect positive_origin(100, 100, 500, 500);
+
+    // Ideally, this should be (100, 100, limit + 400, limit + 400).
+    // However, width overflows and would be clamped to limit, but right
+    // overflows too and so will be clamped to limit - 100.
+    Rect expected(100, 100, limit - 100, limit - 100);
+    EXPECT_EQ(UnionRects(clamped, positive_origin), expected);
+  }
+
+  // Unioning a left=minint rect with a right=maxint rect.
+  // We can't represent both ends of the spectrum in the same rect.
+  // Make sure we keep the most useful area.
+  {
+    int part_limit = min_limit / 3;
+    Rect left_minint(min_limit, min_limit, 1, 1);
+    Rect right_maxint(limit - 1, limit - 1, limit, limit);
+    Rect expected(part_limit, part_limit, 2 * part_limit, 2 * part_limit);
+    Rect result = UnionRects(left_minint, right_maxint);
+
+    // The result should be maximally big.
+    EXPECT_EQ(limit, result.height());
+    EXPECT_EQ(limit, result.width());
+
+    // The result should include the area near the origin.
+    EXPECT_GT(-part_limit, result.x());
+    EXPECT_LT(part_limit, result.right());
+    EXPECT_GT(-part_limit, result.y());
+    EXPECT_LT(part_limit, result.bottom());
+
+    // More succinctly, but harder to read in the results.
+    EXPECT_TRUE(UnionRects(left_minint, right_maxint).Contains(expected));
+  }
+}
+
+TEST(RectTest, ScaleToEnclosingRectSafe) {
+  const int max_int = std::numeric_limits<int>::max();
+  const int min_int = std::numeric_limits<int>::min();
+
+  Rect xy_underflow(-100000, -123456, 10, 20);
+  EXPECT_EQ(ScaleToEnclosingRectSafe(xy_underflow, 100000, 100000),
+            Rect(min_int, min_int, 1000000, 2000000));
+
+  // A location overflow means that width/right and bottom/top also
+  // overflow so need to be clamped.
+  Rect xy_overflow(100000, 123456, 10, 20);
+  EXPECT_EQ(ScaleToEnclosingRectSafe(xy_overflow, 100000, 100000),
+            Rect(max_int, max_int, 0, 0));
+
+  // In practice all rects are clamped to 0 width / 0 height so
+  // negative sizes don't matter, but try this for the sake of testing.
+  Rect size_underflow(-1, -2, 100000, 100000);
+  EXPECT_EQ(ScaleToEnclosingRectSafe(size_underflow, -100000, -100000),
+            Rect(100000, 200000, 0, 0));
+
+  Rect size_overflow(-1, -2, 123456, 234567);
+  EXPECT_EQ(ScaleToEnclosingRectSafe(size_overflow, 100000, 100000),
+            Rect(-100000, -200000, max_int, max_int));
+  // Verify width/right gets clamped properly too if x/y positive.
+  Rect size_overflow2(1, 2, 123456, 234567);
+  EXPECT_EQ(ScaleToEnclosingRectSafe(size_overflow2, 100000, 100000),
+            Rect(100000, 200000, max_int - 100000, max_int - 200000));
+
+  Rect max_rect(max_int, max_int, max_int, max_int);
+  EXPECT_EQ(ScaleToEnclosingRectSafe(max_rect, max_int, max_int),
+            Rect(max_int, max_int, 0, 0));
+
+  Rect min_rect(min_int, min_int, max_int, max_int);
+  // Min rect can't be scaled up any further in any dimension.
+  EXPECT_EQ(ScaleToEnclosingRectSafe(min_rect, 2, 3.5), min_rect);
+  EXPECT_EQ(ScaleToEnclosingRectSafe(min_rect, max_int, max_int), min_rect);
+  // Min rect scaled by min is an empty rect at (max, max)
+  EXPECT_EQ(ScaleToEnclosingRectSafe(min_rect, min_int, min_int), max_rect);
+}
+
+}  // namespace gfx
diff --git a/ui/gfx/geometry/safe_integer_conversions_unittest.cc b/ui/gfx/geometry/safe_integer_conversions_unittest.cc
new file mode 100644
index 0000000..91bdbb8
--- /dev/null
+++ b/ui/gfx/geometry/safe_integer_conversions_unittest.cc
@@ -0,0 +1,80 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/gfx/geometry/safe_integer_conversions.h"
+
+#include <limits>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace gfx {
+
+TEST(SafeIntegerConversions, ToFlooredInt) {
+  float max = std::numeric_limits<int>::max();
+  float min = std::numeric_limits<int>::min();
+  float infinity = std::numeric_limits<float>::infinity();
+
+  int int_max = std::numeric_limits<int>::max();
+  int int_min = std::numeric_limits<int>::min();
+
+  EXPECT_EQ(int_max, ToFlooredInt(infinity));
+  EXPECT_EQ(int_max, ToFlooredInt(max));
+  EXPECT_EQ(int_max, ToFlooredInt(max + 100));
+
+  EXPECT_EQ(-101, ToFlooredInt(-100.5f));
+  EXPECT_EQ(0, ToFlooredInt(0.f));
+  EXPECT_EQ(100, ToFlooredInt(100.5f));
+
+  EXPECT_EQ(int_min, ToFlooredInt(-infinity));
+  EXPECT_EQ(int_min, ToFlooredInt(min));
+  EXPECT_EQ(int_min, ToFlooredInt(min - 100));
+}
+
+TEST(SafeIntegerConversions, ToCeiledInt) {
+  float max = std::numeric_limits<int>::max();
+  float min = std::numeric_limits<int>::min();
+  float infinity = std::numeric_limits<float>::infinity();
+
+  int int_max = std::numeric_limits<int>::max();
+  int int_min = std::numeric_limits<int>::min();
+
+  EXPECT_EQ(int_max, ToCeiledInt(infinity));
+  EXPECT_EQ(int_max, ToCeiledInt(max));
+  EXPECT_EQ(int_max, ToCeiledInt(max + 100));
+
+  EXPECT_EQ(-100, ToCeiledInt(-100.5f));
+  EXPECT_EQ(0, ToCeiledInt(0.f));
+  EXPECT_EQ(101, ToCeiledInt(100.5f));
+
+  EXPECT_EQ(int_min, ToCeiledInt(-infinity));
+  EXPECT_EQ(int_min, ToCeiledInt(min));
+  EXPECT_EQ(int_min, ToCeiledInt(min - 100));
+}
+
+TEST(SafeIntegerConversions, ToRoundedInt) {
+  float max = std::numeric_limits<int>::max();
+  float min = std::numeric_limits<int>::min();
+  float infinity = std::numeric_limits<float>::infinity();
+
+  int int_max = std::numeric_limits<int>::max();
+  int int_min = std::numeric_limits<int>::min();
+
+  EXPECT_EQ(int_max, ToRoundedInt(infinity));
+  EXPECT_EQ(int_max, ToRoundedInt(max));
+  EXPECT_EQ(int_max, ToRoundedInt(max + 100));
+
+  EXPECT_EQ(-100, ToRoundedInt(-100.1f));
+  EXPECT_EQ(-101, ToRoundedInt(-100.5f));
+  EXPECT_EQ(-101, ToRoundedInt(-100.9f));
+  EXPECT_EQ(0, ToRoundedInt(0.f));
+  EXPECT_EQ(100, ToRoundedInt(100.1f));
+  EXPECT_EQ(101, ToRoundedInt(100.5f));
+  EXPECT_EQ(101, ToRoundedInt(100.9f));
+
+  EXPECT_EQ(int_min, ToRoundedInt(-infinity));
+  EXPECT_EQ(int_min, ToRoundedInt(min));
+  EXPECT_EQ(int_min, ToRoundedInt(min - 100));
+}
+
+}  // namespace gfx
diff --git a/ui/gfx/geometry/scroll_offset_unittest.cc b/ui/gfx/geometry/scroll_offset_unittest.cc
new file mode 100644
index 0000000..782fdf4
--- /dev/null
+++ b/ui/gfx/geometry/scroll_offset_unittest.cc
@@ -0,0 +1,124 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+
+#include <cmath>
+#include <limits>
+
+#include "base/macros.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/geometry/scroll_offset.h"
+
+namespace gfx {
+
+TEST(ScrollOffsetTest, IsZero) {
+  ScrollOffset zero(0, 0);
+  ScrollOffset nonzero(0.1f, -0.1f);
+
+  EXPECT_TRUE(zero.IsZero());
+  EXPECT_FALSE(nonzero.IsZero());
+}
+
+TEST(ScrollOffsetTest, Add) {
+  ScrollOffset f1(3.1f, 5.1f);
+  ScrollOffset f2(4.3f, -1.3f);
+
+  const struct {
+    ScrollOffset expected;
+    ScrollOffset actual;
+  } scroll_offset_tests[] = {
+    { ScrollOffset(3.1f, 5.1f), f1 + ScrollOffset() },
+    { ScrollOffset(3.1f + 4.3f, 5.1f - 1.3f), f1 + f2 },
+    { ScrollOffset(3.1f - 4.3f, 5.1f + 1.3f), f1 - f2 }
+  };
+
+  for (size_t i = 0; i < arraysize(scroll_offset_tests); ++i)
+    EXPECT_EQ(scroll_offset_tests[i].expected.ToString(),
+              scroll_offset_tests[i].actual.ToString());
+}
+
+TEST(ScrollOffsetTest, Negative) {
+  const struct {
+    ScrollOffset expected;
+    ScrollOffset actual;
+  } scroll_offset_tests[] = {
+    { ScrollOffset(-0.3f, -0.3f), -ScrollOffset(0.3f, 0.3f) },
+    { ScrollOffset(0.3f, 0.3f), -ScrollOffset(-0.3f, -0.3f) },
+    { ScrollOffset(-0.3f, 0.3f), -ScrollOffset(0.3f, -0.3f) },
+    { ScrollOffset(0.3f, -0.3f), -ScrollOffset(-0.3f, 0.3f) }
+  };
+
+  for (size_t i = 0; i < arraysize(scroll_offset_tests); ++i)
+    EXPECT_EQ(scroll_offset_tests[i].expected.ToString(),
+              scroll_offset_tests[i].actual.ToString());
+}
+
+TEST(ScrollOffsetTest, Scale) {
+  float float_values[][4] = {
+    { 4.5f, 1.2f, 3.3f, 5.6f },
+    { 4.5f, -1.2f, 3.3f, 5.6f },
+    { 4.5f, 1.2f, 3.3f, -5.6f },
+    { 4.5f, 1.2f, -3.3f, -5.6f },
+    { -4.5f, 1.2f, 3.3f, 5.6f },
+    { -4.5f, 1.2f, 0, 5.6f },
+    { -4.5f, 1.2f, 3.3f, 0 },
+    { 4.5f, 0, 3.3f, 5.6f },
+    { 0, 1.2f, 3.3f, 5.6f }
+  };
+
+  for (size_t i = 0; i < arraysize(float_values); ++i) {
+    ScrollOffset v(float_values[i][0], float_values[i][1]);
+    v.Scale(float_values[i][2], float_values[i][3]);
+    EXPECT_EQ(v.x(), float_values[i][0] * float_values[i][2]);
+    EXPECT_EQ(v.y(), float_values[i][1] * float_values[i][3]);
+  }
+
+  float single_values[][3] = {
+    { 4.5f, 1.2f, 3.3f },
+    { 4.5f, -1.2f, 3.3f },
+    { 4.5f, 1.2f, 3.3f },
+    { 4.5f, 1.2f, -3.3f },
+    { -4.5f, 1.2f, 3.3f },
+    { -4.5f, 1.2f, 0 },
+    { -4.5f, 1.2f, 3.3f },
+    { 4.5f, 0, 3.3f },
+    { 0, 1.2f, 3.3f }
+  };
+
+  for (size_t i = 0; i < arraysize(single_values); ++i) {
+    ScrollOffset v(single_values[i][0], single_values[i][1]);
+    v.Scale(single_values[i][2]);
+    EXPECT_EQ(v.x(), single_values[i][0] * single_values[i][2]);
+    EXPECT_EQ(v.y(), single_values[i][1] * single_values[i][2]);
+  }
+}
+
+TEST(ScrollOffsetTest, ClampScrollOffset) {
+  ScrollOffset a;
+
+  a = ScrollOffset(3.5, 5.5);
+  EXPECT_EQ(ScrollOffset(3.5, 5.5).ToString(), a.ToString());
+  a.SetToMax(ScrollOffset(2.5, 4.5));
+  EXPECT_EQ(ScrollOffset(3.5, 5.5).ToString(), a.ToString());
+  a.SetToMax(ScrollOffset(3.5, 5.5));
+  EXPECT_EQ(ScrollOffset(3.5, 5.5).ToString(), a.ToString());
+  a.SetToMax(ScrollOffset(4.5, 2.5));
+  EXPECT_EQ(ScrollOffset(4.5, 5.5).ToString(), a.ToString());
+  a.SetToMax(ScrollOffset(8.5, 10.5));
+  EXPECT_EQ(ScrollOffset(8.5, 10.5).ToString(), a.ToString());
+
+  a.SetToMin(ScrollOffset(9.5, 11.5));
+  EXPECT_EQ(ScrollOffset(8.5, 10.5).ToString(), a.ToString());
+  a.SetToMin(ScrollOffset(8.5, 10.5));
+  EXPECT_EQ(ScrollOffset(8.5, 10.5).ToString(), a.ToString());
+  a.SetToMin(ScrollOffset(11.5, 9.5));
+  EXPECT_EQ(ScrollOffset(8.5, 9.5).ToString(), a.ToString());
+  a.SetToMin(ScrollOffset(7.5, 11.5));
+  EXPECT_EQ(ScrollOffset(7.5, 9.5).ToString(), a.ToString());
+  a.SetToMin(ScrollOffset(3.5, 5.5));
+  EXPECT_EQ(ScrollOffset(3.5, 5.5).ToString(), a.ToString());
+}
+
+}  // namespace gfx
diff --git a/ui/gfx/geometry/size_unittest.cc b/ui/gfx/geometry/size_unittest.cc
new file mode 100644
index 0000000..ab4a831
--- /dev/null
+++ b/ui/gfx/geometry/size_unittest.cc
@@ -0,0 +1,251 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/gfx/geometry/size_conversions.h"
+#include "ui/gfx/geometry/size_f.h"
+
+namespace gfx {
+
+namespace {
+
+int TestSizeF(const SizeF& s) {
+  return s.width();
+}
+
+}  // namespace
+
+TEST(SizeTest, ToSizeF) {
+  // Check that explicit conversion from integer to float compiles.
+  Size a(10, 20);
+  float width = TestSizeF(gfx::SizeF(a));
+  EXPECT_EQ(width, a.width());
+
+  SizeF b(10, 20);
+
+  EXPECT_EQ(b, gfx::SizeF(a));
+}
+
+TEST(SizeTest, ToFlooredSize) {
+  EXPECT_EQ(Size(0, 0), ToFlooredSize(SizeF(0, 0)));
+  EXPECT_EQ(Size(0, 0), ToFlooredSize(SizeF(0.0001f, 0.0001f)));
+  EXPECT_EQ(Size(0, 0), ToFlooredSize(SizeF(0.4999f, 0.4999f)));
+  EXPECT_EQ(Size(0, 0), ToFlooredSize(SizeF(0.5f, 0.5f)));
+  EXPECT_EQ(Size(0, 0), ToFlooredSize(SizeF(0.9999f, 0.9999f)));
+
+  EXPECT_EQ(Size(10, 10), ToFlooredSize(SizeF(10, 10)));
+  EXPECT_EQ(Size(10, 10), ToFlooredSize(SizeF(10.0001f, 10.0001f)));
+  EXPECT_EQ(Size(10, 10), ToFlooredSize(SizeF(10.4999f, 10.4999f)));
+  EXPECT_EQ(Size(10, 10), ToFlooredSize(SizeF(10.5f, 10.5f)));
+  EXPECT_EQ(Size(10, 10), ToFlooredSize(SizeF(10.9999f, 10.9999f)));
+}
+
+TEST(SizeTest, ToCeiledSize) {
+  EXPECT_EQ(Size(0, 0), ToCeiledSize(SizeF(0, 0)));
+  EXPECT_EQ(Size(1, 1), ToCeiledSize(SizeF(0.0001f, 0.0001f)));
+  EXPECT_EQ(Size(1, 1), ToCeiledSize(SizeF(0.4999f, 0.4999f)));
+  EXPECT_EQ(Size(1, 1), ToCeiledSize(SizeF(0.5f, 0.5f)));
+  EXPECT_EQ(Size(1, 1), ToCeiledSize(SizeF(0.9999f, 0.9999f)));
+
+  EXPECT_EQ(Size(10, 10), ToCeiledSize(SizeF(10, 10)));
+  EXPECT_EQ(Size(11, 11), ToCeiledSize(SizeF(10.0001f, 10.0001f)));
+  EXPECT_EQ(Size(11, 11), ToCeiledSize(SizeF(10.4999f, 10.4999f)));
+  EXPECT_EQ(Size(11, 11), ToCeiledSize(SizeF(10.5f, 10.5f)));
+  EXPECT_EQ(Size(11, 11), ToCeiledSize(SizeF(10.9999f, 10.9999f)));
+}
+
+TEST(SizeTest, ToRoundedSize) {
+  EXPECT_EQ(Size(0, 0), ToRoundedSize(SizeF(0, 0)));
+  EXPECT_EQ(Size(0, 0), ToRoundedSize(SizeF(0.0001f, 0.0001f)));
+  EXPECT_EQ(Size(0, 0), ToRoundedSize(SizeF(0.4999f, 0.4999f)));
+  EXPECT_EQ(Size(1, 1), ToRoundedSize(SizeF(0.5f, 0.5f)));
+  EXPECT_EQ(Size(1, 1), ToRoundedSize(SizeF(0.9999f, 0.9999f)));
+
+  EXPECT_EQ(Size(10, 10), ToRoundedSize(SizeF(10, 10)));
+  EXPECT_EQ(Size(10, 10), ToRoundedSize(SizeF(10.0001f, 10.0001f)));
+  EXPECT_EQ(Size(10, 10), ToRoundedSize(SizeF(10.4999f, 10.4999f)));
+  EXPECT_EQ(Size(11, 11), ToRoundedSize(SizeF(10.5f, 10.5f)));
+  EXPECT_EQ(Size(11, 11), ToRoundedSize(SizeF(10.9999f, 10.9999f)));
+}
+
+TEST(SizeTest, ClampSize) {
+  Size a;
+
+  a = Size(3, 5);
+  EXPECT_EQ(Size(3, 5).ToString(), a.ToString());
+  a.SetToMax(Size(2, 4));
+  EXPECT_EQ(Size(3, 5).ToString(), a.ToString());
+  a.SetToMax(Size(3, 5));
+  EXPECT_EQ(Size(3, 5).ToString(), a.ToString());
+  a.SetToMax(Size(4, 2));
+  EXPECT_EQ(Size(4, 5).ToString(), a.ToString());
+  a.SetToMax(Size(8, 10));
+  EXPECT_EQ(Size(8, 10).ToString(), a.ToString());
+
+  a.SetToMin(Size(9, 11));
+  EXPECT_EQ(Size(8, 10).ToString(), a.ToString());
+  a.SetToMin(Size(8, 10));
+  EXPECT_EQ(Size(8, 10).ToString(), a.ToString());
+  a.SetToMin(Size(11, 9));
+  EXPECT_EQ(Size(8, 9).ToString(), a.ToString());
+  a.SetToMin(Size(7, 11));
+  EXPECT_EQ(Size(7, 9).ToString(), a.ToString());
+  a.SetToMin(Size(3, 5));
+  EXPECT_EQ(Size(3, 5).ToString(), a.ToString());
+}
+
+TEST(SizeTest, ClampSizeF) {
+  SizeF a;
+
+  a = SizeF(3.5f, 5.5f);
+  EXPECT_EQ(SizeF(3.5f, 5.5f).ToString(), a.ToString());
+  a.SetToMax(SizeF(2.5f, 4.5f));
+  EXPECT_EQ(SizeF(3.5f, 5.5f).ToString(), a.ToString());
+  a.SetToMax(SizeF(3.5f, 5.5f));
+  EXPECT_EQ(SizeF(3.5f, 5.5f).ToString(), a.ToString());
+  a.SetToMax(SizeF(4.5f, 2.5f));
+  EXPECT_EQ(SizeF(4.5f, 5.5f).ToString(), a.ToString());
+  a.SetToMax(SizeF(8.5f, 10.5f));
+  EXPECT_EQ(SizeF(8.5f, 10.5f).ToString(), a.ToString());
+
+  a.SetToMin(SizeF(9.5f, 11.5f));
+  EXPECT_EQ(SizeF(8.5f, 10.5f).ToString(), a.ToString());
+  a.SetToMin(SizeF(8.5f, 10.5f));
+  EXPECT_EQ(SizeF(8.5f, 10.5f).ToString(), a.ToString());
+  a.SetToMin(SizeF(11.5f, 9.5f));
+  EXPECT_EQ(SizeF(8.5f, 9.5f).ToString(), a.ToString());
+  a.SetToMin(SizeF(7.5f, 11.5f));
+  EXPECT_EQ(SizeF(7.5f, 9.5f).ToString(), a.ToString());
+  a.SetToMin(SizeF(3.5f, 5.5f));
+  EXPECT_EQ(SizeF(3.5f, 5.5f).ToString(), a.ToString());
+}
+
+TEST(SizeTest, Enlarge) {
+  Size test(3, 4);
+  test.Enlarge(5, -8);
+  EXPECT_EQ(test, Size(8, -4));
+}
+
+TEST(SizeTest, IntegerOverflow) {
+  int int_max = std::numeric_limits<int>::max();
+  int int_min = std::numeric_limits<int>::min();
+
+  Size max_size(int_max, int_max);
+  Size min_size(int_min, int_min);
+  Size test;
+
+  test = Size();
+  test.Enlarge(int_max, int_max);
+  EXPECT_EQ(test, max_size);
+
+  test = Size();
+  test.Enlarge(int_min, int_min);
+  EXPECT_EQ(test, min_size);
+
+  test = Size(10, 20);
+  test.Enlarge(int_max, int_max);
+  EXPECT_EQ(test, max_size);
+
+  test = Size(-10, -20);
+  test.Enlarge(int_min, int_min);
+  EXPECT_EQ(test, min_size);
+}
+
+// This checks that we set IsEmpty appropriately.
+TEST(SizeTest, TrivialDimensionTests) {
+  const float clearly_trivial = SizeF::kTrivial / 2.f;
+  const float massize_dimension = 4e13f;
+
+  // First, using the constructor.
+  EXPECT_TRUE(SizeF(clearly_trivial, 1.f).IsEmpty());
+  EXPECT_TRUE(SizeF(.01f, clearly_trivial).IsEmpty());
+  EXPECT_TRUE(SizeF(0.f, 0.f).IsEmpty());
+  EXPECT_FALSE(SizeF(.01f, .01f).IsEmpty());
+
+  // Then use the setter.
+  SizeF test(2.f, 1.f);
+  EXPECT_FALSE(test.IsEmpty());
+
+  test.SetSize(clearly_trivial, 1.f);
+  EXPECT_TRUE(test.IsEmpty());
+
+  test.SetSize(.01f, clearly_trivial);
+  EXPECT_TRUE(test.IsEmpty());
+
+  test.SetSize(0.f, 0.f);
+  EXPECT_TRUE(test.IsEmpty());
+
+  test.SetSize(.01f, .01f);
+  EXPECT_FALSE(test.IsEmpty());
+
+  // Now just one dimension at a time.
+  test.set_width(clearly_trivial);
+  EXPECT_TRUE(test.IsEmpty());
+
+  test.set_width(massize_dimension);
+  test.set_height(clearly_trivial);
+  EXPECT_TRUE(test.IsEmpty());
+
+  test.set_width(clearly_trivial);
+  test.set_height(massize_dimension);
+  EXPECT_TRUE(test.IsEmpty());
+
+  test.set_width(2.f);
+  EXPECT_FALSE(test.IsEmpty());
+}
+
+// These are the ramifications of the decision to keep the recorded size
+// at zero for trivial sizes.
+TEST(SizeTest, ClampsToZero) {
+  const float clearly_trivial = SizeF::kTrivial / 2.f;
+  const float nearly_trivial = SizeF::kTrivial * 1.5f;
+
+  SizeF test(clearly_trivial, 1.f);
+
+  EXPECT_FLOAT_EQ(0.f, test.width());
+  EXPECT_FLOAT_EQ(1.f, test.height());
+
+  test.SetSize(.01f, clearly_trivial);
+
+  EXPECT_FLOAT_EQ(.01f, test.width());
+  EXPECT_FLOAT_EQ(0.f, test.height());
+
+  test.SetSize(nearly_trivial, nearly_trivial);
+
+  EXPECT_FLOAT_EQ(nearly_trivial, test.width());
+  EXPECT_FLOAT_EQ(nearly_trivial, test.height());
+
+  test.Scale(0.5f);
+
+  EXPECT_FLOAT_EQ(0.f, test.width());
+  EXPECT_FLOAT_EQ(0.f, test.height());
+
+  test.SetSize(0.f, 0.f);
+  test.Enlarge(clearly_trivial, clearly_trivial);
+  test.Enlarge(clearly_trivial, clearly_trivial);
+  test.Enlarge(clearly_trivial, clearly_trivial);
+
+  EXPECT_EQ(SizeF(0.f, 0.f), test);
+}
+
+// These make sure the constructor and setter have the same effect on the
+// boundary case. This claims to know the boundary, but not which way it goes.
+TEST(SizeTest, ConsistentClamping) {
+  SizeF resized;
+
+  resized.SetSize(SizeF::kTrivial, 0.f);
+  EXPECT_EQ(SizeF(SizeF::kTrivial, 0.f), resized);
+
+  resized.SetSize(0.f, SizeF::kTrivial);
+  EXPECT_EQ(SizeF(0.f, SizeF::kTrivial), resized);
+}
+
+// Let's make sure we don't unexpectedly grow the struct by adding constants.
+// Also, if some platform packs floats inefficiently, it would be worth noting.
+TEST(SizeTest, StaysSmall) {
+  EXPECT_EQ(2 * sizeof(float), sizeof(SizeF));
+}
+
+}  // namespace gfx
diff --git a/ui/gfx/geometry/test/rect_test_util.cc b/ui/gfx/geometry/test/rect_test_util.cc
new file mode 100644
index 0000000..bcc943b
--- /dev/null
+++ b/ui/gfx/geometry/test/rect_test_util.cc
@@ -0,0 +1,23 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/gfx/geometry/test/rect_test_util.h"
+
+namespace gfx {
+namespace test {
+
+testing::AssertionResult RectContains(const gfx::Rect& outer_rect,
+                                      const gfx::Rect& inner_rect) {
+  if (outer_rect.Contains(inner_rect)) {
+    return testing::AssertionSuccess()
+           << "outer_rect (" << outer_rect.ToString()
+           << ") does contain inner_rect (" << inner_rect.ToString() << ")";
+  }
+  return testing::AssertionFailure() << "outer_rect (" << outer_rect.ToString()
+                                     << ") does not contain inner_rect ("
+                                     << inner_rect.ToString() << ")";
+}
+
+}  // namespace test
+}  // namespace gfx
diff --git a/ui/gfx/geometry/test/rect_test_util.h b/ui/gfx/geometry/test/rect_test_util.h
new file mode 100644
index 0000000..857dd21
--- /dev/null
+++ b/ui/gfx/geometry/test/rect_test_util.h
@@ -0,0 +1,21 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_GFX_GEOMETRY_TEST_RECT_TEST_UTIL_H_
+#define UI_GFX_GEOMETRY_TEST_RECT_TEST_UTIL_H_
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/gfx_export.h"
+
+namespace gfx {
+namespace test {
+
+testing::AssertionResult RectContains(const gfx::Rect& outer_rect,
+                                      const gfx::Rect& inner_rect);
+
+}  // namespace test
+}  // namespace gfx
+
+#endif  // UI_GFX_GEOMETRY_TEST_RECT_TEST_UTIL_H_
diff --git a/ui/gfx/geometry/vector2d_conversions.cc b/ui/gfx/geometry/vector2d_conversions.cc
new file mode 100644
index 0000000..dceb69e
--- /dev/null
+++ b/ui/gfx/geometry/vector2d_conversions.cc
@@ -0,0 +1,30 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/gfx/geometry/vector2d_conversions.h"
+
+#include "ui/gfx/geometry/safe_integer_conversions.h"
+
+namespace gfx {
+
+Vector2d ToFlooredVector2d(const Vector2dF& vector2d) {
+  int x = ToFlooredInt(vector2d.x());
+  int y = ToFlooredInt(vector2d.y());
+  return Vector2d(x, y);
+}
+
+Vector2d ToCeiledVector2d(const Vector2dF& vector2d) {
+  int x = ToCeiledInt(vector2d.x());
+  int y = ToCeiledInt(vector2d.y());
+  return Vector2d(x, y);
+}
+
+Vector2d ToRoundedVector2d(const Vector2dF& vector2d) {
+  int x = ToRoundedInt(vector2d.x());
+  int y = ToRoundedInt(vector2d.y());
+  return Vector2d(x, y);
+}
+
+}  // namespace gfx
+
diff --git a/ui/gfx/geometry/vector2d_conversions.h b/ui/gfx/geometry/vector2d_conversions.h
new file mode 100644
index 0000000..f4e16ae
--- /dev/null
+++ b/ui/gfx/geometry/vector2d_conversions.h
@@ -0,0 +1,24 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_GFX_GEOMETRY_VECTOR2D_CONVERSIONS_H_
+#define UI_GFX_GEOMETRY_VECTOR2D_CONVERSIONS_H_
+
+#include "ui/gfx/geometry/vector2d.h"
+#include "ui/gfx/geometry/vector2d_f.h"
+
+namespace gfx {
+
+// Returns a Vector2d with each component from the input Vector2dF floored.
+GFX_EXPORT Vector2d ToFlooredVector2d(const Vector2dF& vector2d);
+
+// Returns a Vector2d with each component from the input Vector2dF ceiled.
+GFX_EXPORT Vector2d ToCeiledVector2d(const Vector2dF& vector2d);
+
+// Returns a Vector2d with each component from the input Vector2dF rounded.
+GFX_EXPORT Vector2d ToRoundedVector2d(const Vector2dF& vector2d);
+
+}  // namespace gfx
+
+#endif  // UI_GFX_GEOMETRY_VECTOR2D_CONVERSIONS_H_
diff --git a/ui/gfx/geometry/vector2d_unittest.cc b/ui/gfx/geometry/vector2d_unittest.cc
new file mode 100644
index 0000000..4bdf24c
--- /dev/null
+++ b/ui/gfx/geometry/vector2d_unittest.cc
@@ -0,0 +1,293 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+
+#include <cmath>
+#include <limits>
+
+#include "base/macros.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/geometry/vector2d.h"
+#include "ui/gfx/geometry/vector2d_f.h"
+
+namespace gfx {
+
+TEST(Vector2dTest, ConversionToFloat) {
+  Vector2d i(3, 4);
+  Vector2dF f = i;
+  EXPECT_EQ(i, f);
+}
+
+TEST(Vector2dTest, IsZero) {
+  Vector2d int_zero(0, 0);
+  Vector2d int_nonzero(2, -2);
+  Vector2dF float_zero(0, 0);
+  Vector2dF float_nonzero(0.1f, -0.1f);
+
+  EXPECT_TRUE(int_zero.IsZero());
+  EXPECT_FALSE(int_nonzero.IsZero());
+  EXPECT_TRUE(float_zero.IsZero());
+  EXPECT_FALSE(float_nonzero.IsZero());
+}
+
+TEST(Vector2dTest, Add) {
+  Vector2d i1(3, 5);
+  Vector2d i2(4, -1);
+
+  const struct {
+    Vector2d expected;
+    Vector2d actual;
+  } int_tests[] = {
+    { Vector2d(3, 5), i1 + Vector2d() },
+    { Vector2d(3 + 4, 5 - 1), i1 + i2 },
+    { Vector2d(3 - 4, 5 + 1), i1 - i2 }
+  };
+
+  for (size_t i = 0; i < arraysize(int_tests); ++i)
+    EXPECT_EQ(int_tests[i].expected.ToString(),
+              int_tests[i].actual.ToString());
+
+  Vector2dF f1(3.1f, 5.1f);
+  Vector2dF f2(4.3f, -1.3f);
+
+  const struct {
+    Vector2dF expected;
+    Vector2dF actual;
+  } float_tests[] = {
+    { Vector2dF(3.1F, 5.1F), f1 + Vector2d() },
+    { Vector2dF(3.1F, 5.1F), f1 + Vector2dF() },
+    { Vector2dF(3.1f + 4.3f, 5.1f - 1.3f), f1 + f2 },
+    { Vector2dF(3.1f - 4.3f, 5.1f + 1.3f), f1 - f2 }
+  };
+
+  for (size_t i = 0; i < arraysize(float_tests); ++i)
+    EXPECT_EQ(float_tests[i].expected.ToString(),
+              float_tests[i].actual.ToString());
+}
+
+TEST(Vector2dTest, Negative) {
+  const struct {
+    Vector2d expected;
+    Vector2d actual;
+  } int_tests[] = {
+    { Vector2d(0, 0), -Vector2d(0, 0) },
+    { Vector2d(-3, -3), -Vector2d(3, 3) },
+    { Vector2d(3, 3), -Vector2d(-3, -3) },
+    { Vector2d(-3, 3), -Vector2d(3, -3) },
+    { Vector2d(3, -3), -Vector2d(-3, 3) }
+  };
+
+  for (size_t i = 0; i < arraysize(int_tests); ++i)
+    EXPECT_EQ(int_tests[i].expected.ToString(),
+              int_tests[i].actual.ToString());
+
+  const struct {
+    Vector2dF expected;
+    Vector2dF actual;
+  } float_tests[] = {
+    { Vector2dF(0, 0), -Vector2d(0, 0) },
+    { Vector2dF(-0.3f, -0.3f), -Vector2dF(0.3f, 0.3f) },
+    { Vector2dF(0.3f, 0.3f), -Vector2dF(-0.3f, -0.3f) },
+    { Vector2dF(-0.3f, 0.3f), -Vector2dF(0.3f, -0.3f) },
+    { Vector2dF(0.3f, -0.3f), -Vector2dF(-0.3f, 0.3f) }
+  };
+
+  for (size_t i = 0; i < arraysize(float_tests); ++i)
+    EXPECT_EQ(float_tests[i].expected.ToString(),
+              float_tests[i].actual.ToString());
+}
+
+TEST(Vector2dTest, Scale) {
+  float double_values[][4] = {
+    { 4.5f, 1.2f, 3.3f, 5.6f },
+    { 4.5f, -1.2f, 3.3f, 5.6f },
+    { 4.5f, 1.2f, 3.3f, -5.6f },
+    { 4.5f, 1.2f, -3.3f, -5.6f },
+    { -4.5f, 1.2f, 3.3f, 5.6f },
+    { -4.5f, 1.2f, 0, 5.6f },
+    { -4.5f, 1.2f, 3.3f, 0 },
+    { 4.5f, 0, 3.3f, 5.6f },
+    { 0, 1.2f, 3.3f, 5.6f }
+  };
+
+  for (size_t i = 0; i < arraysize(double_values); ++i) {
+    Vector2dF v(double_values[i][0], double_values[i][1]);
+    v.Scale(double_values[i][2], double_values[i][3]);
+    EXPECT_EQ(v.x(), double_values[i][0] * double_values[i][2]);
+    EXPECT_EQ(v.y(), double_values[i][1] * double_values[i][3]);
+
+    Vector2dF v2 = ScaleVector2d(
+        gfx::Vector2dF(double_values[i][0], double_values[i][1]),
+        double_values[i][2], double_values[i][3]);
+    EXPECT_EQ(double_values[i][0] * double_values[i][2], v2.x());
+    EXPECT_EQ(double_values[i][1] * double_values[i][3], v2.y());
+  }
+
+  float single_values[][3] = {
+    { 4.5f, 1.2f, 3.3f },
+    { 4.5f, -1.2f, 3.3f },
+    { 4.5f, 1.2f, 3.3f },
+    { 4.5f, 1.2f, -3.3f },
+    { -4.5f, 1.2f, 3.3f },
+    { -4.5f, 1.2f, 0 },
+    { -4.5f, 1.2f, 3.3f },
+    { 4.5f, 0, 3.3f },
+    { 0, 1.2f, 3.3f }
+  };
+
+  for (size_t i = 0; i < arraysize(single_values); ++i) {
+    Vector2dF v(single_values[i][0], single_values[i][1]);
+    v.Scale(single_values[i][2]);
+    EXPECT_EQ(v.x(), single_values[i][0] * single_values[i][2]);
+    EXPECT_EQ(v.y(), single_values[i][1] * single_values[i][2]);
+
+    Vector2dF v2 = ScaleVector2d(
+        gfx::Vector2dF(double_values[i][0], double_values[i][1]),
+        double_values[i][2]);
+    EXPECT_EQ(single_values[i][0] * single_values[i][2], v2.x());
+    EXPECT_EQ(single_values[i][1] * single_values[i][2], v2.y());
+  }
+}
+
+TEST(Vector2dTest, Length) {
+  int int_values[][2] = {
+    { 0, 0 },
+    { 10, 20 },
+    { 20, 10 },
+    { -10, -20 },
+    { -20, 10 },
+    { 10, -20 },
+  };
+
+  for (size_t i = 0; i < arraysize(int_values); ++i) {
+    int v0 = int_values[i][0];
+    int v1 = int_values[i][1];
+    double length_squared =
+        static_cast<double>(v0) * v0 + static_cast<double>(v1) * v1;
+    double length = std::sqrt(length_squared);
+    Vector2d vector(v0, v1);
+    EXPECT_EQ(static_cast<float>(length_squared), vector.LengthSquared());
+    EXPECT_EQ(static_cast<float>(length), vector.Length());
+  }
+
+  float float_values[][2] = {
+    { 0, 0 },
+    { 10.5f, 20.5f },
+    { 20.5f, 10.5f },
+    { -10.5f, -20.5f },
+    { -20.5f, 10.5f },
+    { 10.5f, -20.5f },
+    // A large vector that fails if the Length function doesn't use
+    // double precision internally.
+    { 1236278317862780234892374893213178027.12122348904204230f,
+      335890352589839028212313231225425134332.38123f },
+  };
+
+  for (size_t i = 0; i < arraysize(float_values); ++i) {
+    double v0 = float_values[i][0];
+    double v1 = float_values[i][1];
+    double length_squared =
+        static_cast<double>(v0) * v0 + static_cast<double>(v1) * v1;
+    double length = std::sqrt(length_squared);
+    Vector2dF vector(v0, v1);
+    EXPECT_DOUBLE_EQ(length_squared, vector.LengthSquared());
+    EXPECT_FLOAT_EQ(static_cast<float>(length), vector.Length());
+  }
+}
+
+TEST(Vector2dTest, ClampVector2d) {
+  Vector2d a;
+
+  a = Vector2d(3, 5);
+  EXPECT_EQ(Vector2d(3, 5).ToString(), a.ToString());
+  a.SetToMax(Vector2d(2, 4));
+  EXPECT_EQ(Vector2d(3, 5).ToString(), a.ToString());
+  a.SetToMax(Vector2d(3, 5));
+  EXPECT_EQ(Vector2d(3, 5).ToString(), a.ToString());
+  a.SetToMax(Vector2d(4, 2));
+  EXPECT_EQ(Vector2d(4, 5).ToString(), a.ToString());
+  a.SetToMax(Vector2d(8, 10));
+  EXPECT_EQ(Vector2d(8, 10).ToString(), a.ToString());
+
+  a.SetToMin(Vector2d(9, 11));
+  EXPECT_EQ(Vector2d(8, 10).ToString(), a.ToString());
+  a.SetToMin(Vector2d(8, 10));
+  EXPECT_EQ(Vector2d(8, 10).ToString(), a.ToString());
+  a.SetToMin(Vector2d(11, 9));
+  EXPECT_EQ(Vector2d(8, 9).ToString(), a.ToString());
+  a.SetToMin(Vector2d(7, 11));
+  EXPECT_EQ(Vector2d(7, 9).ToString(), a.ToString());
+  a.SetToMin(Vector2d(3, 5));
+  EXPECT_EQ(Vector2d(3, 5).ToString(), a.ToString());
+}
+
+TEST(Vector2dTest, ClampVector2dF) {
+  Vector2dF a;
+
+  a = Vector2dF(3.5f, 5.5f);
+  EXPECT_EQ(Vector2dF(3.5f, 5.5f).ToString(), a.ToString());
+  a.SetToMax(Vector2dF(2.5f, 4.5f));
+  EXPECT_EQ(Vector2dF(3.5f, 5.5f).ToString(), a.ToString());
+  a.SetToMax(Vector2dF(3.5f, 5.5f));
+  EXPECT_EQ(Vector2dF(3.5f, 5.5f).ToString(), a.ToString());
+  a.SetToMax(Vector2dF(4.5f, 2.5f));
+  EXPECT_EQ(Vector2dF(4.5f, 5.5f).ToString(), a.ToString());
+  a.SetToMax(Vector2dF(8.5f, 10.5f));
+  EXPECT_EQ(Vector2dF(8.5f, 10.5f).ToString(), a.ToString());
+
+  a.SetToMin(Vector2dF(9.5f, 11.5f));
+  EXPECT_EQ(Vector2dF(8.5f, 10.5f).ToString(), a.ToString());
+  a.SetToMin(Vector2dF(8.5f, 10.5f));
+  EXPECT_EQ(Vector2dF(8.5f, 10.5f).ToString(), a.ToString());
+  a.SetToMin(Vector2dF(11.5f, 9.5f));
+  EXPECT_EQ(Vector2dF(8.5f, 9.5f).ToString(), a.ToString());
+  a.SetToMin(Vector2dF(7.5f, 11.5f));
+  EXPECT_EQ(Vector2dF(7.5f, 9.5f).ToString(), a.ToString());
+  a.SetToMin(Vector2dF(3.5f, 5.5f));
+  EXPECT_EQ(Vector2dF(3.5f, 5.5f).ToString(), a.ToString());
+}
+
+TEST(Vector2dTest, IntegerOverflow) {
+  int int_max = std::numeric_limits<int>::max();
+  int int_min = std::numeric_limits<int>::min();
+
+  Vector2d max_vector(int_max, int_max);
+  Vector2d min_vector(int_min, int_min);
+  Vector2d test;
+
+  test = Vector2d();
+  test += Vector2d(int_max, int_max);
+  EXPECT_EQ(test, max_vector);
+
+  test = Vector2d();
+  test += Vector2d(int_min, int_min);
+  EXPECT_EQ(test, min_vector);
+
+  test = Vector2d(10, 20);
+  test += Vector2d(int_max, int_max);
+  EXPECT_EQ(test, max_vector);
+
+  test = Vector2d(-10, -20);
+  test += Vector2d(int_min, int_min);
+  EXPECT_EQ(test, min_vector);
+
+  test = Vector2d();
+  test -= Vector2d(int_max, int_max);
+  EXPECT_EQ(test, Vector2d(-int_max, -int_max));
+
+  test = Vector2d();
+  test -= Vector2d(int_min, int_min);
+  EXPECT_EQ(test, max_vector);
+
+  test = Vector2d(10, 20);
+  test -= Vector2d(int_min, int_min);
+  EXPECT_EQ(test, max_vector);
+
+  test = Vector2d(-10, -20);
+  test -= Vector2d(int_max, int_max);
+  EXPECT_EQ(test, min_vector);
+}
+
+}  // namespace gfx
diff --git a/ui/gfx/geometry/vector3d_f.cc b/ui/gfx/geometry/vector3d_f.cc
new file mode 100644
index 0000000..749e330
--- /dev/null
+++ b/ui/gfx/geometry/vector3d_f.cc
@@ -0,0 +1,108 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/gfx/geometry/vector3d_f.h"
+
+#include <cmath>
+
+#include "base/strings/stringprintf.h"
+#include "ui/gfx/geometry/angle_conversions.h"
+
+namespace {
+const double kEpsilon = 1.0e-6;
+}
+
+namespace gfx {
+
+std::string Vector3dF::ToString() const {
+  return base::StringPrintf("[%f %f %f]", x_, y_, z_);
+}
+
+bool Vector3dF::IsZero() const {
+  return x_ == 0 && y_ == 0 && z_ == 0;
+}
+
+void Vector3dF::Add(const Vector3dF& other) {
+  x_ += other.x_;
+  y_ += other.y_;
+  z_ += other.z_;
+}
+
+void Vector3dF::Subtract(const Vector3dF& other) {
+  x_ -= other.x_;
+  y_ -= other.y_;
+  z_ -= other.z_;
+}
+
+double Vector3dF::LengthSquared() const {
+  return static_cast<double>(x_) * x_ + static_cast<double>(y_) * y_ +
+      static_cast<double>(z_) * z_;
+}
+
+float Vector3dF::Length() const {
+  return static_cast<float>(std::sqrt(LengthSquared()));
+}
+
+void Vector3dF::Scale(float x_scale, float y_scale, float z_scale) {
+  x_ *= x_scale;
+  y_ *= y_scale;
+  z_ *= z_scale;
+}
+
+void Vector3dF::Cross(const Vector3dF& other) {
+  double dx = x_;
+  double dy = y_;
+  double dz = z_;
+  float x = static_cast<float>(dy * other.z() - dz * other.y());
+  float y = static_cast<float>(dz * other.x() - dx * other.z());
+  float z = static_cast<float>(dx * other.y() - dy * other.x());
+  x_ = x;
+  y_ = y;
+  z_ = z;
+}
+
+bool Vector3dF::GetNormalized(Vector3dF* out) const {
+  double length_squared = LengthSquared();
+  *out = *this;
+  if (length_squared < kEpsilon * kEpsilon)
+    return false;
+  out->Scale(1 / sqrt(length_squared));
+  return true;
+}
+
+float DotProduct(const Vector3dF& lhs, const Vector3dF& rhs) {
+  return lhs.x() * rhs.x() + lhs.y() * rhs.y() + lhs.z() * rhs.z();
+}
+
+Vector3dF ScaleVector3d(const Vector3dF& v,
+                        float x_scale,
+                        float y_scale,
+                        float z_scale) {
+  Vector3dF scaled_v(v);
+  scaled_v.Scale(x_scale, y_scale, z_scale);
+  return scaled_v;
+}
+
+float AngleBetweenVectorsInDegrees(const gfx::Vector3dF& base,
+                                   const gfx::Vector3dF& other) {
+  return gfx::RadToDeg(
+      std::acos(gfx::DotProduct(base, other) / base.Length() / other.Length()));
+}
+
+float ClockwiseAngleBetweenVectorsInDegrees(const gfx::Vector3dF& base,
+                                            const gfx::Vector3dF& other,
+                                            const gfx::Vector3dF& normal) {
+  float angle = AngleBetweenVectorsInDegrees(base, other);
+  gfx::Vector3dF cross(base);
+  cross.Cross(other);
+
+  // If the dot product of this cross product is normal, it means that the
+  // shortest angle between |base| and |other| was counterclockwise with respect
+  // to the surface represented by |normal| and this angle must be reversed.
+  if (gfx::DotProduct(cross, normal) > 0.0f)
+    angle = 360.0f - angle;
+  return angle;
+}
+
+}  // namespace gfx
diff --git a/ui/gfx/geometry/vector3d_f.h b/ui/gfx/geometry/vector3d_f.h
new file mode 100644
index 0000000..0e5e437
--- /dev/null
+++ b/ui/gfx/geometry/vector3d_f.h
@@ -0,0 +1,151 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Defines a simple float vector class.  This class is used to indicate a
+// distance in two dimensions between two points. Subtracting two points should
+// produce a vector, and adding a vector to a point produces the point at the
+// vector's distance from the original point.
+
+#ifndef UI_GFX_GEOMETRY_VECTOR3D_F_H_
+#define UI_GFX_GEOMETRY_VECTOR3D_F_H_
+
+#include <iosfwd>
+#include <string>
+
+#include "ui/gfx/geometry/vector2d_f.h"
+#include "ui/gfx/gfx_export.h"
+
+namespace gfx {
+
+class GFX_EXPORT Vector3dF {
+ public:
+  constexpr Vector3dF() : x_(0), y_(0), z_(0) {}
+  constexpr Vector3dF(float x, float y, float z) : x_(x), y_(y), z_(z) {}
+
+  constexpr explicit Vector3dF(const Vector2dF& other)
+      : x_(other.x()), y_(other.y()), z_(0) {}
+
+  constexpr float x() const { return x_; }
+  void set_x(float x) { x_ = x; }
+
+  constexpr float y() const { return y_; }
+  void set_y(float y) { y_ = y; }
+
+  constexpr float z() const { return z_; }
+  void set_z(float z) { z_ = z; }
+
+  // True if all components of the vector are 0.
+  bool IsZero() const;
+
+  // Add the components of the |other| vector to the current vector.
+  void Add(const Vector3dF& other);
+  // Subtract the components of the |other| vector from the current vector.
+  void Subtract(const Vector3dF& other);
+
+  void operator+=(const Vector3dF& other) { Add(other); }
+  void operator-=(const Vector3dF& other) { Subtract(other); }
+
+  void SetToMin(const Vector3dF& other) {
+    x_ = x_ <= other.x_ ? x_ : other.x_;
+    y_ = y_ <= other.y_ ? y_ : other.y_;
+    z_ = z_ <= other.z_ ? z_ : other.z_;
+  }
+
+  void SetToMax(const Vector3dF& other) {
+    x_ = x_ >= other.x_ ? x_ : other.x_;
+    y_ = y_ >= other.y_ ? y_ : other.y_;
+    z_ = z_ >= other.z_ ? z_ : other.z_;
+  }
+
+  // Gives the square of the diagonal length of the vector.
+  double LengthSquared() const;
+  // Gives the diagonal length of the vector.
+  float Length() const;
+
+  // Scale all components of the vector by |scale|.
+  void Scale(float scale) { Scale(scale, scale, scale); }
+  // Scale the each component of the vector by the given scale factors.
+  void Scale(float x_scale, float y_scale, float z_scale);
+
+  // Take the cross product of this vector with |other| and become the result.
+  void Cross(const Vector3dF& other);
+
+  // |out| is assigned a unit-length vector in the direction of |this| iff
+  // this function returns true. It can return false if |this| is too short.
+  bool GetNormalized(Vector3dF* out) const;
+
+  std::string ToString() const;
+
+ private:
+  float x_;
+  float y_;
+  float z_;
+};
+
+inline bool operator==(const Vector3dF& lhs, const Vector3dF& rhs) {
+  return lhs.x() == rhs.x() && lhs.y() == rhs.y() && lhs.z() == rhs.z();
+}
+
+inline Vector3dF operator-(const Vector3dF& v) {
+  return Vector3dF(-v.x(), -v.y(), -v.z());
+}
+
+inline Vector3dF operator+(const Vector3dF& lhs, const Vector3dF& rhs) {
+  Vector3dF result = lhs;
+  result.Add(rhs);
+  return result;
+}
+
+inline Vector3dF operator-(const Vector3dF& lhs, const Vector3dF& rhs) {
+  Vector3dF result = lhs;
+  result.Add(-rhs);
+  return result;
+}
+
+// Return the cross product of two vectors.
+inline Vector3dF CrossProduct(const Vector3dF& lhs, const Vector3dF& rhs) {
+  Vector3dF result = lhs;
+  result.Cross(rhs);
+  return result;
+}
+
+// Return the dot product of two vectors.
+GFX_EXPORT float DotProduct(const Vector3dF& lhs, const Vector3dF& rhs);
+
+// Return a vector that is |v| scaled by the given scale factors along each
+// axis.
+GFX_EXPORT Vector3dF ScaleVector3d(const Vector3dF& v,
+                                   float x_scale,
+                                   float y_scale,
+                                   float z_scale);
+
+// Return a vector that is |v| scaled by the components of |s|
+inline Vector3dF ScaleVector3d(const Vector3dF& v, const Vector3dF& s) {
+  return ScaleVector3d(v, s.x(), s.y(), s.z());
+}
+
+// Return a vector that is |v| scaled by the given scale factor.
+inline Vector3dF ScaleVector3d(const Vector3dF& v, float scale) {
+  return ScaleVector3d(v, scale, scale, scale);
+}
+
+// Returns the angle between |base| and |other| in degrees.
+GFX_EXPORT float AngleBetweenVectorsInDegrees(const gfx::Vector3dF& base,
+                                              const gfx::Vector3dF& other);
+
+// Returns the clockwise angle between |base| and |other| where |normal| is the
+// normal of the virtual surface to measure clockwise according to.
+GFX_EXPORT float ClockwiseAngleBetweenVectorsInDegrees(
+    const gfx::Vector3dF& base,
+    const gfx::Vector3dF& other,
+    const gfx::Vector3dF& normal);
+
+// This is declared here for use in gtest-based unit tests but is defined in
+// the //ui/gfx:test_support target. Depend on that to use this in your unit
+// test. This should not be used in production code - call ToString() instead.
+void PrintTo(const Vector3dF& vector, ::std::ostream* os);
+
+}  // namespace gfx
+
+#endif // UI_GFX_GEOMETRY_VECTOR3D_F_H_
diff --git a/ui/gfx/geometry/vector3d_unittest.cc b/ui/gfx/geometry/vector3d_unittest.cc
new file mode 100644
index 0000000..12ee757
--- /dev/null
+++ b/ui/gfx/geometry/vector3d_unittest.cc
@@ -0,0 +1,347 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+
+#include <cmath>
+#include <limits>
+
+#include "base/macros.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/geometry/vector3d_f.h"
+
+namespace gfx {
+
+TEST(Vector3dTest, IsZero) {
+  gfx::Vector3dF float_zero(0, 0, 0);
+  gfx::Vector3dF float_nonzero(0.1f, -0.1f, 0.1f);
+
+  EXPECT_TRUE(float_zero.IsZero());
+  EXPECT_FALSE(float_nonzero.IsZero());
+}
+
+TEST(Vector3dTest, Add) {
+  gfx::Vector3dF f1(3.1f, 5.1f, 2.7f);
+  gfx::Vector3dF f2(4.3f, -1.3f, 8.1f);
+
+  const struct {
+    gfx::Vector3dF expected;
+    gfx::Vector3dF actual;
+  } float_tests[] = {
+    { gfx::Vector3dF(3.1F, 5.1F, 2.7f), f1 + gfx::Vector3dF() },
+    { gfx::Vector3dF(3.1f + 4.3f, 5.1f - 1.3f, 2.7f + 8.1f), f1 + f2 },
+    { gfx::Vector3dF(3.1f - 4.3f, 5.1f + 1.3f, 2.7f - 8.1f), f1 - f2 }
+  };
+
+  for (size_t i = 0; i < arraysize(float_tests); ++i)
+    EXPECT_EQ(float_tests[i].expected.ToString(),
+              float_tests[i].actual.ToString());
+}
+
+TEST(Vector3dTest, Negative) {
+  const struct {
+    gfx::Vector3dF expected;
+    gfx::Vector3dF actual;
+  } float_tests[] = {
+    { gfx::Vector3dF(-0.0f, -0.0f, -0.0f), -gfx::Vector3dF(0, 0, 0) },
+    { gfx::Vector3dF(-0.3f, -0.3f, -0.3f), -gfx::Vector3dF(0.3f, 0.3f, 0.3f) },
+    { gfx::Vector3dF(0.3f, 0.3f, 0.3f), -gfx::Vector3dF(-0.3f, -0.3f, -0.3f) },
+    { gfx::Vector3dF(-0.3f, 0.3f, -0.3f), -gfx::Vector3dF(0.3f, -0.3f, 0.3f) },
+    { gfx::Vector3dF(0.3f, -0.3f, -0.3f), -gfx::Vector3dF(-0.3f, 0.3f, 0.3f) },
+    { gfx::Vector3dF(-0.3f, -0.3f, 0.3f), -gfx::Vector3dF(0.3f, 0.3f, -0.3f) }
+  };
+
+  for (size_t i = 0; i < arraysize(float_tests); ++i)
+    EXPECT_EQ(float_tests[i].expected.ToString(),
+              float_tests[i].actual.ToString());
+}
+
+TEST(Vector3dTest, Scale) {
+  float triple_values[][6] = {
+    { 4.5f, 1.2f, 1.8f, 3.3f, 5.6f, 4.2f },
+    { 4.5f, -1.2f, -1.8f, 3.3f, 5.6f, 4.2f },
+    { 4.5f, 1.2f, -1.8f, 3.3f, 5.6f, 4.2f },
+    { 4.5f, -1.2f -1.8f, 3.3f, 5.6f, 4.2f },
+
+    { 4.5f, 1.2f, 1.8f, 3.3f, -5.6f, -4.2f },
+    { 4.5f, 1.2f, 1.8f, -3.3f, -5.6f, -4.2f },
+    { 4.5f, 1.2f, -1.8f, 3.3f, -5.6f, -4.2f },
+    { 4.5f, 1.2f, -1.8f, -3.3f, -5.6f, -4.2f },
+
+    { -4.5f, 1.2f, 1.8f, 3.3f, 5.6f, 4.2f },
+    { -4.5f, 1.2f, 1.8f, 0, 5.6f, 4.2f },
+    { -4.5f, 1.2f, -1.8f, 3.3f, 5.6f, 4.2f },
+    { -4.5f, 1.2f, -1.8f, 0, 5.6f, 4.2f },
+
+    { -4.5f, 1.2f, 1.8f, 3.3f, 0, 4.2f },
+    { 4.5f, 0, 1.8f, 3.3f, 5.6f, 4.2f },
+    { -4.5f, 1.2f, -1.8f, 3.3f, 0, 4.2f },
+    { 4.5f, 0, -1.8f, 3.3f, 5.6f, 4.2f },
+    { -4.5f, 1.2f, 1.8f, 3.3f, 5.6f, 0 },
+    { -4.5f, 1.2f, -1.8f, 3.3f, 5.6f, 0 },
+
+    { 0, 1.2f, 0, 3.3f, 5.6f, 4.2f },
+    { 0, 1.2f, 1.8f, 3.3f, 5.6f, 4.2f }
+  };
+
+  for (size_t i = 0; i < arraysize(triple_values); ++i) {
+    gfx::Vector3dF v(triple_values[i][0],
+                     triple_values[i][1],
+                     triple_values[i][2]);
+    v.Scale(triple_values[i][3], triple_values[i][4], triple_values[i][5]);
+    EXPECT_EQ(triple_values[i][0] * triple_values[i][3], v.x());
+    EXPECT_EQ(triple_values[i][1] * triple_values[i][4], v.y());
+    EXPECT_EQ(triple_values[i][2] * triple_values[i][5], v.z());
+
+    Vector3dF v2 = ScaleVector3d(
+        gfx::Vector3dF(triple_values[i][0],
+                       triple_values[i][1],
+                       triple_values[i][2]),
+        triple_values[i][3], triple_values[i][4], triple_values[i][5]);
+    EXPECT_EQ(triple_values[i][0] * triple_values[i][3], v2.x());
+    EXPECT_EQ(triple_values[i][1] * triple_values[i][4], v2.y());
+    EXPECT_EQ(triple_values[i][2] * triple_values[i][5], v2.z());
+  }
+
+  float single_values[][4] = {
+    { 4.5f, 1.2f, 1.8f, 3.3f },
+    { 4.5f, -1.2f, 1.8f, 3.3f },
+    { 4.5f, 1.2f, -1.8f, 3.3f },
+    { 4.5f, -1.2f, -1.8f, 3.3f },
+    { -4.5f, 1.2f, 3.3f },
+    { -4.5f, 1.2f, 0 },
+    { -4.5f, 1.2f, 1.8f, 3.3f },
+    { -4.5f, 1.2f, 1.8f, 0 },
+    { 4.5f, 0, 1.8f, 3.3f },
+    { 0, 1.2f, 1.8f, 3.3f },
+    { 4.5f, 0, 1.8f, 3.3f },
+    { 0, 1.2f, 1.8f, 3.3f },
+    { 4.5f, 1.2f, 0, 3.3f },
+    { 4.5f, 1.2f, 0, 3.3f }
+  };
+
+  for (size_t i = 0; i < arraysize(single_values); ++i) {
+    gfx::Vector3dF v(single_values[i][0],
+                     single_values[i][1],
+                     single_values[i][2]);
+    v.Scale(single_values[i][3]);
+    EXPECT_EQ(single_values[i][0] * single_values[i][3], v.x());
+    EXPECT_EQ(single_values[i][1] * single_values[i][3], v.y());
+    EXPECT_EQ(single_values[i][2] * single_values[i][3], v.z());
+
+    Vector3dF v2 = ScaleVector3d(
+        gfx::Vector3dF(single_values[i][0],
+                       single_values[i][1],
+                       single_values[i][2]),
+        single_values[i][3]);
+    EXPECT_EQ(single_values[i][0] * single_values[i][3], v2.x());
+    EXPECT_EQ(single_values[i][1] * single_values[i][3], v2.y());
+    EXPECT_EQ(single_values[i][2] * single_values[i][3], v2.z());
+  }
+}
+
+TEST(Vector3dTest, Length) {
+  float float_values[][3] = {
+    { 0, 0, 0 },
+    { 10.5f, 20.5f, 8.5f },
+    { 20.5f, 10.5f, 8.5f },
+    { 8.5f, 20.5f, 10.5f },
+    { 10.5f, 8.5f, 20.5f },
+    { -10.5f, -20.5f, -8.5f },
+    { -20.5f, 10.5f, -8.5f },
+    { -8.5f, -20.5f, -10.5f },
+    { -10.5f, -8.5f, -20.5f },
+    { 10.5f, -20.5f, 8.5f },
+    { -10.5f, 20.5f, 8.5f },
+    { 10.5f, -20.5f, -8.5f },
+    { -10.5f, 20.5f, -8.5f },
+    // A large vector that fails if the Length function doesn't use
+    // double precision internally.
+    { 1236278317862780234892374893213178027.12122348904204230f,
+      335890352589839028212313231225425134332.38123f,
+      27861786423846742743236423478236784678.236713617231f }
+  };
+
+  for (size_t i = 0; i < arraysize(float_values); ++i) {
+    double v0 = float_values[i][0];
+    double v1 = float_values[i][1];
+    double v2 = float_values[i][2];
+    double length_squared =
+        static_cast<double>(v0) * v0 +
+        static_cast<double>(v1) * v1 +
+        static_cast<double>(v2) * v2;
+    double length = std::sqrt(length_squared);
+    gfx::Vector3dF vector(v0, v1, v2);
+    EXPECT_DOUBLE_EQ(length_squared, vector.LengthSquared());
+    EXPECT_FLOAT_EQ(static_cast<float>(length), vector.Length());
+  }
+}
+
+TEST(Vector3dTest, DotProduct) {
+  const struct {
+    float expected;
+    gfx::Vector3dF input1;
+    gfx::Vector3dF input2;
+  } tests[] = {
+    { 0, gfx::Vector3dF(1, 0, 0), gfx::Vector3dF(0, 1, 1) },
+    { 0, gfx::Vector3dF(0, 1, 0), gfx::Vector3dF(1, 0, 1) },
+    { 0, gfx::Vector3dF(0, 0, 1), gfx::Vector3dF(1, 1, 0) },
+
+    { 3, gfx::Vector3dF(1, 1, 1), gfx::Vector3dF(1, 1, 1) },
+
+    { 1.2f, gfx::Vector3dF(1.2f, -1.2f, 1.2f), gfx::Vector3dF(1, 1, 1) },
+    { 1.2f, gfx::Vector3dF(1, 1, 1), gfx::Vector3dF(1.2f, -1.2f, 1.2f) },
+
+    { 38.72f,
+      gfx::Vector3dF(1.1f, 2.2f, 3.3f), gfx::Vector3dF(4.4f, 5.5f, 6.6f) }
+  };
+
+  for (size_t i = 0; i < arraysize(tests); ++i) {
+    float actual = gfx::DotProduct(tests[i].input1, tests[i].input2);
+    EXPECT_EQ(tests[i].expected, actual);
+  }
+}
+
+TEST(Vector3dTest, CrossProduct) {
+  const struct {
+    gfx::Vector3dF expected;
+    gfx::Vector3dF input1;
+    gfx::Vector3dF input2;
+  } tests[] = {
+    { Vector3dF(), Vector3dF(), Vector3dF(1, 1, 1) },
+    { Vector3dF(), Vector3dF(1, 1, 1), Vector3dF() },
+    { Vector3dF(), Vector3dF(1, 1, 1), Vector3dF(1, 1, 1) },
+    { Vector3dF(),
+      Vector3dF(1.6f, 10.6f, -10.6f),
+      Vector3dF(1.6f, 10.6f, -10.6f) },
+
+    { Vector3dF(1, -1, 0), Vector3dF(1, 1, 1), Vector3dF(0, 0, 1) },
+    { Vector3dF(-1, 0, 1), Vector3dF(1, 1, 1), Vector3dF(0, 1, 0) },
+    { Vector3dF(0, 1, -1), Vector3dF(1, 1, 1), Vector3dF(1, 0, 0) },
+
+    { Vector3dF(-1, 1, 0), Vector3dF(0, 0, 1), Vector3dF(1, 1, 1) },
+    { Vector3dF(1, 0, -1), Vector3dF(0, 1, 0), Vector3dF(1, 1, 1) },
+    { Vector3dF(0, -1, 1), Vector3dF(1, 0, 0), Vector3dF(1, 1, 1) }
+  };
+
+  for (size_t i = 0; i < arraysize(tests); ++i) {
+    SCOPED_TRACE(i);
+    Vector3dF actual = gfx::CrossProduct(tests[i].input1, tests[i].input2);
+    EXPECT_EQ(tests[i].expected.ToString(), actual.ToString());
+  }
+}
+
+TEST(Vector3dFTest, ClampVector3dF) {
+  Vector3dF a;
+
+  a = Vector3dF(3.5f, 5.5f, 7.5f);
+  EXPECT_EQ(Vector3dF(3.5f, 5.5f, 7.5f).ToString(), a.ToString());
+  a.SetToMax(Vector3dF(2, 4.5f, 6.5f));
+  EXPECT_EQ(Vector3dF(3.5f, 5.5f, 7.5f).ToString(), a.ToString());
+  a.SetToMax(Vector3dF(3.5f, 5.5f, 7.5f));
+  EXPECT_EQ(Vector3dF(3.5f, 5.5f, 7.5f).ToString(), a.ToString());
+  a.SetToMax(Vector3dF(4.5f, 2, 6.5f));
+  EXPECT_EQ(Vector3dF(4.5f, 5.5f, 7.5f).ToString(), a.ToString());
+  a.SetToMax(Vector3dF(3.5f, 6.5f, 6.5f));
+  EXPECT_EQ(Vector3dF(4.5f, 6.5f, 7.5f).ToString(), a.ToString());
+  a.SetToMax(Vector3dF(3.5f, 5.5f, 8.5f));
+  EXPECT_EQ(Vector3dF(4.5f, 6.5f, 8.5f).ToString(), a.ToString());
+  a.SetToMax(Vector3dF(8.5f, 10.5f, 12.5f));
+  EXPECT_EQ(Vector3dF(8.5f, 10.5f, 12.5f).ToString(), a.ToString());
+
+  a.SetToMin(Vector3dF(9.5f, 11.5f, 13.5f));
+  EXPECT_EQ(Vector3dF(8.5f, 10.5f, 12.5f).ToString(), a.ToString());
+  a.SetToMin(Vector3dF(8.5f, 10.5f, 12.5f));
+  EXPECT_EQ(Vector3dF(8.5f, 10.5f, 12.5f).ToString(), a.ToString());
+  a.SetToMin(Vector3dF(7.5f, 11.5f, 13.5f));
+  EXPECT_EQ(Vector3dF(7.5f, 10.5f, 12.5f).ToString(), a.ToString());
+  a.SetToMin(Vector3dF(9.5f, 9.5f, 13.5f));
+  EXPECT_EQ(Vector3dF(7.5f, 9.5f, 12.5f).ToString(), a.ToString());
+  a.SetToMin(Vector3dF(9.5f, 11.5f, 11.5f));
+  EXPECT_EQ(Vector3dF(7.5f, 9.5f, 11.5f).ToString(), a.ToString());
+  a.SetToMin(Vector3dF(3.5f, 5.5f, 7.5f));
+  EXPECT_EQ(Vector3dF(3.5f, 5.5f, 7.5f).ToString(), a.ToString());
+}
+
+TEST(Vector3dTest, AngleBetweenVectorsInDegress) {
+  const struct {
+    float expected;
+    gfx::Vector3dF input1;
+    gfx::Vector3dF input2;
+  } tests[] = {
+      {0, gfx::Vector3dF(0, 1, 0), gfx::Vector3dF(0, 1, 0)},
+      {90, gfx::Vector3dF(0, 1, 0), gfx::Vector3dF(0, 0, 1)},
+      {45,
+       gfx::Vector3dF(0, 1, 0),
+       gfx::Vector3dF(0, 0.70710678188f, 0.70710678188f)},
+      {180, gfx::Vector3dF(0, 1, 0), gfx::Vector3dF(0, -1, 0)},
+  };
+
+  for (size_t i = 0; i < arraysize(tests); ++i) {
+    float actual =
+        gfx::AngleBetweenVectorsInDegrees(tests[i].input1, tests[i].input2);
+    EXPECT_FLOAT_EQ(tests[i].expected, actual);
+    actual =
+        gfx::AngleBetweenVectorsInDegrees(tests[i].input2, tests[i].input1);
+    EXPECT_FLOAT_EQ(tests[i].expected, actual);
+  }
+}
+
+TEST(Vector3dTest, ClockwiseAngleBetweenVectorsInDegress) {
+  const struct {
+    float expected;
+    gfx::Vector3dF input1;
+    gfx::Vector3dF input2;
+  } tests[] = {
+      {0, gfx::Vector3dF(0, 1, 0), gfx::Vector3dF(0, 1, 0)},
+      {90, gfx::Vector3dF(0, 1, 0), gfx::Vector3dF(0, 0, -1)},
+      {45,
+       gfx::Vector3dF(0, -1, 0),
+       gfx::Vector3dF(0, -0.70710678188f, 0.70710678188f)},
+      {180, gfx::Vector3dF(0, -1, 0), gfx::Vector3dF(0, 1, 0)},
+      {270, gfx::Vector3dF(0, 1, 0), gfx::Vector3dF(0, 0, 1)},
+  };
+
+  const gfx::Vector3dF normal_vector(1.0f, 0.0f, 0.0f);
+
+  for (size_t i = 0; i < arraysize(tests); ++i) {
+    float actual = gfx::ClockwiseAngleBetweenVectorsInDegrees(
+        tests[i].input1, tests[i].input2, normal_vector);
+    EXPECT_FLOAT_EQ(tests[i].expected, actual);
+    actual = -gfx::ClockwiseAngleBetweenVectorsInDegrees(
+                 tests[i].input2, tests[i].input1, normal_vector);
+    if (actual < 0.0f)
+      actual += 360.0f;
+    EXPECT_FLOAT_EQ(tests[i].expected, actual);
+  }
+}
+
+TEST(Vector3dTest, GetNormalized) {
+  const struct {
+    bool expected;
+    gfx::Vector3dF v;
+    gfx::Vector3dF normalized;
+  } tests[] = {
+      {false, gfx::Vector3dF(0, 0, 0), gfx::Vector3dF(0, 0, 0)},
+      {false,
+       gfx::Vector3dF(std::numeric_limits<float>::min(),
+                      std::numeric_limits<float>::min(),
+                      std::numeric_limits<float>::min()),
+       gfx::Vector3dF(std::numeric_limits<float>::min(),
+                      std::numeric_limits<float>::min(),
+                      std::numeric_limits<float>::min())},
+      {true, gfx::Vector3dF(1, 0, 0), gfx::Vector3dF(1, 0, 0)},
+      {true, gfx::Vector3dF(std::numeric_limits<float>::max(), 0, 0),
+       gfx::Vector3dF(1, 0, 0)},
+  };
+
+  for (size_t i = 0; i < arraysize(tests); ++i) {
+    gfx::Vector3dF n;
+    EXPECT_EQ(tests[i].expected, tests[i].v.GetNormalized(&n));
+    EXPECT_EQ(tests[i].normalized.ToString(), n.ToString());
+  }
+}
+
+}  // namespace gfx
diff --git a/ui/gfx/range/BUILD.gn b/ui/gfx/range/BUILD.gn
deleted file mode 100644
index 2a2568a..0000000
--- a/ui/gfx/range/BUILD.gn
+++ /dev/null
@@ -1,35 +0,0 @@
-# Copyright 2016 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//build/config/jumbo.gni")
-
-jumbo_component("range") {
-  sources = [
-    "gfx_range_export.h",
-    "range.cc",
-    "range.h",
-    "range_f.cc",
-    "range_f.h",
-    "range_mac.mm",
-    "range_win.cc",
-  ]
-
-  if (is_ios) {
-    set_sources_assignment_filter([])
-    sources += [ "range_mac.mm" ]
-    set_sources_assignment_filter(sources_assignment_filter)
-  }
-
-  configs += [
-    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-    "//build/config/compiler:no_size_t_to_int_warning",
-  ]
-
-  defines = [ "GFX_RANGE_IMPLEMENTATION" ]
-
-  deps = [
-    "//base",
-    "//ui/gfx:gfx_export",
-  ]
-}
diff --git a/ui/gfx/range/mojo/BUILD.gn b/ui/gfx/range/mojo/BUILD.gn
deleted file mode 100644
index b6d458d..0000000
--- a/ui/gfx/range/mojo/BUILD.gn
+++ /dev/null
@@ -1,49 +0,0 @@
-# Copyright 2016 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//mojo/public/tools/bindings/mojom.gni")
-
-# This target does NOT depend on skia. One can depend on this target to avoid
-# picking up a dependency on skia.
-mojom("mojo") {
-  sources = [
-    "range.mojom",
-  ]
-}
-
-mojom("test_interfaces") {
-  sources = [
-    "range_traits_test_service.mojom",
-  ]
-
-  public_deps = [
-    ":mojo",
-  ]
-}
-
-source_set("unit_test") {
-  testonly = true
-
-  sources = [
-    "range_struct_traits_unittest.cc",
-  ]
-
-  deps = [
-    ":test_interfaces",
-    "//base",
-    "//mojo/public/cpp/bindings",
-    "//testing/gtest",
-    "//ui/gfx/range",
-  ]
-}
-
-source_set("struct_traits") {
-  sources = [
-    "range_struct_traits.h",
-  ]
-  public_deps = [
-    ":mojo_shared_cpp_sources",
-    "//ui/gfx/range",
-  ]
-}